]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/btrfs/inode.c
Add a min size parameter to btrfs_alloc_extent
[mv-sheeva.git] / fs / btrfs / inode.c
index 5a38443a24ee281bf7b65dd0925f1d1887333b65..7869c0157a1f1dae098d3e3ff170f8cc06e5601a 100644 (file)
@@ -16,6 +16,7 @@
  * Boston, MA 021110-1307, USA.
  */
 
+#include <linux/bio.h>
 #include <linux/buffer_head.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
@@ -39,6 +40,7 @@
 #include "btrfs_inode.h"
 #include "ioctl.h"
 #include "print-tree.h"
+#include "volumes.h"
 
 struct btrfs_iget_args {
        u64 ino;
@@ -123,6 +125,7 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end)
        while(num_bytes > 0) {
                cur_alloc_size = min(num_bytes, root->fs_info->max_extent);
                ret = btrfs_alloc_extent(trans, root, cur_alloc_size,
+                                        root->sectorsize,
                                         root->root_key.objectid,
                                         trans->transid,
                                         inode->i_ino, start, 0,
@@ -131,9 +134,11 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end)
                        WARN_ON(1);
                        goto out;
                }
+               cur_alloc_size = ins.offset;
                ret = btrfs_insert_file_extent(trans, root, inode->i_ino,
                                               start, ins.objectid, ins.offset,
                                               ins.offset);
+               inode->i_blocks += ins.offset >> 9;
                btrfs_check_file(root, inode);
                num_bytes -= cur_alloc_size;
                alloc_hint = ins.objectid + ins.offset;
@@ -142,6 +147,7 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end)
        btrfs_drop_extent_cache(inode, orig_start,
                                orig_start + orig_num_bytes - 1);
        btrfs_add_ordered_inode(inode);
+       btrfs_update_inode(trans, root, inode);
 out:
        btrfs_end_transaction(trans, root);
        return ret;
@@ -260,11 +266,12 @@ static int run_delalloc_range(struct inode *inode, u64 start, u64 end)
 }
 
 int btrfs_set_bit_hook(struct inode *inode, u64 start, u64 end,
-                      unsigned long bits)
+                      unsigned long old, unsigned long bits)
 {
-       if ((bits & EXTENT_DELALLOC)) {
+       if (!(old & EXTENT_DELALLOC) && (bits & EXTENT_DELALLOC)) {
                struct btrfs_root *root = BTRFS_I(inode)->root;
                spin_lock(&root->fs_info->delalloc_lock);
+               BTRFS_I(inode)->delalloc_bytes += end - start + 1;
                root->fs_info->delalloc_bytes += end - start + 1;
                spin_unlock(&root->fs_info->delalloc_lock);
        }
@@ -272,40 +279,78 @@ int btrfs_set_bit_hook(struct inode *inode, u64 start, u64 end,
 }
 
 int btrfs_clear_bit_hook(struct inode *inode, u64 start, u64 end,
-                        unsigned long bits)
+                        unsigned long old, unsigned long bits)
 {
-       if ((bits & EXTENT_DELALLOC)) {
+       if ((old & EXTENT_DELALLOC) && (bits & EXTENT_DELALLOC)) {
                struct btrfs_root *root = BTRFS_I(inode)->root;
                spin_lock(&root->fs_info->delalloc_lock);
-               root->fs_info->delalloc_bytes -= end - start + 1;
+               if (end - start + 1 > root->fs_info->delalloc_bytes) {
+                       printk("warning: delalloc account %Lu %Lu\n",
+                              end - start + 1, root->fs_info->delalloc_bytes);
+                       root->fs_info->delalloc_bytes = 0;
+                       BTRFS_I(inode)->delalloc_bytes = 0;
+               } else {
+                       root->fs_info->delalloc_bytes -= end - start + 1;
+                       BTRFS_I(inode)->delalloc_bytes -= end - start + 1;
+               }
                spin_unlock(&root->fs_info->delalloc_lock);
        }
        return 0;
 }
 
-int btrfs_writepage_io_hook(struct page *page, u64 start, u64 end)
+int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
+                        size_t size, struct bio *bio)
+{
+       struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
+       struct btrfs_mapping_tree *map_tree;
+       u64 logical = bio->bi_sector << 9;
+       u64 length = 0;
+       u64 map_length;
+       struct bio_vec *bvec;
+       int i;
+       int ret;
+
+       bio_for_each_segment(bvec, bio, i) {
+               length += bvec->bv_len;
+       }
+       map_tree = &root->fs_info->mapping_tree;
+       map_length = length;
+       ret = btrfs_map_block(map_tree, READ, logical,
+                             &map_length, NULL, 0);
+
+       if (map_length < length + size) {
+               return 1;
+       }
+       return 0;
+}
+
+int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
+                         int mirror_num)
 {
-       struct inode *inode = page->mapping->host;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_trans_handle *trans;
-       char *kaddr;
        int ret = 0;
-       u64 page_start = (u64)page->index << PAGE_CACHE_SHIFT;
-       size_t offset = start - page_start;
+
+       if (!(rw & (1 << BIO_RW))) {
+               ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
+               BUG_ON(ret);
+               goto mapit;
+       }
+
        if (btrfs_test_opt(root, NODATASUM) ||
-           btrfs_test_flag(inode, NODATASUM))
-               return 0;
+           btrfs_test_flag(inode, NODATASUM)) {
+               goto mapit;
+       }
+
        mutex_lock(&root->fs_info->fs_mutex);
        trans = btrfs_start_transaction(root, 1);
        btrfs_set_trans_block_group(trans, inode);
-       kaddr = kmap(page);
-       btrfs_csum_file_block(trans, root, inode, inode->i_ino,
-                             start, kaddr + offset, end - start + 1);
-       kunmap(page);
+       btrfs_csum_file_blocks(trans, root, inode, bio);
        ret = btrfs_end_transaction(trans, root);
        BUG_ON(ret);
        mutex_unlock(&root->fs_info->fs_mutex);
-       return ret;
+mapit:
+       return btrfs_map_bio(root, rw, bio, mirror_num);
 }
 
 int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end)
@@ -342,6 +387,87 @@ out:
        return ret;
 }
 
+struct io_failure_record {
+       struct page *page;
+       u64 start;
+       u64 len;
+       u64 logical;
+       int last_mirror;
+};
+
+int btrfs_readpage_io_failed_hook(struct bio *failed_bio,
+                                 struct page *page, u64 start, u64 end,
+                                 struct extent_state *state)
+{
+       struct io_failure_record *failrec = NULL;
+       u64 private;
+       struct extent_map *em;
+       struct inode *inode = page->mapping->host;
+       struct extent_io_tree *failure_tree = &BTRFS_I(inode)->io_failure_tree;
+       struct bio *bio;
+       int num_copies;
+       int ret;
+       u64 logical;
+
+       ret = get_state_private(failure_tree, start, &private);
+       if (ret) {
+               size_t pg_offset = start - page_offset(page);
+               failrec = kmalloc(sizeof(*failrec), GFP_NOFS);
+               if (!failrec)
+                       return -ENOMEM;
+               failrec->start = start;
+               failrec->len = end - start + 1;
+               failrec->last_mirror = 0;
+
+               em = btrfs_get_extent(inode, NULL, pg_offset, start,
+                                     failrec->len, 0);
+
+               if (!em || IS_ERR(em)) {
+                       kfree(failrec);
+                       return -EIO;
+               }
+               logical = start - em->start;
+               logical = em->block_start + logical;
+               failrec->logical = logical;
+               free_extent_map(em);
+               set_extent_bits(failure_tree, start, end, EXTENT_LOCKED |
+                               EXTENT_DIRTY, GFP_NOFS);
+               set_state_private(failure_tree, start,
+                                (u64)(unsigned long)failrec);
+       } else {
+               failrec = (struct io_failure_record *)(unsigned long)private;
+       }
+       num_copies = btrfs_num_copies(
+                             &BTRFS_I(inode)->root->fs_info->mapping_tree,
+                             failrec->logical, failrec->len);
+       failrec->last_mirror++;
+       if (!state) {
+               spin_lock_irq(&BTRFS_I(inode)->io_tree.lock);
+               state = find_first_extent_bit_state(&BTRFS_I(inode)->io_tree,
+                                                   failrec->start,
+                                                   EXTENT_LOCKED);
+               if (state && state->start != failrec->start)
+                       state = NULL;
+               spin_unlock_irq(&BTRFS_I(inode)->io_tree.lock);
+       }
+       if (!state || failrec->last_mirror > num_copies) {
+               set_state_private(failure_tree, failrec->start, 0);
+               clear_extent_bits(failure_tree, failrec->start,
+                                 failrec->start + failrec->len - 1,
+                                 EXTENT_LOCKED | EXTENT_DIRTY, GFP_NOFS);
+               kfree(failrec);
+               return -EIO;
+       }
+       bio = bio_alloc(GFP_NOFS, 1);
+       bio->bi_private = state;
+       bio->bi_end_io = failed_bio->bi_end_io;
+       bio->bi_sector = failrec->logical >> 9;
+       bio->bi_bdev = failed_bio->bi_bdev;
+       bio_add_page(bio, page, failrec->len, start - page_offset(page));
+       btrfs_submit_bio_hook(inode, READ, bio, failrec->last_mirror);
+       return 0;
+}
+
 int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end,
                               struct extent_state *state)
 {
@@ -358,8 +484,7 @@ int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end,
        if (btrfs_test_opt(root, NODATASUM) ||
            btrfs_test_flag(inode, NODATASUM))
                return 0;
-
-       if (state->start == start) {
+       if (state && state->start == start) {
                private = state->private;
                ret = 0;
        } else {
@@ -377,6 +502,30 @@ int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end,
        }
        kunmap_atomic(kaddr, KM_IRQ0);
        local_irq_restore(flags);
+
+       /* if the io failure tree for this inode is non-empty,
+        * check to see if we've recovered from a failed IO
+        */
+       private = 0;
+       if (count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private,
+                            (u64)-1, 1, EXTENT_DIRTY)) {
+               u64 private_failure;
+               struct io_failure_record *failure;
+               ret = get_state_private(&BTRFS_I(inode)->io_failure_tree,
+                                       start, &private_failure);
+               if (ret == 0) {
+                       failure = (struct io_failure_record *)(unsigned long)
+                                  private_failure;
+                       set_state_private(&BTRFS_I(inode)->io_failure_tree,
+                                         failure->start, 0);
+                       clear_extent_bits(&BTRFS_I(inode)->io_failure_tree,
+                                         failure->start,
+                                         failure->start + failure->len - 1,
+                                         EXTENT_DIRTY | EXTENT_LOCKED,
+                                         GFP_NOFS);
+                       kfree(failure);
+               }
+       }
        return 0;
 
 zeroit:
@@ -387,7 +536,7 @@ zeroit:
        flush_dcache_page(page);
        kunmap_atomic(kaddr, KM_IRQ0);
        local_irq_restore(flags);
-       return 0;
+       return -EIO;
 }
 
 void btrfs_read_locked_inode(struct inode *inode)
@@ -395,7 +544,7 @@ void btrfs_read_locked_inode(struct inode *inode)
        struct btrfs_path *path;
        struct extent_buffer *leaf;
        struct btrfs_inode_item *inode_item;
-       struct btrfs_inode_timespec *tspec;
+       struct btrfs_timespec *tspec;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_key location;
        u64 alloc_group_block;
@@ -444,7 +593,8 @@ void btrfs_read_locked_inode(struct inode *inode)
        BTRFS_I(inode)->flags = btrfs_inode_flags(leaf, inode_item);
        if (!BTRFS_I(inode)->block_group) {
                BTRFS_I(inode)->block_group = btrfs_find_block_group(root,
-                                                        NULL, 0, 0, 0);
+                                                NULL, 0,
+                                                BTRFS_BLOCK_GROUP_METADATA, 0);
        }
        btrfs_free_path(path);
        inode_item = NULL;
@@ -454,6 +604,7 @@ void btrfs_read_locked_inode(struct inode *inode)
        switch (inode->i_mode & S_IFMT) {
        case S_IFREG:
                inode->i_mapping->a_ops = &btrfs_aops;
+               inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
                BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
                inode->i_fop = &btrfs_file_operations;
                inode->i_op = &btrfs_file_inode_operations;
@@ -468,6 +619,7 @@ void btrfs_read_locked_inode(struct inode *inode)
        case S_IFLNK:
                inode->i_op = &btrfs_symlink_inode_operations;
                inode->i_mapping->a_ops = &btrfs_symlink_aops;
+               inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
                break;
        default:
                init_special_inode(inode, inode->i_mode, rdev);
@@ -823,35 +975,42 @@ search_again:
                                        btrfs_file_extent_num_bytes(leaf, fi);
                                extent_num_bytes = inode->i_size -
                                        found_key.offset + root->sectorsize - 1;
+                               extent_num_bytes = extent_num_bytes &
+                                       ~((u64)root->sectorsize - 1);
                                btrfs_set_file_extent_num_bytes(leaf, fi,
                                                         extent_num_bytes);
                                num_dec = (orig_num_bytes -
-                                          extent_num_bytes) >> 9;
-                               if (extent_start != 0) {
-                                       inode->i_blocks -= num_dec;
-                               }
+                                          extent_num_bytes);
+                               if (extent_start != 0)
+                                       dec_i_blocks(inode, num_dec);
                                btrfs_mark_buffer_dirty(leaf);
                        } else {
                                extent_num_bytes =
                                        btrfs_file_extent_disk_num_bytes(leaf,
                                                                         fi);
                                /* FIXME blocksize != 4096 */
-                               num_dec = btrfs_file_extent_num_bytes(leaf,
-                                                                      fi) >> 9;
+                               num_dec = btrfs_file_extent_num_bytes(leaf, fi);
                                if (extent_start != 0) {
                                        found_extent = 1;
-                                       inode->i_blocks -= num_dec;
+                                       dec_i_blocks(inode, num_dec);
                                }
                                root_gen = btrfs_header_generation(leaf);
                                root_owner = btrfs_header_owner(leaf);
                        }
-               } else if (extent_type == BTRFS_FILE_EXTENT_INLINE &&
-                          !del_item) {
-                       u32 newsize = inode->i_size - found_key.offset;
-                       newsize = btrfs_file_extent_calc_inline_size(newsize);
-                       ret = btrfs_truncate_item(trans, root, path,
-                                                 newsize, 1);
-                       BUG_ON(ret);
+               } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+                       if (!del_item) {
+                               u32 newsize = inode->i_size - found_key.offset;
+                               dec_i_blocks(inode, item_end + 1 -
+                                           found_key.offset - newsize);
+                               newsize =
+                                   btrfs_file_extent_calc_inline_size(newsize);
+                               ret = btrfs_truncate_item(trans, root, path,
+                                                         newsize, 1);
+                               BUG_ON(ret);
+                       } else {
+                               dec_i_blocks(inode, item_end + 1 -
+                                            found_key.offset);
+                       }
                }
 delete:
                if (del_item) {
@@ -998,20 +1157,14 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
                struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
 
                u64 mask = root->sectorsize - 1;
-               u64 pos = (inode->i_size + mask) & ~mask;
-               u64 block_end = attr->ia_size | mask;
-               u64 hole_start;
+               u64 hole_start = (inode->i_size + mask) & ~mask;
+               u64 block_end = (attr->ia_size + mask) & ~mask;
                u64 hole_size;
                u64 alloc_hint = 0;
 
-               if (attr->ia_size <= pos)
+               if (attr->ia_size <= hole_start)
                        goto out;
 
-               if (pos != inode->i_size)
-                       hole_start = pos + root->sectorsize;
-               else
-                       hole_start = pos;
-
                mutex_lock(&root->fs_info->fs_mutex);
                err = btrfs_check_free_space(root, 1, 0);
                mutex_unlock(&root->fs_info->fs_mutex);
@@ -1020,14 +1173,14 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
 
                btrfs_truncate_page(inode->i_mapping, inode->i_size);
 
-               lock_extent(io_tree, pos, block_end, GFP_NOFS);
+               lock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
                hole_size = block_end - hole_start;
 
                mutex_lock(&root->fs_info->fs_mutex);
                trans = btrfs_start_transaction(root, 1);
                btrfs_set_trans_block_group(trans, inode);
                err = btrfs_drop_extents(trans, root, inode,
-                                        pos, block_end, pos,
+                                        hole_start, block_end, hole_start,
                                         &alloc_hint);
 
                if (alloc_hint != EXTENT_MAP_INLINE) {
@@ -1041,7 +1194,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
                }
                btrfs_end_transaction(trans, root);
                mutex_unlock(&root->fs_info->fs_mutex);
-               unlock_extent(io_tree, pos, block_end, GFP_NOFS);
+               unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
                if (err)
                        return err;
        }
@@ -1221,9 +1374,12 @@ static int btrfs_init_locked_inode(struct inode *inode, void *p)
        struct btrfs_iget_args *args = p;
        inode->i_ino = args->ino;
        BTRFS_I(inode)->root = args->root;
+       BTRFS_I(inode)->delalloc_bytes = 0;
        extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
        extent_io_tree_init(&BTRFS_I(inode)->io_tree,
                             inode->i_mapping, GFP_NOFS);
+       extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
+                            inode->i_mapping, GFP_NOFS);
        return 0;
 }
 
@@ -1453,7 +1609,10 @@ read_dir_items:
                        di = (struct btrfs_dir_item *)((char *)di + di_len);
                }
        }
-       filp->f_pos = INT_LIMIT(typeof(filp->f_pos));
+       if (key_type == BTRFS_DIR_INDEX_KEY)
+               filp->f_pos = INT_LIMIT(typeof(filp->f_pos));
+       else
+               filp->f_pos++;
 nopos:
        ret = 0;
 err:
@@ -1508,6 +1667,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 {
        struct inode *inode;
        struct btrfs_inode_item *inode_item;
+       struct btrfs_block_group_cache *new_inode_group;
        struct btrfs_key *location;
        struct btrfs_path *path;
        struct btrfs_inode_ref *ref;
@@ -1527,14 +1687,22 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
        extent_io_tree_init(&BTRFS_I(inode)->io_tree,
                             inode->i_mapping, GFP_NOFS);
+       extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
+                            inode->i_mapping, GFP_NOFS);
+       BTRFS_I(inode)->delalloc_bytes = 0;
        BTRFS_I(inode)->root = root;
 
        if (mode & S_IFDIR)
                owner = 0;
        else
                owner = 1;
-       group = btrfs_find_block_group(root, group, 0, 0, owner);
-       BTRFS_I(inode)->block_group = group;
+       new_inode_group = btrfs_find_block_group(root, group, 0,
+                                      BTRFS_BLOCK_GROUP_METADATA, owner);
+       if (!new_inode_group) {
+               printk("find_block group failed\n");
+               new_inode_group = group;
+       }
+       BTRFS_I(inode)->block_group = new_inode_group;
        BTRFS_I(inode)->flags = 0;
 
        key[0].objectid = objectid;
@@ -1740,11 +1908,15 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
                drop_inode = 1;
        else {
                inode->i_mapping->a_ops = &btrfs_aops;
+               inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
                inode->i_fop = &btrfs_file_operations;
                inode->i_op = &btrfs_file_inode_operations;
                extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
                extent_io_tree_init(&BTRFS_I(inode)->io_tree,
                                     inode->i_mapping, GFP_NOFS);
+               extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
+                                    inode->i_mapping, GFP_NOFS);
+               BTRFS_I(inode)->delalloc_bytes = 0;
                BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
        }
        dir->i_sb->s_dirt = 1;
@@ -2097,6 +2269,70 @@ out:
        return em;
 }
 
+static int btrfs_get_block(struct inode *inode, sector_t iblock,
+                       struct buffer_head *bh_result, int create)
+{
+       struct extent_map *em;
+       u64 start = (u64)iblock << inode->i_blkbits;
+       struct btrfs_multi_bio *multi = NULL;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       u64 len;
+       u64 logical;
+       u64 map_length;
+       int ret = 0;
+
+       em = btrfs_get_extent(inode, NULL, 0, start, bh_result->b_size, 0);
+
+       if (!em || IS_ERR(em))
+               goto out;
+
+       if (em->start > start || em->start + em->len <= start)
+           goto out;
+
+       if (em->block_start == EXTENT_MAP_INLINE) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (em->block_start == EXTENT_MAP_HOLE ||
+           em->block_start == EXTENT_MAP_DELALLOC) {
+               goto out;
+       }
+
+       len = em->start + em->len - start;
+       len = min_t(u64, len, INT_LIMIT(typeof(bh_result->b_size)));
+
+       logical = start - em->start;
+       logical = em->block_start + logical;
+
+       map_length = len;
+       ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
+                             logical, &map_length, &multi, 0);
+       BUG_ON(ret);
+       bh_result->b_blocknr = multi->stripes[0].physical >> inode->i_blkbits;
+       bh_result->b_size = min(map_length, len);
+       bh_result->b_bdev = multi->stripes[0].dev->bdev;
+       set_buffer_mapped(bh_result);
+       kfree(multi);
+out:
+       free_extent_map(em);
+       return ret;
+}
+
+static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
+                       const struct iovec *iov, loff_t offset,
+                       unsigned long nr_segs)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file->f_mapping->host;
+
+       if (rw == WRITE)
+               return -EINVAL;
+
+       return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
+                                 offset, nr_segs, btrfs_get_block, NULL);
+}
+
 static sector_t btrfs_bmap(struct address_space *mapping, sector_t iblock)
 {
        return extent_bmap(mapping, iblock, btrfs_get_extent);
@@ -2796,6 +3032,7 @@ static int btrfs_getattr(struct vfsmount *mnt,
        struct inode *inode = dentry->d_inode;
        generic_fillattr(inode, stat);
        stat->blksize = PAGE_CACHE_SIZE;
+       stat->blocks = inode->i_blocks + (BTRFS_I(inode)->delalloc_bytes >> 9);
        return 0;
 }
 
@@ -2906,11 +3143,15 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
                drop_inode = 1;
        else {
                inode->i_mapping->a_ops = &btrfs_aops;
+               inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
                inode->i_fop = &btrfs_file_operations;
                inode->i_op = &btrfs_file_inode_operations;
                extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
                extent_io_tree_init(&BTRFS_I(inode)->io_tree,
                                     inode->i_mapping, GFP_NOFS);
+               extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
+                                    inode->i_mapping, GFP_NOFS);
+               BTRFS_I(inode)->delalloc_bytes = 0;
                BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
        }
        dir->i_sb->s_dirt = 1;
@@ -2944,6 +3185,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
 
        inode->i_op = &btrfs_symlink_inode_operations;
        inode->i_mapping->a_ops = &btrfs_symlink_aops;
+       inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
        inode->i_size = name_len - 1;
        err = btrfs_update_inode(trans, root, inode);
        if (err)
@@ -2962,6 +3204,7 @@ out_fail:
        btrfs_throttle(root);
        return err;
 }
+
 static int btrfs_permission(struct inode *inode, int mask,
                            struct nameidata *nd)
 {
@@ -3003,9 +3246,13 @@ static struct file_operations btrfs_dir_file_operations = {
 
 static struct extent_io_ops btrfs_extent_io_ops = {
        .fill_delalloc = run_delalloc_range,
-       .writepage_io_hook = btrfs_writepage_io_hook,
+       .submit_bio_hook = btrfs_submit_bio_hook,
+       .merge_bio_hook = btrfs_merge_bio_hook,
        .readpage_io_hook = btrfs_readpage_io_hook,
        .readpage_end_io_hook = btrfs_readpage_end_io_hook,
+       .readpage_io_failed_hook = btrfs_readpage_io_failed_hook,
+       .set_bit_hook = btrfs_set_bit_hook,
+       .clear_bit_hook = btrfs_clear_bit_hook,
 };
 
 static struct address_space_operations btrfs_aops = {
@@ -3015,6 +3262,7 @@ static struct address_space_operations btrfs_aops = {
        .readpages      = btrfs_readpages,
        .sync_page      = block_sync_page,
        .bmap           = btrfs_bmap,
+       .direct_IO      = btrfs_direct_IO,
        .invalidatepage = btrfs_invalidatepage,
        .releasepage    = btrfs_releasepage,
        .set_page_dirty = __set_page_dirty_nobuffers,