]> Pileus Git - ~andy/linux/blobdiff - fs/btrfs/inode.c
Btrfs: Search data ordered extents first for checksums on read
[~andy/linux] / fs / btrfs / inode.c
index 0a687326c0b00343ab7571e5d4494b8f5281326a..0e90315ea803288a37816fe8a73655adb126c066 100644 (file)
@@ -128,7 +128,9 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end)
                goto out;
 
        BUG_ON(num_bytes > btrfs_super_total_bytes(&root->fs_info->super_copy));
+       mutex_lock(&BTRFS_I(inode)->extent_mutex);
        btrfs_drop_extent_cache(inode, start, start + num_bytes - 1);
+       mutex_unlock(&BTRFS_I(inode)->extent_mutex);
 
        while(num_bytes > 0) {
                cur_alloc_size = min(num_bytes, root->fs_info->max_extent);
@@ -144,6 +146,8 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end)
                em->len = ins.offset;
                em->block_start = ins.objectid;
                em->bdev = root->fs_info->fs_devices->latest_bdev;
+               mutex_lock(&BTRFS_I(inode)->extent_mutex);
+               set_bit(EXTENT_FLAG_PINNED, &em->flags);
                while(1) {
                        spin_lock(&em_tree->lock);
                        ret = add_extent_mapping(em_tree, em);
@@ -155,6 +159,7 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end)
                        btrfs_drop_extent_cache(inode, start,
                                                start + ins.offset - 1);
                }
+               mutex_unlock(&BTRFS_I(inode)->extent_mutex);
 
                cur_alloc_size = ins.offset;
                ret = btrfs_add_ordered_extent(inode, start, ins.objectid,
@@ -351,12 +356,8 @@ int __btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        int ret = 0;
-       struct btrfs_ordered_sum *sums;
 
-       ret = btrfs_csum_one_bio(root, bio, &sums);
-       BUG_ON(ret);
-
-       ret = btrfs_add_ordered_sum(inode, sums);
+       ret = btrfs_csum_one_bio(root, inode, bio);
        BUG_ON(ret);
 
        return btrfs_map_bio(root, rw, bio, mirror_num, 1);
@@ -382,7 +383,7 @@ mapit:
        return btrfs_map_bio(root, rw, bio, mirror_num, 0);
 }
 
-static int add_pending_csums(struct btrfs_trans_handle *trans,
+static noinline int add_pending_csums(struct btrfs_trans_handle *trans,
                             struct inode *inode, u64 file_offset,
                             struct list_head *list)
 {
@@ -390,15 +391,12 @@ static int add_pending_csums(struct btrfs_trans_handle *trans,
        struct btrfs_ordered_sum *sum;
 
        btrfs_set_trans_block_group(trans, inode);
-       while(!list_empty(list)) {
-               cur = list->next;
+       list_for_each(cur, list) {
                sum = list_entry(cur, struct btrfs_ordered_sum, list);
                mutex_lock(&BTRFS_I(inode)->csum_mutex);
                btrfs_csum_file_blocks(trans, BTRFS_I(inode)->root,
                                       inode, sum);
                mutex_unlock(&BTRFS_I(inode)->csum_mutex);
-               list_del(&sum->list);
-               kfree(sum);
        }
        return 0;
 }
@@ -420,7 +418,7 @@ void btrfs_writepage_fixup_worker(struct btrfs_work *work)
 
        fixup = container_of(work, struct btrfs_writepage_fixup, work);
        page = fixup->page;
-
+again:
        lock_page(page);
        if (!page->mapping || !PageDirty(page) || !PageChecked(page)) {
                ClearPageChecked(page);
@@ -432,9 +430,21 @@ void btrfs_writepage_fixup_worker(struct btrfs_work *work)
        page_end = page_offset(page) + PAGE_CACHE_SIZE - 1;
 
        lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end, GFP_NOFS);
-       ordered = btrfs_lookup_ordered_extent(inode, page_start);
-       if (ordered)
+
+       /* already ordered? We're done */
+       if (test_range_bit(&BTRFS_I(inode)->io_tree, page_start, page_end,
+                            EXTENT_ORDERED, 0)) {
                goto out;
+       }
+
+       ordered = btrfs_lookup_ordered_extent(inode, page_start);
+       if (ordered) {
+               unlock_extent(&BTRFS_I(inode)->io_tree, page_start,
+                             page_end, GFP_NOFS);
+               unlock_page(page);
+               btrfs_start_ordered_extent(inode, ordered, 1);
+               goto again;
+       }
 
        set_extent_delalloc(&BTRFS_I(inode)->io_tree, page_start, page_end,
                            GFP_NOFS);
@@ -475,7 +485,7 @@ int btrfs_writepage_start_hook(struct page *page, u64 start, u64 end)
        fixup = kzalloc(sizeof(*fixup), GFP_NOFS);
        if (!fixup)
                return -EAGAIN;
-printk("queueing worker to fixup page %lu %Lu\n", inode->i_ino, page_offset(page));
+
        SetPageChecked(page);
        page_cache_get(page);
        fixup->work.func = btrfs_writepage_fixup_worker;
@@ -484,23 +494,26 @@ printk("queueing worker to fixup page %lu %Lu\n", inode->i_ino, page_offset(page
        return -EAGAIN;
 }
 
-int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
-                               struct extent_state *state, int uptodate)
+static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
 {
-       struct inode *inode = page->mapping->host;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_trans_handle *trans;
        struct btrfs_ordered_extent *ordered_extent;
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
+       struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
+       struct extent_map *em;
+       struct extent_map *em_orig;
        u64 alloc_hint = 0;
+       u64 clear_start;
+       u64 clear_end;
        struct list_head list;
        struct btrfs_key ins;
+       struct rb_node *rb;
        int ret;
 
        ret = btrfs_dec_test_ordered_pending(inode, start, end - start + 1);
-       if (!ret) {
+       if (!ret)
                return 0;
-       }
 
        trans = btrfs_join_transaction(root, 1);
 
@@ -516,10 +529,30 @@ int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
        ins.objectid = ordered_extent->start;
        ins.offset = ordered_extent->len;
        ins.type = BTRFS_EXTENT_ITEM_KEY;
+
        ret = btrfs_alloc_reserved_extent(trans, root, root->root_key.objectid,
                                          trans->transid, inode->i_ino,
                                          ordered_extent->file_offset, &ins);
        BUG_ON(ret);
+
+       mutex_lock(&BTRFS_I(inode)->extent_mutex);
+
+       spin_lock(&em_tree->lock);
+       clear_start = ordered_extent->file_offset;
+       clear_end = ordered_extent->file_offset + ordered_extent->len;
+       em = lookup_extent_mapping(em_tree, clear_start,
+                                  ordered_extent->len);
+       em_orig = em;
+       while(em && clear_start < extent_map_end(em) && clear_end > em->start) {
+               clear_bit(EXTENT_FLAG_PINNED, &em->flags);
+               rb = rb_next(&em->rb_node);
+               if (!rb)
+                       break;
+               em = rb_entry(rb, struct extent_map, rb_node);
+       }
+       free_extent_map(em_orig);
+       spin_unlock(&em_tree->lock);
+
        ret = btrfs_drop_extents(trans, root, inode,
                                 ordered_extent->file_offset,
                                 ordered_extent->file_offset +
@@ -532,9 +565,12 @@ int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
                                       ordered_extent->len,
                                       ordered_extent->len, 0);
        BUG_ON(ret);
+
        btrfs_drop_extent_cache(inode, ordered_extent->file_offset,
                                ordered_extent->file_offset +
                                ordered_extent->len - 1);
+       mutex_unlock(&BTRFS_I(inode)->extent_mutex);
+
        inode->i_blocks += ordered_extent->len >> 9;
        unlock_extent(io_tree, ordered_extent->file_offset,
                    ordered_extent->file_offset + ordered_extent->len - 1,
@@ -544,6 +580,7 @@ int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
 
        btrfs_ordered_update_i_size(inode, ordered_extent);
        btrfs_remove_ordered_extent(inode, ordered_extent);
+
        /* once for us */
        btrfs_put_ordered_extent(ordered_extent);
        /* once for the tree */
@@ -554,6 +591,12 @@ int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
        return 0;
 }
 
+int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
+                               struct extent_state *state, int uptodate)
+{
+       return btrfs_finish_ordered_io(page->mapping->host, start, end);
+}
+
 int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end)
 {
        int ret = 0;
@@ -568,6 +611,22 @@ int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end)
            btrfs_test_flag(inode, NODATASUM))
                return 0;
 
+       /*
+        * It is possible there is an ordered extent that has
+        * not yet finished for this range in the file.  If so,
+        * that extent will have a csum cached, and it will insert
+        * the sum after all the blocks in the extent are fully
+        * on disk.  So, look for an ordered extent and use the
+        * sum if found.  We have to do this before looking in the
+        * btree because csum items are pre-inserted based on
+        * the file size.  btrfs_lookup_csum might find an item
+        * that still hasn't been fully filled.
+        */
+       ret = btrfs_find_ordered_sum(inode, start, &csum);
+       if (ret == 0)
+               goto found;
+
+       ret = 0;
        path = btrfs_alloc_path();
        item = btrfs_lookup_csum(NULL, root, path, inode->i_ino, start, 0);
        if (IS_ERR(item)) {
@@ -582,6 +641,7 @@ int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end)
        }
        read_extent_buffer(path->nodes[0], &csum, (unsigned long)item,
                           BTRFS_CRC32_SIZE);
+found:
        set_state_private(io_tree, start, csum);
 out:
        if (path)
@@ -888,7 +948,7 @@ static void fill_inode_item(struct extent_buffer *leaf,
                                    BTRFS_I(inode)->block_group->key.objectid);
 }
 
-int btrfs_update_inode(struct btrfs_trans_handle *trans,
+int noinline btrfs_update_inode(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root,
                              struct inode *inode)
 {
@@ -1317,7 +1377,7 @@ again:
                }
                if (!PageUptodate(page)) {
                        ret = -EIO;
-                       goto out;
+                       goto out_unlock;
                }
        }
        wait_on_page_writeback(page);
@@ -1330,7 +1390,7 @@ again:
                unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
                unlock_page(page);
                page_cache_release(page);
-               btrfs_wait_ordered_extent(inode, ordered);
+               btrfs_start_ordered_extent(inode, ordered, 1);
                btrfs_put_ordered_extent(ordered);
                goto again;
        }
@@ -1348,6 +1408,7 @@ again:
        set_page_dirty(page);
        unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
 
+out_unlock:
        unlock_page(page);
        page_cache_release(page);
 out:
@@ -1390,6 +1451,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
 
                trans = btrfs_start_transaction(root, 1);
                btrfs_set_trans_block_group(trans, inode);
+               mutex_lock(&BTRFS_I(inode)->extent_mutex);
                err = btrfs_drop_extents(trans, root, inode,
                                         hole_start, block_end, hole_start,
                                         &alloc_hint);
@@ -1403,6 +1465,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
                                                (u64)-1);
                        btrfs_check_file(root, inode);
                }
+               mutex_unlock(&BTRFS_I(inode)->extent_mutex);
                btrfs_end_transaction(trans, root);
                unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
                if (err)
@@ -1421,11 +1484,11 @@ void btrfs_delete_inode(struct inode *inode)
        unsigned long nr;
        int ret;
 
-       btrfs_wait_ordered_range(inode, 0, (u64)-1);
        truncate_inode_pages(&inode->i_data, 0);
        if (is_bad_inode(inode)) {
                goto no_delete;
        }
+       btrfs_wait_ordered_range(inode, 0, (u64)-1);
 
        btrfs_i_size_write(inode, 0);
        trans = btrfs_start_transaction(root, 1);
@@ -1567,7 +1630,9 @@ static int btrfs_init_locked_inode(struct inode *inode, void *p)
                             inode->i_mapping, GFP_NOFS);
        extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
                             inode->i_mapping, GFP_NOFS);
+       btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
        mutex_init(&BTRFS_I(inode)->csum_mutex);
+       mutex_init(&BTRFS_I(inode)->extent_mutex);
        return 0;
 }
 
@@ -1868,7 +1933,9 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                             inode->i_mapping, GFP_NOFS);
        extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
                             inode->i_mapping, GFP_NOFS);
+       btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
        mutex_init(&BTRFS_I(inode)->csum_mutex);
+       mutex_init(&BTRFS_I(inode)->extent_mutex);
        BTRFS_I(inode)->delalloc_bytes = 0;
        BTRFS_I(inode)->disk_i_size = 0;
        BTRFS_I(inode)->root = root;
@@ -2094,9 +2161,11 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
                extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
                                     inode->i_mapping, GFP_NOFS);
                mutex_init(&BTRFS_I(inode)->csum_mutex);
+               mutex_init(&BTRFS_I(inode)->extent_mutex);
                BTRFS_I(inode)->delalloc_bytes = 0;
                BTRFS_I(inode)->disk_i_size = 0;
                BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
+               btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
        }
        dir->i_sb->s_dirt = 1;
        btrfs_update_inode_block_group(trans, inode);
@@ -2256,7 +2325,7 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
        u64 extent_end = 0;
        u64 objectid = inode->i_ino;
        u32 found_type;
-       struct btrfs_path *path;
+       struct btrfs_path *path = NULL;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_file_extent_item *item;
        struct extent_buffer *leaf;
@@ -2266,9 +2335,6 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct btrfs_trans_handle *trans = NULL;
 
-       path = btrfs_alloc_path();
-       BUG_ON(!path);
-
 again:
        spin_lock(&em_tree->lock);
        em = lookup_extent_mapping(em_tree, start, len);
@@ -2292,6 +2358,12 @@ again:
        em->bdev = root->fs_info->fs_devices->latest_bdev;
        em->start = EXTENT_MAP_HOLE;
        em->len = (u64)-1;
+
+       if (!path) {
+               path = btrfs_alloc_path();
+               BUG_ON(!path);
+       }
+
        ret = btrfs_lookup_file_extent(trans, root, path,
                                       objectid, start, trans != NULL);
        if (ret < 0) {
@@ -2468,7 +2540,8 @@ insert:
        }
        spin_unlock(&em_tree->lock);
 out:
-       btrfs_free_path(path);
+       if (path)
+               btrfs_free_path(path);
        if (trans) {
                ret = btrfs_end_transaction(trans, root);
                if (!err) {
@@ -2581,8 +2654,8 @@ static int btrfs_writepage(struct page *page, struct writeback_control *wbc)
        return extent_write_full_page(tree, page, btrfs_get_extent, wbc);
 }
 
-static int btrfs_writepages(struct address_space *mapping,
-                           struct writeback_control *wbc)
+int btrfs_writepages(struct address_space *mapping,
+                    struct writeback_control *wbc)
 {
        struct extent_io_tree *tree;
        tree = &BTRFS_I(mapping->host)->io_tree;
@@ -2608,7 +2681,6 @@ static int __btrfs_releasepage(struct page *page, gfp_t gfp_flags)
        map = &BTRFS_I(page->mapping->host)->extent_tree;
        ret = try_release_extent_mapping(map, tree, page, gfp_flags);
        if (ret == 1) {
-               invalidate_extent_lru(tree, page_offset(page), PAGE_CACHE_SIZE);
                ClearPagePrivate(page);
                set_page_private(page, 0);
                page_cache_release(page);
@@ -2618,14 +2690,6 @@ static int __btrfs_releasepage(struct page *page, gfp_t gfp_flags)
 
 static int btrfs_releasepage(struct page *page, gfp_t gfp_flags)
 {
-       struct btrfs_ordered_extent *ordered;
-
-       ordered = btrfs_lookup_ordered_extent(page->mapping->host,
-                                             page_offset(page));
-       if (ordered) {
-               btrfs_put_ordered_extent(ordered);
-               return 0;
-       }
        return __btrfs_releasepage(page, gfp_flags);
 }
 
@@ -2647,11 +2711,15 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset)
        ordered = btrfs_lookup_ordered_extent(page->mapping->host,
                                           page_offset(page));
        if (ordered) {
+               /*
+                * IO on this page will never be started, so we need
+                * to account for any ordered extents now
+                */
                clear_extent_bit(tree, page_start, page_end,
                                 EXTENT_DIRTY | EXTENT_DELALLOC |
                                 EXTENT_LOCKED, 1, 0, GFP_NOFS);
-               btrfs_writepage_end_io_hook(page, page_start,
-                                           page_end, NULL, 1);
+               btrfs_finish_ordered_io(page->mapping->host,
+                                       page_start, page_end);
                btrfs_put_ordered_extent(ordered);
                lock_extent(tree, page_start, page_end, GFP_NOFS);
        }
@@ -2661,9 +2729,8 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset)
                 1, 1, GFP_NOFS);
        __btrfs_releasepage(page, GFP_NOFS);
 
+       ClearPageChecked(page);
        if (PagePrivate(page)) {
-               invalidate_extent_lru(tree, page_offset(page),
-                                     PAGE_CACHE_SIZE);
                ClearPagePrivate(page);
                set_page_private(page, 0);
                page_cache_release(page);
@@ -2719,11 +2786,15 @@ again:
        lock_extent(io_tree, page_start, page_end, GFP_NOFS);
        set_page_extent_mapped(page);
 
+       /*
+        * we can't set the delalloc bits if there are pending ordered
+        * extents.  Drop our locks and wait for them to finish
+        */
        ordered = btrfs_lookup_ordered_extent(inode, page_start);
        if (ordered) {
                unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
                unlock_page(page);
-               btrfs_wait_ordered_extent(inode, ordered);
+               btrfs_start_ordered_extent(inode, ordered, 1);
                btrfs_put_ordered_extent(ordered);
                goto again;
        }
@@ -2768,10 +2839,10 @@ static void btrfs_truncate(struct inode *inode)
                return;
 
        btrfs_truncate_page(inode->i_mapping, inode->i_size);
+       btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
 
        trans = btrfs_start_transaction(root, 1);
        btrfs_set_trans_block_group(trans, inode);
-       btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
        btrfs_i_size_write(inode, inode->i_size);
 
        /* FIXME, add redo link to tree so we don't leak on crash */
@@ -3075,9 +3146,11 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
                extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
                                     inode->i_mapping, GFP_NOFS);
                mutex_init(&BTRFS_I(inode)->csum_mutex);
+               mutex_init(&BTRFS_I(inode)->extent_mutex);
                BTRFS_I(inode)->delalloc_bytes = 0;
                BTRFS_I(inode)->disk_i_size = 0;
                BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
+               btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
        }
        dir->i_sb->s_dirt = 1;
        btrfs_update_inode_block_group(trans, inode);