Btrfs: fix a deadlock in aborting transaction due to ENOSPC
authorLiu Bo <bo.li.liu@oracle.com>
Mon, 5 Nov 2012 12:42:08 +0000 (12:42 +0000)
committerJosef Bacik <jbacik@fusionio.com>
Wed, 12 Dec 2012 22:15:25 +0000 (17:15 -0500)
When committing a transaction, we may bail out of running delayed refs
due to ENOSPC, and then abort the current transaction to flip into readonly.

But we'll hit a deadlock on ref head's lock since we forget to release
its lock and other cleanup stuff.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
fs/btrfs/extent-tree.c

index 0bcb9543da60ad2ef89e2c9eb5a8793b8b0e6678..f8a358aee060e8591a779baa2eba44a406ebe3ff 100644 (file)
@@ -2297,6 +2297,9 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
                                kfree(extent_op);
 
                                if (ret) {
+                                       list_del_init(&locked_ref->cluster);
+                                       mutex_unlock(&locked_ref->mutex);
+
                                        printk(KERN_DEBUG "btrfs: run_delayed_extent_op returned %d\n", ret);
                                        spin_lock(&delayed_refs->lock);
                                        return ret;
@@ -2339,6 +2342,10 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
                count++;
 
                if (ret) {
+                       if (locked_ref) {
+                               list_del_init(&locked_ref->cluster);
+                               mutex_unlock(&locked_ref->mutex);
+                       }
                        printk(KERN_DEBUG "btrfs: run_one_delayed_ref returned %d\n", ret);
                        spin_lock(&delayed_refs->lock);
                        return ret;