Btrfs: do not run snapshot-aware defragment on error
authorLiu Bo <bo.li.liu@oracle.com>
Tue, 29 Oct 2013 02:45:05 +0000 (10:45 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 20 Dec 2013 15:45:12 +0000 (07:45 -0800)
commit 6f519564d7d978c00351d9ab6abac3deeac31621 upstream.

If something wrong happens in write endio, running snapshot-aware defragment
can end up with undefined results, maybe a crash, so we should avoid it.

In order to share similar code, this also adds a helper to free the struct for
snapshot-aware defrag.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/btrfs/inode.c

index 1e2288dc5346fb3ee60a018b131a103387cef7c8..0bcee78cde16c962ec28e3b338a47f5768cef70f 100644 (file)
@@ -2419,10 +2419,23 @@ out_unlock:
        return ret;
 }
 
+static void free_sa_defrag_extent(struct new_sa_defrag_extent *new)
+{
+       struct old_sa_defrag_extent *old, *tmp;
+
+       if (!new)
+               return;
+
+       list_for_each_entry_safe(old, tmp, &new->head, list) {
+               list_del(&old->list);
+               kfree(old);
+       }
+       kfree(new);
+}
+
 static void relink_file_extents(struct new_sa_defrag_extent *new)
 {
        struct btrfs_path *path;
-       struct old_sa_defrag_extent *old, *tmp;
        struct sa_defrag_extent_backref *backref;
        struct sa_defrag_extent_backref *prev = NULL;
        struct inode *inode;
@@ -2465,16 +2478,11 @@ static void relink_file_extents(struct new_sa_defrag_extent *new)
        kfree(prev);
 
        btrfs_free_path(path);
-
-       list_for_each_entry_safe(old, tmp, &new->head, list) {
-               list_del(&old->list);
-               kfree(old);
-       }
 out:
+       free_sa_defrag_extent(new);
+
        atomic_dec(&root->fs_info->defrag_running);
        wake_up(&root->fs_info->transaction_wait);
-
-       kfree(new);
 }
 
 static struct new_sa_defrag_extent *
@@ -2484,7 +2492,7 @@ record_old_file_extents(struct inode *inode,
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_path *path;
        struct btrfs_key key;
-       struct old_sa_defrag_extent *old, *tmp;
+       struct old_sa_defrag_extent *old;
        struct new_sa_defrag_extent *new;
        int ret;
 
@@ -2532,7 +2540,7 @@ record_old_file_extents(struct inode *inode,
                if (slot >= btrfs_header_nritems(l)) {
                        ret = btrfs_next_leaf(root, path);
                        if (ret < 0)
-                               goto out_free_list;
+                               goto out_free_path;
                        else if (ret > 0)
                                break;
                        continue;
@@ -2561,7 +2569,7 @@ record_old_file_extents(struct inode *inode,
 
                old = kmalloc(sizeof(*old), GFP_NOFS);
                if (!old)
-                       goto out_free_list;
+                       goto out_free_path;
 
                offset = max(new->file_pos, key.offset);
                end = min(new->file_pos + new->len, key.offset + num_bytes);
@@ -2583,15 +2591,10 @@ next:
 
        return new;
 
-out_free_list:
-       list_for_each_entry_safe(old, tmp, &new->head, list) {
-               list_del(&old->list);
-               kfree(old);
-       }
 out_free_path:
        btrfs_free_path(path);
 out_kfree:
-       kfree(new);
+       free_sa_defrag_extent(new);
        return NULL;
 }
 
@@ -2743,8 +2746,14 @@ out:
        btrfs_remove_ordered_extent(inode, ordered_extent);
 
        /* for snapshot-aware defrag */
-       if (new)
-               relink_file_extents(new);
+       if (new) {
+               if (ret) {
+                       free_sa_defrag_extent(new);
+                       atomic_dec(&root->fs_info->defrag_running);
+               } else {
+                       relink_file_extents(new);
+               }
+       }
 
        /* once for us */
        btrfs_put_ordered_extent(ordered_extent);