Btrfs: fix joining the same transaction handler more than 2 times
authorMiao Xie <miaox@cn.fujitsu.com>
Thu, 1 Nov 2012 07:32:18 +0000 (07:32 +0000)
committerJosef Bacik <jbacik@fusionio.com>
Wed, 12 Dec 2012 22:15:20 +0000 (17:15 -0500)
If we flush inodes with pending delalloc in a transaction, we may join
the same transaction handler more than 2 times.

The reason is:
  Task use_count of trans handle
  commit_transaction 1
    |-> btrfs_start_delalloc_inodes 1
  |-> run_delalloc_nocow 1
|-> join_transaction 2
|-> cow_file_range 2
|-> join_transaction 3

In fact, cow_file_range needn't join the transaction again because the caller
have joined the transaction, so we fix this problem by this way.

Reported-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
fs/btrfs/inode.c
fs/btrfs/transaction.c

index dce9e218b845eb258732d36a3a40e1af91a37675..96d20903beebaa7351c5213122a37ff806e42963 100644 (file)
@@ -804,14 +804,14 @@ static u64 get_extent_allocation_hint(struct inode *inode, u64 start,
  * required to start IO on it.  It may be clean and already done with
  * IO when we return.
  */
-static noinline int cow_file_range(struct inode *inode,
-                                  struct page *locked_page,
-                                  u64 start, u64 end, int *page_started,
-                                  unsigned long *nr_written,
-                                  int unlock)
+static noinline int __cow_file_range(struct btrfs_trans_handle *trans,
+                                    struct inode *inode,
+                                    struct btrfs_root *root,
+                                    struct page *locked_page,
+                                    u64 start, u64 end, int *page_started,
+                                    unsigned long *nr_written,
+                                    int unlock)
 {
-       struct btrfs_root *root = BTRFS_I(inode)->root;
-       struct btrfs_trans_handle *trans;
        u64 alloc_hint = 0;
        u64 num_bytes;
        unsigned long ram_size;
@@ -824,25 +824,10 @@ static noinline int cow_file_range(struct inode *inode,
        int ret = 0;
 
        BUG_ON(btrfs_is_free_space_inode(inode));
-       trans = btrfs_join_transaction(root);
-       if (IS_ERR(trans)) {
-               extent_clear_unlock_delalloc(inode,
-                            &BTRFS_I(inode)->io_tree,
-                            start, end, locked_page,
-                            EXTENT_CLEAR_UNLOCK_PAGE |
-                            EXTENT_CLEAR_UNLOCK |
-                            EXTENT_CLEAR_DELALLOC |
-                            EXTENT_CLEAR_DIRTY |
-                            EXTENT_SET_WRITEBACK |
-                            EXTENT_END_WRITEBACK);
-               return PTR_ERR(trans);
-       }
-       trans->block_rsv = &root->fs_info->delalloc_block_rsv;
 
        num_bytes = (end - start + blocksize) & ~(blocksize - 1);
        num_bytes = max(blocksize,  num_bytes);
        disk_num_bytes = num_bytes;
-       ret = 0;
 
        /* if this is a small write inside eof, kick off defrag */
        if (num_bytes < 64 * 1024 &&
@@ -953,11 +938,9 @@ static noinline int cow_file_range(struct inode *inode,
                alloc_hint = ins.objectid + ins.offset;
                start += cur_alloc_size;
        }
-       ret = 0;
 out:
-       btrfs_end_transaction(trans, root);
-
        return ret;
+
 out_unlock:
        extent_clear_unlock_delalloc(inode,
                     &BTRFS_I(inode)->io_tree,
@@ -972,6 +955,39 @@ out_unlock:
        goto out;
 }
 
+static noinline int cow_file_range(struct inode *inode,
+                                  struct page *locked_page,
+                                  u64 start, u64 end, int *page_started,
+                                  unsigned long *nr_written,
+                                  int unlock)
+{
+       struct btrfs_trans_handle *trans;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       int ret;
+
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               extent_clear_unlock_delalloc(inode,
+                            &BTRFS_I(inode)->io_tree,
+                            start, end, locked_page,
+                            EXTENT_CLEAR_UNLOCK_PAGE |
+                            EXTENT_CLEAR_UNLOCK |
+                            EXTENT_CLEAR_DELALLOC |
+                            EXTENT_CLEAR_DIRTY |
+                            EXTENT_SET_WRITEBACK |
+                            EXTENT_END_WRITEBACK);
+               return PTR_ERR(trans);
+       }
+       trans->block_rsv = &root->fs_info->delalloc_block_rsv;
+
+       ret = __cow_file_range(trans, inode, root, locked_page, start, end,
+                              page_started, nr_written, unlock);
+
+       btrfs_end_transaction(trans, root);
+
+       return ret;
+}
+
 /*
  * work queue call back to started compression on a file and pages
  */
@@ -1282,9 +1298,9 @@ out_check:
 
                btrfs_release_path(path);
                if (cow_start != (u64)-1) {
-                       ret = cow_file_range(inode, locked_page, cow_start,
-                                       found_key.offset - 1, page_started,
-                                       nr_written, 1);
+                       ret = __cow_file_range(trans, inode, root, locked_page,
+                                              cow_start, found_key.offset - 1,
+                                              page_started, nr_written, 1);
                        if (ret) {
                                btrfs_abort_transaction(trans, root, ret);
                                goto error;
@@ -1353,8 +1369,9 @@ out_check:
        }
 
        if (cow_start != (u64)-1) {
-               ret = cow_file_range(inode, locked_page, cow_start, end,
-                                    page_started, nr_written, 1);
+               ret = __cow_file_range(trans, inode, root, locked_page,
+                                      cow_start, end,
+                                      page_started, nr_written, 1);
                if (ret) {
                        btrfs_abort_transaction(trans, root, ret);
                        goto error;
index 259f74eabdb883717b3213447a18051b79d4f0d1..44a5d73fddbe21b2450dd424c808ed5de8ead77a 100644 (file)
@@ -312,6 +312,7 @@ start_transaction(struct btrfs_root *root, u64 num_items, int type,
                WARN_ON(type != TRANS_JOIN && type != TRANS_JOIN_NOLOCK);
                h = current->journal_info;
                h->use_count++;
+               WARN_ON(h->use_count > 2);
                h->orig_rsv = h->block_rsv;
                h->block_rsv = NULL;
                goto got_it;