cur = btrfs_find_tree_block(root, blocknr, blocksize);
                if (cur)
-                       uptodate = btrfs_buffer_uptodate(cur);
+                       uptodate = btrfs_buffer_uptodate(cur, gen);
                else
                        uptodate = 0;
                if (!cur || !uptodate) {
 
        return 0;
 }
 
+static int verify_parent_transid(struct extent_io_tree *io_tree,
+                                struct extent_buffer *eb, u64 parent_transid)
+{
+       int ret;
+
+       if (!parent_transid || btrfs_header_generation(eb) == parent_transid)
+               return 0;
+
+       lock_extent(io_tree, eb->start, eb->start + eb->len - 1, GFP_NOFS);
+       if (extent_buffer_uptodate(io_tree, eb) &&
+           btrfs_header_generation(eb) == parent_transid) {
+               ret = 0;
+               goto out;
+       }
+       printk("parent transid verify failed on %llu wanted %llu found %llu\n",
+              (unsigned long long)eb->start,
+              (unsigned long long)parent_transid,
+              (unsigned long long)btrfs_header_generation(eb));
+       ret = 1;
+out:
+       clear_extent_buffer_uptodate(io_tree, eb);
+       unlock_extent(io_tree, eb->start, eb->start + eb->len - 1,
+                     GFP_NOFS);
+       return ret;
+
+}
+
 static int btree_read_extent_buffer_pages(struct btrfs_root *root,
                                          struct extent_buffer *eb,
                                          u64 start, u64 parent_transid)
        while (1) {
                ret = read_extent_buffer_pages(io_tree, eb, start, 1,
                                               btree_get_extent, mirror_num);
-               if (!ret)
+               if (!ret &&
+                   !verify_parent_transid(io_tree, eb, parent_transid))
                        return ret;
 
                num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
                ret = -EIO;
                goto err;
        }
+       if (memcmp_extent_buffer(eb, root->fs_info->fsid,
+                                (unsigned long)btrfs_header_fsid(eb),
+                                BTRFS_FSID_SIZE)) {
+               printk("bad fsid on block %Lu\n", eb->start);
+               ret = -EIO;
+               goto err;
+       }
        found_level = btrfs_header_level(eb);
 
        ret = csum_tree_block(root, eb, 1);
                                        "I/O error on %s\n",
                                       bdevname(bh->b_bdev, b));
                }
-               set_buffer_write_io_error(bh);
+               /* note, we dont' set_buffer_write_io_error because we have
+                * our own ways of dealing with the IO errors
+                */
                clear_buffer_uptodate(bh);
        }
        unlock_buffer(bh);
                                ret = submit_bh(WRITE, bh);
                                BUG_ON(ret);
                                wait_on_buffer(bh);
-                               BUG_ON(!buffer_uptodate(bh));
+                               if (!buffer_uptodate(bh))
+                                       total_errors++;
                        } else {
                                total_errors++;
                        }
        return 0;
 }
 
-int btrfs_buffer_uptodate(struct extent_buffer *buf)
+int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
 {
+       int ret;
        struct inode *btree_inode = buf->first_page->mapping->host;
-       return extent_buffer_uptodate(&BTRFS_I(btree_inode)->io_tree, buf);
+
+       ret = extent_buffer_uptodate(&BTRFS_I(btree_inode)->io_tree, buf);
+       if (!ret)
+               return ret;
+
+       ret = verify_parent_transid(&BTRFS_I(btree_inode)->io_tree, buf,
+                                   parent_transid);
+       return !ret;
 }
 
 int btrfs_set_buffer_uptodate(struct extent_buffer *buf)
 
 void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr);
 int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root);
 void btrfs_mark_buffer_dirty(struct extent_buffer *buf);
-int btrfs_buffer_uptodate(struct extent_buffer *buf);
+int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid);
 int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
 int wait_on_tree_block_writeback(struct btrfs_root *root,
                                 struct extent_buffer *buf);
 
        if (!pending) {
                buf = btrfs_find_tree_block(root, bytenr, num_bytes);
                if (buf) {
-                       if (btrfs_buffer_uptodate(buf)) {
+                       if (btrfs_buffer_uptodate(buf, 0)) {
                                u64 transid =
                                    root->fs_info->running_transaction->transid;
                                u64 header_transid =
                        continue;
                }
                next = btrfs_find_tree_block(root, bytenr, blocksize);
-               if (!next || !btrfs_buffer_uptodate(next)) {
+               if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
                        free_extent_buffer(next);
                        reada_walk_down(root, cur, path->slots[*level]);
 
 
                                   unsigned int bytes_done, int err)
 #endif
 {
-       const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+       int uptodate = err == 0;
        struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
        struct extent_state *state = bio->bi_private;
        struct extent_io_tree *tree = state->tree;
        u64 end;
        u64 cur;
        int whole_page;
+       int ret;
        unsigned long flags;
 
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
                if (--bvec >= bio->bi_io_vec)
                        prefetchw(&bvec->bv_page->flags);
 
+               if (tree->ops && tree->ops->writepage_end_io_hook) {
+                       ret = tree->ops->writepage_end_io_hook(page, start,
+                                                      end, state);
+                       if (ret)
+                               uptodate = 0;
+               }
+
+               if (!uptodate && tree->ops &&
+                   tree->ops->writepage_io_failed_hook) {
+                       ret = tree->ops->writepage_io_failed_hook(bio, page,
+                                                        start, end, state);
+                       if (ret == 0) {
+                               state = NULL;
+                               uptodate = (err == 0);
+                               continue;
+                       }
+               }
+
                if (!uptodate) {
                        clear_extent_uptodate(tree, start, end, GFP_ATOMIC);
                        ClearPageUptodate(page);
                        SetPageError(page);
                }
 
-               if (tree->ops && tree->ops->writepage_end_io_hook) {
-                       tree->ops->writepage_end_io_hook(page, start, end,
-                                                        state);
-               }
-
                /*
                 * bios can get merged in funny ways, and so we need to
                 * be careful with the state variable.  We know the
                } else {
                        ret = 0;
                }
-               if (ret)
+               if (ret) {
                        SetPageError(page);
-               else {
+               } else {
                        unsigned long max_nr = end_index + 1;
                        set_range_writeback(tree, cur, cur + iosize - 1);
                        if (!PageWriteback(page)) {
 }
 EXPORT_SYMBOL(set_extent_buffer_dirty);
 
+int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
+                               struct extent_buffer *eb)
+{
+       unsigned long i;
+       struct page *page;
+       unsigned long num_pages;
+
+       num_pages = num_extent_pages(eb->start, eb->len);
+       eb->flags &= ~EXTENT_UPTODATE;
+
+       clear_extent_uptodate(tree, eb->start, eb->start + eb->len - 1,
+                             GFP_NOFS);
+       for (i = 0; i < num_pages; i++) {
+               page = extent_buffer_page(eb, i);
+               ClearPageUptodate(page);
+       }
+       return 0;
+}
+
 int set_extent_buffer_uptodate(struct extent_io_tree *tree,
                                struct extent_buffer *eb)
 {
 
        int (*readpage_io_failed_hook)(struct bio *bio, struct page *page,
                                       u64 start, u64 end,
                                       struct extent_state *state);
+       int (*writepage_io_failed_hook)(struct bio *bio, struct page *page,
+                                       u64 start, u64 end,
+                                      struct extent_state *state);
        int (*readpage_end_io_hook)(struct page *page, u64 start, u64 end,
                                    struct extent_state *state);
-       void (*writepage_end_io_hook)(struct page *page, u64 start, u64 end,
+       int (*writepage_end_io_hook)(struct page *page, u64 start, u64 end,
                                      struct extent_state *state);
        int (*set_bit_hook)(struct inode *inode, u64 start, u64 end,
                            unsigned long old, unsigned long bits);
                             struct extent_buffer *eb);
 int set_extent_buffer_uptodate(struct extent_io_tree *tree,
                               struct extent_buffer *eb);
+int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
+                               struct extent_buffer *eb);
 int extent_buffer_uptodate(struct extent_io_tree *tree,
                           struct extent_buffer *eb);
 int map_extent_buffer(struct extent_buffer *eb, unsigned long offset,
 
        int last_mirror;
 };
 
-int btrfs_readpage_io_failed_hook(struct bio *failed_bio,
-                                 struct page *page, u64 start, u64 end,
-                                 struct extent_state *state)
+int btrfs_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 bio *bio;
        int num_copies;
        int ret;
+       int rw;
        u64 logical;
 
        ret = get_state_private(failure_tree, start, &private);
        bio->bi_bdev = failed_bio->bi_bdev;
        bio->bi_size = 0;
        bio_add_page(bio, page, failrec->len, start - page_offset(page));
-       btrfs_submit_bio_hook(inode, READ, bio, failrec->last_mirror);
+       if (failed_bio->bi_rw & (1 << BIO_RW))
+               rw = WRITE;
+       else
+               rw = READ;
+
+       BTRFS_I(inode)->io_tree.ops->submit_bio_hook(inode, rw, bio,
+                                                     failrec->last_mirror);
+       return 0;
+}
+
+int btrfs_clean_io_failures(struct inode *inode, u64 start)
+{
+       u64 private;
+       u64 private_failure;
+       struct io_failure_record *failure;
+       int ret;
+
+       private = 0;
+       if (count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private,
+                            (u64)-1, 1, EXTENT_DIRTY)) {
+               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;
 }
 
        /* 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);
-               }
-       }
+       btrfs_clean_io_failures(inode, start);
        return 0;
 
 zeroit:
        .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,
+       .readpage_io_failed_hook = btrfs_io_failed_hook,
        .set_bit_hook = btrfs_set_bit_hook,
        .clear_bit_hook = btrfs_clear_bit_hook,
 };
 
        struct extent_buffer *next;
        struct extent_buffer *cur;
        u64 bytenr;
+       u64 ptr_gen;
        int ret = 0;
        int is_extent = 0;
 
                        break;
                }
                bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
+               ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
 
                if (cache_only) {
                        next = btrfs_find_tree_block(root, bytenr,
                                           btrfs_level_size(root, *level - 1));
-                       if (!next || !btrfs_buffer_uptodate(next) ||
+                       if (!next || !btrfs_buffer_uptodate(next, ptr_gen) ||
                            !btrfs_buffer_defrag(next)) {
                                free_extent_buffer(next);
                                path->slots[*level]++;
                } else {
                        next = read_tree_block(root, bytenr,
                                       btrfs_level_size(root, *level - 1),
-                                      btrfs_node_ptr_generation(cur,
-                                                        path->slots[*level]));
+                                      ptr_gen);
                }
                ret = btrfs_cow_block(trans, root, next, path->nodes[*level],
                                      path->slots[*level], &next);
 
        if (atomic_dec_and_test(&multi->stripes_pending)) {
                bio->bi_private = multi->private;
                bio->bi_end_io = multi->end_io;
-
                /* only send an error to the higher layers if it is
                 * beyond the tolerance of the multi-bio
                 */
-               if (atomic_read(&multi->error) > multi->max_errors)
+               if (atomic_read(&multi->error) > multi->max_errors) {
                        err = -EIO;
-               else
+               } else if (err) {
+                       /*
+                        * this bio is actually up to date, we didn't
+                        * go over the max number of errors
+                        */
+                       set_bit(BIO_UPTODATE, &bio->bi_flags);
                        err = 0;
+               }
                kfree(multi);
 
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)