Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Nov 2010 22:11:08 +0000 (14:11 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Nov 2010 22:11:08 +0000 (14:11 -0800)
* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable: (24 commits)
  Btrfs: don't use migrate page without CONFIG_MIGRATION
  Btrfs: deal with DIO bios that span more than one ordered extent
  Btrfs: setup blank root and fs_info for mount time
  Btrfs: fix fiemap
  Btrfs - fix race between btrfs_get_sb() and umount
  Btrfs: update inode ctime when using links
  Btrfs: make sure new inode size is ok in fallocate
  Btrfs: fix typo in fallocate to make it honor actual size
  Btrfs: avoid NULL pointer deref in try_release_extent_buffer
  Btrfs: make btrfs_add_nondir take parent inode as an argument
  Btrfs: hold i_mutex when calling btrfs_log_dentry_safe
  Btrfs: use dget_parent where we can UPDATED
  Btrfs: fix more ESTALE problems with NFS
  Btrfs: handle NFS lookups properly
  btrfs: make 1-bit signed fileds unsigned
  btrfs: Show device attr correctly for symlinks
  btrfs: Set file size correctly in file clone
  btrfs: Check if dest_offset is block-size aligned before cloning file
  Btrfs: handle the space_cache option properly
  btrfs: Fix early enospc because 'unused' calculated with wrong sign.
  ...

1  2 
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/inode.c
fs/btrfs/super.c

diff --combined fs/btrfs/disk-io.c
@@@ -28,6 -28,7 +28,7 @@@
  #include <linux/freezer.h>
  #include <linux/crc32c.h>
  #include <linux/slab.h>
+ #include <linux/migrate.h>
  #include "compat.h"
  #include "ctree.h"
  #include "disk-io.h"
@@@ -355,6 -356,8 +356,8 @@@ static int csum_dirty_buffer(struct btr
        ret = btree_read_extent_buffer_pages(root, eb, start + PAGE_CACHE_SIZE,
                                             btrfs_header_generation(eb));
        BUG_ON(ret);
+       WARN_ON(!btrfs_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN));
        found_start = btrfs_header_bytenr(eb);
        if (found_start != start) {
                WARN_ON(1);
@@@ -693,6 -696,29 +696,29 @@@ static int btree_submit_bio_hook(struc
                                   __btree_submit_bio_done);
  }
  
+ static int btree_migratepage(struct address_space *mapping,
+                       struct page *newpage, struct page *page)
+ {
+       /*
+        * we can't safely write a btree page from here,
+        * we haven't done the locking hook
+        */
+       if (PageDirty(page))
+               return -EAGAIN;
+       /*
+        * Buffers may be managed in a filesystem specific way.
+        * We must have no buffers or drop them.
+        */
+       if (page_has_private(page) &&
+           !try_to_release_page(page, GFP_KERNEL))
+               return -EAGAIN;
+ #ifdef CONFIG_MIGRATION
+       return migrate_page(mapping, newpage, page);
+ #else
+       return -ENOSYS;
+ #endif
+ }
  static int btree_writepage(struct page *page, struct writeback_control *wbc)
  {
        struct extent_io_tree *tree;
        }
  
        redirty_page_for_writepage(wbc, page);
-       eb = btrfs_find_tree_block(root, page_offset(page),
-                                     PAGE_CACHE_SIZE);
+       eb = btrfs_find_tree_block(root, page_offset(page), PAGE_CACHE_SIZE);
        WARN_ON(!eb);
  
        was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
@@@ -799,6 -824,9 +824,9 @@@ static const struct address_space_opera
        .releasepage    = btree_releasepage,
        .invalidatepage = btree_invalidatepage,
        .sync_page      = block_sync_page,
+ #ifdef CONFIG_MIGRATION
+       .migratepage    = btree_migratepage,
+ #endif
  };
  
  int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
@@@ -1538,10 -1566,8 +1566,8 @@@ struct btrfs_root *open_ctree(struct su
                                                 GFP_NOFS);
        struct btrfs_root *csum_root = kzalloc(sizeof(struct btrfs_root),
                                                 GFP_NOFS);
-       struct btrfs_root *tree_root = kzalloc(sizeof(struct btrfs_root),
-                                              GFP_NOFS);
-       struct btrfs_fs_info *fs_info = kzalloc(sizeof(*fs_info),
-                                               GFP_NOFS);
+       struct btrfs_root *tree_root = btrfs_sb(sb);
+       struct btrfs_fs_info *fs_info = tree_root->fs_info;
        struct btrfs_root *chunk_root = kzalloc(sizeof(struct btrfs_root),
                                                GFP_NOFS);
        struct btrfs_root *dev_root = kzalloc(sizeof(struct btrfs_root),
@@@ -2067,7 -2093,7 +2093,7 @@@ static void btrfs_end_buffer_write_sync
        if (uptodate) {
                set_buffer_uptodate(bh);
        } else {
 -              if (!buffer_eopnotsupp(bh) && printk_ratelimit()) {
 +              if (printk_ratelimit()) {
                        printk(KERN_WARNING "lost page write due to "
                                        "I/O error on %s\n",
                                       bdevname(bh->b_bdev, b));
@@@ -2204,10 -2230,21 +2230,10 @@@ static int write_dev_supers(struct btrf
                        bh->b_end_io = btrfs_end_buffer_write_sync;
                }
  
 -              if (i == last_barrier && do_barriers && device->barriers) {
 -                      ret = submit_bh(WRITE_BARRIER, bh);
 -                      if (ret == -EOPNOTSUPP) {
 -                              printk("btrfs: disabling barriers on dev %s\n",
 -                                     device->name);
 -                              set_buffer_uptodate(bh);
 -                              device->barriers = 0;
 -                              /* one reference for submit_bh */
 -                              get_bh(bh);
 -                              lock_buffer(bh);
 -                              ret = submit_bh(WRITE_SYNC, bh);
 -                      }
 -              } else {
 +              if (i == last_barrier && do_barriers)
 +                      ret = submit_bh(WRITE_FLUSH_FUA, bh);
 +              else
                        ret = submit_bh(WRITE_SYNC, bh);
 -              }
  
                if (ret)
                        errors++;
diff --combined fs/btrfs/extent-tree.c
@@@ -1742,7 -1742,8 +1742,7 @@@ static int remove_extent_backref(struc
  static void btrfs_issue_discard(struct block_device *bdev,
                                u64 start, u64 len)
  {
 -      blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL,
 -                      BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER);
 +      blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL, 0);
  }
  
  static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
@@@ -3412,7 -3413,7 +3412,7 @@@ again
         * our reservation.
         */
        if (unused <= space_info->total_bytes) {
-               unused -= space_info->total_bytes;
+               unused = space_info->total_bytes - unused;
                if (unused >= num_bytes) {
                        if (!reserved)
                                space_info->bytes_reserved += orig_bytes;
diff --combined fs/btrfs/extent_io.c
@@@ -1828,9 -1828,9 +1828,9 @@@ static void end_bio_extent_preparewrite
        bio_put(bio);
  }
  
- static struct bio *
extent_bio_alloc(struct block_device *bdev, u64 first_sector, int nr_vecs,
-                gfp_t gfp_flags)
+ struct bio *
btrfs_bio_alloc(struct block_device *bdev, u64 first_sector, int nr_vecs,
+               gfp_t gfp_flags)
  {
        struct bio *bio;
  
@@@ -1919,7 -1919,7 +1919,7 @@@ static int submit_extent_page(int rw, s
        else
                nr = bio_get_nr_vecs(bdev);
  
-       bio = extent_bio_alloc(bdev, sector, nr, GFP_NOFS | __GFP_HIGH);
+       bio = btrfs_bio_alloc(bdev, sector, nr, GFP_NOFS | __GFP_HIGH);
  
        bio_add_page(bio, page, page_size, offset);
        bio->bi_end_io = end_io_func;
  int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                __u64 start, __u64 len, get_extent_t *get_extent)
  {
-       int ret;
+       int ret = 0;
        u64 off = start;
        u64 max = start + len;
        u32 flags = 0;
+       u32 found_type;
+       u64 last;
        u64 disko = 0;
+       struct btrfs_key found_key;
        struct extent_map *em = NULL;
        struct extent_state *cached_state = NULL;
+       struct btrfs_path *path;
+       struct btrfs_file_extent_item *item;
        int end = 0;
        u64 em_start = 0, em_len = 0;
        unsigned long emflags;
-       ret = 0;
+       int hole = 0;
  
        if (len == 0)
                return -EINVAL;
  
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+       path->leave_spinning = 1;
+       ret = btrfs_lookup_file_extent(NULL, BTRFS_I(inode)->root,
+                                      path, inode->i_ino, -1, 0);
+       if (ret < 0) {
+               btrfs_free_path(path);
+               return ret;
+       }
+       WARN_ON(!ret);
+       path->slots[0]--;
+       item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+                             struct btrfs_file_extent_item);
+       btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]);
+       found_type = btrfs_key_type(&found_key);
+       /* No extents, just return */
+       if (found_key.objectid != inode->i_ino ||
+           found_type != BTRFS_EXTENT_DATA_KEY) {
+               btrfs_free_path(path);
+               return 0;
+       }
+       last = found_key.offset;
+       btrfs_free_path(path);
        lock_extent_bits(&BTRFS_I(inode)->io_tree, start, start + len, 0,
                         &cached_state, GFP_NOFS);
        em = get_extent(inode, NULL, 0, off, max - off, 0);
                ret = PTR_ERR(em);
                goto out;
        }
        while (!end) {
+               hole = 0;
                off = em->start + em->len;
                if (off >= max)
                        end = 1;
  
+               if (em->block_start == EXTENT_MAP_HOLE) {
+                       hole = 1;
+                       goto next;
+               }
                em_start = em->start;
                em_len = em->len;
  
                if (em->block_start == EXTENT_MAP_LAST_BYTE) {
                        end = 1;
                        flags |= FIEMAP_EXTENT_LAST;
-               } else if (em->block_start == EXTENT_MAP_HOLE) {
-                       flags |= FIEMAP_EXTENT_UNWRITTEN;
                } else if (em->block_start == EXTENT_MAP_INLINE) {
                        flags |= (FIEMAP_EXTENT_DATA_INLINE |
                                  FIEMAP_EXTENT_NOT_ALIGNED);
                if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags))
                        flags |= FIEMAP_EXTENT_ENCODED;
  
+ next:
                emflags = em->flags;
                free_extent_map(em);
                em = NULL;
                if (!end) {
                        em = get_extent(inode, NULL, 0, off, max - off, 0);
                        if (!em)
                        }
                        emflags = em->flags;
                }
                if (test_bit(EXTENT_FLAG_VACANCY, &emflags)) {
                        flags |= FIEMAP_EXTENT_LAST;
                        end = 1;
                }
  
-               ret = fiemap_fill_next_extent(fieinfo, em_start, disko,
-                                       em_len, flags);
-               if (ret)
-                       goto out_free;
+               if (em_start == last) {
+                       flags |= FIEMAP_EXTENT_LAST;
+                       end = 1;
+               }
+               if (!hole) {
+                       ret = fiemap_fill_next_extent(fieinfo, em_start, disko,
+                                               em_len, flags);
+                       if (ret)
+                               goto out_free;
+               }
        }
  out_free:
        free_extent_map(em);
@@@ -3031,6 -3076,7 +3076,6 @@@ static struct extent_buffer *__alloc_ex
        eb->len = len;
        spin_lock_init(&eb->lock);
        init_waitqueue_head(&eb->lock_wq);
 -      INIT_RCU_HEAD(&eb->rcu_head);
  
  #if LEAK_DEBUG
        spin_lock_irqsave(&leak_lock, flags);
@@@ -3836,8 -3882,10 +3881,10 @@@ int try_release_extent_buffer(struct ex
  
        spin_lock(&tree->buffer_lock);
        eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
-       if (!eb)
-               goto out;
+       if (!eb) {
+               spin_unlock(&tree->buffer_lock);
+               return ret;
+       }
  
        if (test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
                ret = 0;
diff --combined fs/btrfs/inode.c
@@@ -3877,7 -3877,7 +3877,7 @@@ again
        p = &root->inode_tree.rb_node;
        parent = NULL;
  
 -      if (hlist_unhashed(&inode->i_hash))
 +      if (inode_unhashed(inode))
                return;
  
        spin_lock(&root->inode_lock);
@@@ -4501,6 -4501,7 +4501,7 @@@ static struct inode *btrfs_new_inode(st
        BTRFS_I(inode)->index_cnt = 2;
        BTRFS_I(inode)->root = root;
        BTRFS_I(inode)->generation = trans->transid;
+       inode->i_generation = BTRFS_I(inode)->generation;
        btrfs_set_inode_space_info(root, inode);
  
        if (mode & S_IFDIR)
@@@ -4622,12 -4623,12 +4623,12 @@@ int btrfs_add_link(struct btrfs_trans_h
  }
  
  static int btrfs_add_nondir(struct btrfs_trans_handle *trans,
-                           struct dentry *dentry, struct inode *inode,
-                           int backref, u64 index)
+                           struct inode *dir, struct dentry *dentry,
+                           struct inode *inode, int backref, u64 index)
  {
-       int err = btrfs_add_link(trans, dentry->d_parent->d_inode,
-                                inode, dentry->d_name.name,
-                                dentry->d_name.len, backref, index);
+       int err = btrfs_add_link(trans, dir, inode,
+                                dentry->d_name.name, dentry->d_name.len,
+                                backref, index);
        if (!err) {
                d_instantiate(dentry, inode);
                return 0;
@@@ -4668,8 -4669,7 +4669,7 @@@ static int btrfs_mknod(struct inode *di
        btrfs_set_trans_block_group(trans, dir);
  
        inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
-                               dentry->d_name.len,
-                               dentry->d_parent->d_inode->i_ino, objectid,
+                               dentry->d_name.len, dir->i_ino, objectid,
                                BTRFS_I(dir)->block_group, mode, &index);
        err = PTR_ERR(inode);
        if (IS_ERR(inode))
        }
  
        btrfs_set_trans_block_group(trans, inode);
-       err = btrfs_add_nondir(trans, dentry, inode, 0, index);
+       err = btrfs_add_nondir(trans, dir, dentry, inode, 0, index);
        if (err)
                drop_inode = 1;
        else {
@@@ -4730,10 -4730,8 +4730,8 @@@ static int btrfs_create(struct inode *d
        btrfs_set_trans_block_group(trans, dir);
  
        inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
-                               dentry->d_name.len,
-                               dentry->d_parent->d_inode->i_ino,
-                               objectid, BTRFS_I(dir)->block_group, mode,
-                               &index);
+                               dentry->d_name.len, dir->i_ino, objectid,
+                               BTRFS_I(dir)->block_group, mode, &index);
        err = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto out_unlock;
        }
  
        btrfs_set_trans_block_group(trans, inode);
-       err = btrfs_add_nondir(trans, dentry, inode, 0, index);
+       err = btrfs_add_nondir(trans, dir, dentry, inode, 0, index);
        if (err)
                drop_inode = 1;
        else {
@@@ -4787,6 -4785,7 +4785,7 @@@ static int btrfs_link(struct dentry *ol
                return -EPERM;
  
        btrfs_inc_nlink(inode);
+       inode->i_ctime = CURRENT_TIME;
  
        err = btrfs_set_inode_index(dir, &index);
        if (err)
        }
  
        btrfs_set_trans_block_group(trans, dir);
 -      atomic_inc(&inode->i_count);
 +      ihold(inode);
  
-       err = btrfs_add_nondir(trans, dentry, inode, 1, index);
+       err = btrfs_add_nondir(trans, dir, dentry, inode, 1, index);
  
        if (err) {
                drop_inode = 1;
        } else {
+               struct dentry *parent = dget_parent(dentry);
                btrfs_update_inode_block_group(trans, dir);
                err = btrfs_update_inode(trans, root, inode);
                BUG_ON(err);
-               btrfs_log_new_name(trans, inode, NULL, dentry->d_parent);
+               btrfs_log_new_name(trans, inode, NULL, parent);
+               dput(parent);
        }
  
        nr = trans->blocks_used;
@@@ -4853,8 -4854,7 +4854,7 @@@ static int btrfs_mkdir(struct inode *di
        btrfs_set_trans_block_group(trans, dir);
  
        inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
-                               dentry->d_name.len,
-                               dentry->d_parent->d_inode->i_ino, objectid,
+                               dentry->d_name.len, dir->i_ino, objectid,
                                BTRFS_I(dir)->block_group, S_IFDIR | mode,
                                &index);
        if (IS_ERR(inode)) {
        if (err)
                goto out_fail;
  
-       err = btrfs_add_link(trans, dentry->d_parent->d_inode,
-                                inode, dentry->d_name.name,
-                                dentry->d_name.len, 0, index);
+       err = btrfs_add_link(trans, dir, inode, dentry->d_name.name,
+                            dentry->d_name.len, 0, index);
        if (err)
                goto out_fail;
  
@@@ -5535,13 -5534,21 +5534,21 @@@ struct btrfs_dio_private 
        u64 bytes;
        u32 *csums;
        void *private;
+       /* number of bios pending for this dio */
+       atomic_t pending_bios;
+       /* IO errors */
+       int errors;
+       struct bio *orig_bio;
  };
  
  static void btrfs_endio_direct_read(struct bio *bio, int err)
  {
+       struct btrfs_dio_private *dip = bio->bi_private;
        struct bio_vec *bvec_end = bio->bi_io_vec + bio->bi_vcnt - 1;
        struct bio_vec *bvec = bio->bi_io_vec;
-       struct btrfs_dio_private *dip = bio->bi_private;
        struct inode *inode = dip->inode;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        u64 start;
@@@ -5595,15 -5602,18 +5602,18 @@@ static void btrfs_endio_direct_write(st
        struct btrfs_trans_handle *trans;
        struct btrfs_ordered_extent *ordered = NULL;
        struct extent_state *cached_state = NULL;
+       u64 ordered_offset = dip->logical_offset;
+       u64 ordered_bytes = dip->bytes;
        int ret;
  
        if (err)
                goto out_done;
-       ret = btrfs_dec_test_ordered_pending(inode, &ordered,
-                                            dip->logical_offset, dip->bytes);
+ again:
+       ret = btrfs_dec_test_first_ordered_pending(inode, &ordered,
+                                                  &ordered_offset,
+                                                  ordered_bytes);
        if (!ret)
-               goto out_done;
+               goto out_test;
  
        BUG_ON(!ordered);
  
@@@ -5663,8 -5673,20 +5673,20 @@@ out_unlock
  out:
        btrfs_delalloc_release_metadata(inode, ordered->len);
        btrfs_end_transaction(trans, root);
+       ordered_offset = ordered->file_offset + ordered->len;
        btrfs_put_ordered_extent(ordered);
        btrfs_put_ordered_extent(ordered);
+ out_test:
+       /*
+        * our bio might span multiple ordered extents.  If we haven't
+        * completed the accounting for the whole dio, go back and try again
+        */
+       if (ordered_offset < dip->logical_offset + dip->bytes) {
+               ordered_bytes = dip->logical_offset + dip->bytes -
+                       ordered_offset;
+               goto again;
+       }
  out_done:
        bio->bi_private = dip->private;
  
@@@ -5684,6 -5706,176 +5706,176 @@@ static int __btrfs_submit_bio_start_dir
        return 0;
  }
  
+ static void btrfs_end_dio_bio(struct bio *bio, int err)
+ {
+       struct btrfs_dio_private *dip = bio->bi_private;
+       if (err) {
+               printk(KERN_ERR "btrfs direct IO failed ino %lu rw %lu "
+                     "disk_bytenr %lu len %u err no %d\n",
+                     dip->inode->i_ino, bio->bi_rw, bio->bi_sector,
+                     bio->bi_size, err);
+               dip->errors = 1;
+               /*
+                * before atomic variable goto zero, we must make sure
+                * dip->errors is perceived to be set.
+                */
+               smp_mb__before_atomic_dec();
+       }
+       /* if there are more bios still pending for this dio, just exit */
+       if (!atomic_dec_and_test(&dip->pending_bios))
+               goto out;
+       if (dip->errors)
+               bio_io_error(dip->orig_bio);
+       else {
+               set_bit(BIO_UPTODATE, &dip->orig_bio->bi_flags);
+               bio_endio(dip->orig_bio, 0);
+       }
+ out:
+       bio_put(bio);
+ }
+ static struct bio *btrfs_dio_bio_alloc(struct block_device *bdev,
+                                      u64 first_sector, gfp_t gfp_flags)
+ {
+       int nr_vecs = bio_get_nr_vecs(bdev);
+       return btrfs_bio_alloc(bdev, first_sector, nr_vecs, gfp_flags);
+ }
+ static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
+                                        int rw, u64 file_offset, int skip_sum,
+                                        u32 *csums)
+ {
+       int write = rw & REQ_WRITE;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       int ret;
+       bio_get(bio);
+       ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
+       if (ret)
+               goto err;
+       if (write && !skip_sum) {
+               ret = btrfs_wq_submit_bio(root->fs_info,
+                                  inode, rw, bio, 0, 0,
+                                  file_offset,
+                                  __btrfs_submit_bio_start_direct_io,
+                                  __btrfs_submit_bio_done);
+               goto err;
+       } else if (!skip_sum)
+               btrfs_lookup_bio_sums_dio(root, inode, bio,
+                                         file_offset, csums);
+       ret = btrfs_map_bio(root, rw, bio, 0, 1);
+ err:
+       bio_put(bio);
+       return ret;
+ }
+ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
+                                   int skip_sum)
+ {
+       struct inode *inode = dip->inode;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
+       struct bio *bio;
+       struct bio *orig_bio = dip->orig_bio;
+       struct bio_vec *bvec = orig_bio->bi_io_vec;
+       u64 start_sector = orig_bio->bi_sector;
+       u64 file_offset = dip->logical_offset;
+       u64 submit_len = 0;
+       u64 map_length;
+       int nr_pages = 0;
+       u32 *csums = dip->csums;
+       int ret = 0;
+       bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev, start_sector, GFP_NOFS);
+       if (!bio)
+               return -ENOMEM;
+       bio->bi_private = dip;
+       bio->bi_end_io = btrfs_end_dio_bio;
+       atomic_inc(&dip->pending_bios);
+       map_length = orig_bio->bi_size;
+       ret = btrfs_map_block(map_tree, READ, start_sector << 9,
+                             &map_length, NULL, 0);
+       if (ret) {
+               bio_put(bio);
+               return -EIO;
+       }
+       while (bvec <= (orig_bio->bi_io_vec + orig_bio->bi_vcnt - 1)) {
+               if (unlikely(map_length < submit_len + bvec->bv_len ||
+                   bio_add_page(bio, bvec->bv_page, bvec->bv_len,
+                                bvec->bv_offset) < bvec->bv_len)) {
+                       /*
+                        * inc the count before we submit the bio so
+                        * we know the end IO handler won't happen before
+                        * we inc the count. Otherwise, the dip might get freed
+                        * before we're done setting it up
+                        */
+                       atomic_inc(&dip->pending_bios);
+                       ret = __btrfs_submit_dio_bio(bio, inode, rw,
+                                                    file_offset, skip_sum,
+                                                    csums);
+                       if (ret) {
+                               bio_put(bio);
+                               atomic_dec(&dip->pending_bios);
+                               goto out_err;
+                       }
+                       if (!skip_sum)
+                               csums = csums + nr_pages;
+                       start_sector += submit_len >> 9;
+                       file_offset += submit_len;
+                       submit_len = 0;
+                       nr_pages = 0;
+                       bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev,
+                                                 start_sector, GFP_NOFS);
+                       if (!bio)
+                               goto out_err;
+                       bio->bi_private = dip;
+                       bio->bi_end_io = btrfs_end_dio_bio;
+                       map_length = orig_bio->bi_size;
+                       ret = btrfs_map_block(map_tree, READ, start_sector << 9,
+                                             &map_length, NULL, 0);
+                       if (ret) {
+                               bio_put(bio);
+                               goto out_err;
+                       }
+               } else {
+                       submit_len += bvec->bv_len;
+                       nr_pages ++;
+                       bvec++;
+               }
+       }
+       ret = __btrfs_submit_dio_bio(bio, inode, rw, file_offset, skip_sum,
+                                    csums);
+       if (!ret)
+               return 0;
+       bio_put(bio);
+ out_err:
+       dip->errors = 1;
+       /*
+        * before atomic variable goto zero, we must
+        * make sure dip->errors is perceived to be set.
+        */
+       smp_mb__before_atomic_dec();
+       if (atomic_dec_and_test(&dip->pending_bios))
+               bio_io_error(dip->orig_bio);
+       /* bio_end_io() will handle error, so we needn't return it */
+       return 0;
+ }
  static void btrfs_submit_direct(int rw, struct bio *bio, struct inode *inode,
                                loff_t file_offset)
  {
  
        dip->disk_bytenr = (u64)bio->bi_sector << 9;
        bio->bi_private = dip;
+       dip->errors = 0;
+       dip->orig_bio = bio;
+       atomic_set(&dip->pending_bios, 0);
  
        if (write)
                bio->bi_end_io = btrfs_endio_direct_write;
        else
                bio->bi_end_io = btrfs_endio_direct_read;
  
-       ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
-       if (ret)
-               goto out_err;
-       if (write && !skip_sum) {
-               ret = btrfs_wq_submit_bio(BTRFS_I(inode)->root->fs_info,
-                                  inode, rw, bio, 0, 0,
-                                  dip->logical_offset,
-                                  __btrfs_submit_bio_start_direct_io,
-                                  __btrfs_submit_bio_done);
-               if (ret)
-                       goto out_err;
+       ret = btrfs_submit_direct_hook(rw, dip, skip_sum);
+       if (!ret)
                return;
-       } else if (!skip_sum)
-               btrfs_lookup_bio_sums_dio(root, inode, bio,
-                                         dip->logical_offset, dip->csums);
-       ret = btrfs_map_bio(root, rw, bio, 0, 1);
-       if (ret)
-               goto out_err;
-       return;
- out_err:
-       kfree(dip->csums);
-       kfree(dip);
  free_ordered:
        /*
         * If this is a write, we need to clean up the reserved space and kill
@@@ -6607,8 -6781,9 +6781,9 @@@ static int btrfs_rename(struct inode *o
        BUG_ON(ret);
  
        if (old_inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) {
-               btrfs_log_new_name(trans, old_inode, old_dir,
-                                  new_dentry->d_parent);
+               struct dentry *parent = dget_parent(new_dentry);
+               btrfs_log_new_name(trans, old_inode, old_dir, parent);
+               dput(parent);
                btrfs_end_log_trans(root);
        }
  out_fail:
@@@ -6758,8 -6933,7 +6933,7 @@@ static int btrfs_symlink(struct inode *
        btrfs_set_trans_block_group(trans, dir);
  
        inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
-                               dentry->d_name.len,
-                               dentry->d_parent->d_inode->i_ino, objectid,
+                               dentry->d_name.len, dir->i_ino, objectid,
                                BTRFS_I(dir)->block_group, S_IFLNK|S_IRWXUGO,
                                &index);
        err = PTR_ERR(inode);
        }
  
        btrfs_set_trans_block_group(trans, inode);
-       err = btrfs_add_nondir(trans, dentry, inode, 0, index);
+       err = btrfs_add_nondir(trans, dir, dentry, inode, 0, index);
        if (err)
                drop_inode = 1;
        else {
@@@ -6844,6 -7018,7 +7018,7 @@@ static int __btrfs_prealloc_file_range(
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_key ins;
        u64 cur_offset = start;
+       u64 i_size;
        int ret = 0;
        bool own_trans = true;
  
                    (actual_len > inode->i_size) &&
                    (cur_offset > inode->i_size)) {
                        if (cur_offset > actual_len)
-                               i_size_write(inode, actual_len);
+                               i_size = actual_len;
                        else
-                               i_size_write(inode, cur_offset);
-                       i_size_write(inode, cur_offset);
-                       btrfs_ordered_update_i_size(inode, cur_offset, NULL);
+                               i_size = cur_offset;
+                       i_size_write(inode, i_size);
+                       btrfs_ordered_update_i_size(inode, i_size, NULL);
                }
  
                ret = btrfs_update_inode(trans, root, inode);
@@@ -6943,6 -7118,10 +7118,10 @@@ static long btrfs_fallocate(struct inod
        btrfs_wait_ordered_range(inode, alloc_start, alloc_end - alloc_start);
  
        mutex_lock(&inode->i_mutex);
+       ret = inode_newsize_ok(inode, alloc_end);
+       if (ret)
+               goto out;
        if (alloc_start > inode->i_size) {
                ret = btrfs_cont_expand(inode, alloc_start);
                if (ret)
@@@ -7139,6 -7318,7 +7318,7 @@@ static const struct inode_operations bt
        .readlink       = generic_readlink,
        .follow_link    = page_follow_link_light,
        .put_link       = page_put_link,
+       .getattr        = btrfs_getattr,
        .permission     = btrfs_permission,
        .setxattr       = btrfs_setxattr,
        .getxattr       = btrfs_getxattr,
diff --combined fs/btrfs/super.c
@@@ -244,6 -244,7 +244,7 @@@ int btrfs_parse_options(struct btrfs_ro
                case Opt_space_cache:
                        printk(KERN_INFO "btrfs: enabling disk space caching\n");
                        btrfs_set_opt(info->mount_opt, SPACE_CACHE);
+                       break;
                case Opt_clear_cache:
                        printk(KERN_INFO "btrfs: force clearing of disk cache\n");
                        btrfs_set_opt(info->mount_opt, CLEAR_CACHE);
@@@ -562,25 -563,41 +563,41 @@@ static int btrfs_show_options(struct se
  
  static int btrfs_test_super(struct super_block *s, void *data)
  {
-       struct btrfs_fs_devices *test_fs_devices = data;
+       struct btrfs_root *test_root = data;
        struct btrfs_root *root = btrfs_sb(s);
  
-       return root->fs_info->fs_devices == test_fs_devices;
+       /*
+        * If this super block is going away, return false as it
+        * can't match as an existing super block.
+        */
+       if (!atomic_read(&s->s_active))
+               return 0;
+       return root->fs_info->fs_devices == test_root->fs_info->fs_devices;
+ }
+ static int btrfs_set_super(struct super_block *s, void *data)
+ {
+       s->s_fs_info = data;
+       return set_anon_super(s, data);
  }
  
  /*
   * Find a superblock for the given device / mount point.
   *
   * Note:  This is based on get_sb_bdev from fs/super.c with a few additions
   *      for multiple device setup.  Make sure to keep it in sync.
   */
 -static int btrfs_get_sb(struct file_system_type *fs_type, int flags,
 -              const char *dev_name, void *data, struct vfsmount *mnt)
 +static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 +              const char *dev_name, void *data)
  {
        struct block_device *bdev = NULL;
        struct super_block *s;
        struct dentry *root;
        struct btrfs_fs_devices *fs_devices = NULL;
+       struct btrfs_root *tree_root = NULL;
+       struct btrfs_fs_info *fs_info = NULL;
        fmode_t mode = FMODE_READ;
        char *subvol_name = NULL;
        u64 subvol_objectid = 0;
                                          &subvol_name, &subvol_objectid,
                                          &fs_devices);
        if (error)
 -              return error;
 +              return ERR_PTR(error);
  
        error = btrfs_scan_one_device(dev_name, mode, fs_type, &fs_devices);
        if (error)
                goto error_close_devices;
        }
  
+       /*
+        * Setup a dummy root and fs_info for test/set super.  This is because
+        * we don't actually fill this stuff out until open_ctree, but we need
+        * it for searching for existing supers, so this lets us do that and
+        * then open_ctree will properly initialize everything later.
+        */
+       fs_info = kzalloc(sizeof(struct btrfs_fs_info), GFP_NOFS);
+       tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS);
+       if (!fs_info || !tree_root) {
+               error = -ENOMEM;
+               goto error_close_devices;
+       }
+       fs_info->tree_root = tree_root;
+       fs_info->fs_devices = fs_devices;
+       tree_root->fs_info = fs_info;
        bdev = fs_devices->latest_bdev;
-       s = sget(fs_type, btrfs_test_super, set_anon_super, fs_devices);
+       s = sget(fs_type, btrfs_test_super, btrfs_set_super, tree_root);
        if (IS_ERR(s))
                goto error_s;
  
                root = new_root;
        }
  
 -      mnt->mnt_sb = s;
 -      mnt->mnt_root = root;
 -
        kfree(subvol_name);
 -      return 0;
 +      return root;
  
  error_s:
        error = PTR_ERR(s);
  error_close_devices:
        btrfs_close_devices(fs_devices);
+       kfree(fs_info);
+       kfree(tree_root);
  error_free_subvol_name:
        kfree(subvol_name);
 -      return error;
 +      return ERR_PTR(error);
  }
  
  static int btrfs_remount(struct super_block *sb, int *flags, char *data)
@@@ -761,7 -799,7 +796,7 @@@ static int btrfs_statfs(struct dentry *
  static struct file_system_type btrfs_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "btrfs",
 -      .get_sb         = btrfs_get_sb,
 +      .mount          = btrfs_mount,
        .kill_sb        = kill_anon_super,
        .fs_flags       = FS_REQUIRES_DEV,
  };
@@@ -830,7 -868,6 +865,7 @@@ static const struct file_operations btr
        .unlocked_ioctl  = btrfs_control_ioctl,
        .compat_ioctl = btrfs_control_ioctl,
        .owner   = THIS_MODULE,
 +      .llseek = noop_llseek,
  };
  
  static struct miscdevice btrfs_misc = {