minix: replace inode uid,gid,mode init with helper
[safe/jmp/linux-2.6] / fs / btrfs / ordered-data.c
index 897fba8..a127c0e 100644 (file)
@@ -16,7 +16,6 @@
  * Boston, MA 021110-1307, USA.
  */
 
-#include <linux/gfp.h>
 #include <linux/slab.h>
 #include <linux/blkdev.h>
 #include <linux/writeback.h>
@@ -174,7 +173,6 @@ int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
        if (!entry)
                return -ENOMEM;
 
-       mutex_lock(&tree->mutex);
        entry->file_offset = file_offset;
        entry->start = start;
        entry->len = len;
@@ -190,16 +188,17 @@ int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
        INIT_LIST_HEAD(&entry->list);
        INIT_LIST_HEAD(&entry->root_extent_list);
 
+       spin_lock(&tree->lock);
        node = tree_insert(&tree->tree, file_offset,
                           &entry->rb_node);
        BUG_ON(node);
+       spin_unlock(&tree->lock);
 
        spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
        list_add_tail(&entry->root_extent_list,
                      &BTRFS_I(inode)->root->fs_info->ordered_extents);
        spin_unlock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
 
-       mutex_unlock(&tree->mutex);
        BUG_ON(node);
        return 0;
 }
@@ -216,9 +215,9 @@ int btrfs_add_ordered_sum(struct inode *inode,
        struct btrfs_ordered_inode_tree *tree;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       mutex_lock(&tree->mutex);
+       spin_lock(&tree->lock);
        list_add_tail(&sum->list, &entry->list);
-       mutex_unlock(&tree->mutex);
+       spin_unlock(&tree->lock);
        return 0;
 }
 
@@ -232,15 +231,16 @@ int btrfs_add_ordered_sum(struct inode *inode,
  * to make sure this function only returns 1 once for a given ordered extent.
  */
 int btrfs_dec_test_ordered_pending(struct inode *inode,
+                                  struct btrfs_ordered_extent **cached,
                                   u64 file_offset, u64 io_size)
 {
        struct btrfs_ordered_inode_tree *tree;
        struct rb_node *node;
-       struct btrfs_ordered_extent *entry;
+       struct btrfs_ordered_extent *entry = NULL;
        int ret;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       mutex_lock(&tree->mutex);
+       spin_lock(&tree->lock);
        node = tree_search(tree, file_offset);
        if (!node) {
                ret = 1;
@@ -264,7 +264,11 @@ int btrfs_dec_test_ordered_pending(struct inode *inode,
        else
                ret = 1;
 out:
-       mutex_unlock(&tree->mutex);
+       if (!ret && cached && entry) {
+               *cached = entry;
+               atomic_inc(&entry->refs);
+       }
+       spin_unlock(&tree->lock);
        return ret == 0;
 }
 
@@ -291,22 +295,30 @@ int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
 
 /*
  * remove an ordered extent from the tree.  No references are dropped
- * but, anyone waiting on this extent is woken up.
+ * and you must wake_up entry->wait.  You must hold the tree lock
+ * while you call this function.
  */
-int btrfs_remove_ordered_extent(struct inode *inode,
+static int __btrfs_remove_ordered_extent(struct inode *inode,
                                struct btrfs_ordered_extent *entry)
 {
        struct btrfs_ordered_inode_tree *tree;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
        struct rb_node *node;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       mutex_lock(&tree->mutex);
        node = &entry->rb_node;
        rb_erase(node, &tree->tree);
        tree->last = NULL;
        set_bit(BTRFS_ORDERED_COMPLETE, &entry->flags);
 
-       spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
+       spin_lock(&BTRFS_I(inode)->accounting_lock);
+       WARN_ON(!BTRFS_I(inode)->outstanding_extents);
+       BTRFS_I(inode)->outstanding_extents--;
+       spin_unlock(&BTRFS_I(inode)->accounting_lock);
+       btrfs_unreserve_metadata_for_delalloc(BTRFS_I(inode)->root,
+                                             inode, 1);
+
+       spin_lock(&root->fs_info->ordered_extent_lock);
        list_del_init(&entry->root_extent_list);
 
        /*
@@ -318,18 +330,36 @@ int btrfs_remove_ordered_extent(struct inode *inode,
            !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_DIRTY)) {
                list_del_init(&BTRFS_I(inode)->ordered_operations);
        }
-       spin_unlock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
+       spin_unlock(&root->fs_info->ordered_extent_lock);
 
-       mutex_unlock(&tree->mutex);
-       wake_up(&entry->wait);
        return 0;
 }
 
 /*
+ * remove an ordered extent from the tree.  No references are dropped
+ * but any waiters are woken.
+ */
+int btrfs_remove_ordered_extent(struct inode *inode,
+                               struct btrfs_ordered_extent *entry)
+{
+       struct btrfs_ordered_inode_tree *tree;
+       int ret;
+
+       tree = &BTRFS_I(inode)->ordered_tree;
+       spin_lock(&tree->lock);
+       ret = __btrfs_remove_ordered_extent(inode, entry);
+       spin_unlock(&tree->lock);
+       wake_up(&entry->wait);
+
+       return ret;
+}
+
+/*
  * wait for all the ordered extents in a root.  This is done when balancing
  * space between drives.
  */
-int btrfs_wait_ordered_extents(struct btrfs_root *root, int nocow_only)
+int btrfs_wait_ordered_extents(struct btrfs_root *root,
+                              int nocow_only, int delay_iput)
 {
        struct list_head splice;
        struct list_head *cur;
@@ -366,7 +396,10 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nocow_only)
                if (inode) {
                        btrfs_start_ordered_extent(inode, ordered, 1);
                        btrfs_put_ordered_extent(ordered);
-                       iput(inode);
+                       if (delay_iput)
+                               btrfs_add_delayed_iput(inode);
+                       else
+                               iput(inode);
                } else {
                        btrfs_put_ordered_extent(ordered);
                }
@@ -424,7 +457,7 @@ again:
                                btrfs_wait_ordered_range(inode, 0, (u64)-1);
                        else
                                filemap_flush(inode->i_mapping);
-                       iput(inode);
+                       btrfs_add_delayed_iput(inode);
                }
 
                cond_resched();
@@ -540,7 +573,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
        struct btrfs_ordered_extent *entry = NULL;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       mutex_lock(&tree->mutex);
+       spin_lock(&tree->lock);
        node = tree_search(tree, file_offset);
        if (!node)
                goto out;
@@ -551,7 +584,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
        if (entry)
                atomic_inc(&entry->refs);
 out:
-       mutex_unlock(&tree->mutex);
+       spin_unlock(&tree->lock);
        return entry;
 }
 
@@ -567,7 +600,7 @@ btrfs_lookup_first_ordered_extent(struct inode *inode, u64 file_offset)
        struct btrfs_ordered_extent *entry = NULL;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       mutex_lock(&tree->mutex);
+       spin_lock(&tree->lock);
        node = tree_search(tree, file_offset);
        if (!node)
                goto out;
@@ -575,7 +608,7 @@ btrfs_lookup_first_ordered_extent(struct inode *inode, u64 file_offset)
        entry = rb_entry(node, struct btrfs_ordered_extent, rb_node);
        atomic_inc(&entry->refs);
 out:
-       mutex_unlock(&tree->mutex);
+       spin_unlock(&tree->lock);
        return entry;
 }
 
@@ -583,7 +616,7 @@ out:
  * After an extent is done, call this to conditionally update the on disk
  * i_size.  i_size is updated to cover any fully written part of the file.
  */
-int btrfs_ordered_update_i_size(struct inode *inode,
+int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
                                struct btrfs_ordered_extent *ordered)
 {
        struct btrfs_ordered_inode_tree *tree = &BTRFS_I(inode)->ordered_tree;
@@ -591,18 +624,32 @@ int btrfs_ordered_update_i_size(struct inode *inode,
        u64 disk_i_size;
        u64 new_i_size;
        u64 i_size_test;
+       u64 i_size = i_size_read(inode);
        struct rb_node *node;
+       struct rb_node *prev = NULL;
        struct btrfs_ordered_extent *test;
+       int ret = 1;
 
-       mutex_lock(&tree->mutex);
+       if (ordered)
+               offset = entry_end(ordered);
+       else
+               offset = ALIGN(offset, BTRFS_I(inode)->root->sectorsize);
+
+       spin_lock(&tree->lock);
        disk_i_size = BTRFS_I(inode)->disk_i_size;
 
+       /* truncate file */
+       if (disk_i_size > i_size) {
+               BTRFS_I(inode)->disk_i_size = i_size;
+               ret = 0;
+               goto out;
+       }
+
        /*
         * if the disk i_size is already at the inode->i_size, or
         * this ordered extent is inside the disk i_size, we're done
         */
-       if (disk_i_size >= inode->i_size ||
-           ordered->file_offset + ordered->len <= disk_i_size) {
+       if (disk_i_size == i_size || offset <= disk_i_size) {
                goto out;
        }
 
@@ -610,8 +657,7 @@ int btrfs_ordered_update_i_size(struct inode *inode,
         * we can't update the disk_isize if there are delalloc bytes
         * between disk_i_size and  this ordered extent
         */
-       if (test_range_bit(io_tree, disk_i_size,
-                          ordered->file_offset + ordered->len - 1,
+       if (test_range_bit(io_tree, disk_i_size, offset - 1,
                           EXTENT_DELALLOC, 0, NULL)) {
                goto out;
        }
@@ -620,20 +666,32 @@ int btrfs_ordered_update_i_size(struct inode *inode,
         * if we find an ordered extent then we can't update disk i_size
         * yet
         */
-       node = &ordered->rb_node;
-       while (1) {
-               node = rb_prev(node);
-               if (!node)
-                       break;
+       if (ordered) {
+               node = rb_prev(&ordered->rb_node);
+       } else {
+               prev = tree_search(tree, offset);
+               /*
+                * we insert file extents without involving ordered struct,
+                * so there should be no ordered struct cover this offset
+                */
+               if (prev) {
+                       test = rb_entry(prev, struct btrfs_ordered_extent,
+                                       rb_node);
+                       BUG_ON(offset_in_entry(test, offset));
+               }
+               node = prev;
+       }
+       while (node) {
                test = rb_entry(node, struct btrfs_ordered_extent, rb_node);
                if (test->file_offset + test->len <= disk_i_size)
                        break;
-               if (test->file_offset >= inode->i_size)
+               if (test->file_offset >= i_size)
                        break;
                if (test->file_offset >= disk_i_size)
                        goto out;
+               node = rb_prev(node);
        }
-       new_i_size = min_t(u64, entry_end(ordered), i_size_read(inode));
+       new_i_size = min_t(u64, offset, i_size);
 
        /*
         * at this point, we know we can safely update i_size to at least
@@ -641,7 +699,14 @@ int btrfs_ordered_update_i_size(struct inode *inode,
         * walk forward and see if ios from higher up in the file have
         * finished.
         */
-       node = rb_next(&ordered->rb_node);
+       if (ordered) {
+               node = rb_next(&ordered->rb_node);
+       } else {
+               if (prev)
+                       node = rb_next(prev);
+               else
+                       node = rb_first(&tree->tree);
+       }
        i_size_test = 0;
        if (node) {
                /*
@@ -649,10 +714,10 @@ int btrfs_ordered_update_i_size(struct inode *inode,
                 * between our ordered extent and the next one.
                 */
                test = rb_entry(node, struct btrfs_ordered_extent, rb_node);
-               if (test->file_offset > entry_end(ordered))
+               if (test->file_offset > offset)
                        i_size_test = test->file_offset;
        } else {
-               i_size_test = i_size_read(inode);
+               i_size_test = i_size;
        }
 
        /*
@@ -661,15 +726,25 @@ int btrfs_ordered_update_i_size(struct inode *inode,
         * are no delalloc bytes in this area, it is safe to update
         * disk_i_size to the end of the region.
         */
-       if (i_size_test > entry_end(ordered) &&
-           !test_range_bit(io_tree, entry_end(ordered), i_size_test - 1,
-                          EXTENT_DELALLOC, 0, NULL)) {
-               new_i_size = min_t(u64, i_size_test, i_size_read(inode));
+       if (i_size_test > offset &&
+           !test_range_bit(io_tree, offset, i_size_test - 1,
+                           EXTENT_DELALLOC, 0, NULL)) {
+               new_i_size = min_t(u64, i_size_test, i_size);
        }
        BTRFS_I(inode)->disk_i_size = new_i_size;
+       ret = 0;
 out:
-       mutex_unlock(&tree->mutex);
-       return 0;
+       /*
+        * we need to remove the ordered extent with the tree lock held
+        * so that other people calling this function don't find our fully
+        * processed ordered entry and skip updating the i_size
+        */
+       if (ordered)
+               __btrfs_remove_ordered_extent(inode, ordered);
+       spin_unlock(&tree->lock);
+       if (ordered)
+               wake_up(&ordered->wait);
+       return ret;
 }
 
 /*
@@ -693,7 +768,7 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
        if (!ordered)
                return 1;
 
-       mutex_lock(&tree->mutex);
+       spin_lock(&tree->lock);
        list_for_each_entry_reverse(ordered_sum, &ordered->list, list) {
                if (disk_bytenr >= ordered_sum->bytenr) {
                        num_sectors = ordered_sum->len / sectorsize;
@@ -708,7 +783,7 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
                }
        }
 out:
-       mutex_unlock(&tree->mutex);
+       spin_unlock(&tree->lock);
        btrfs_put_ordered_extent(ordered);
        return ret;
 }