OSDN Git Service

btrfs: don't prematurely free work in end_workqueue_fn()
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / btrfs / qgroup.c
index 5279fda..734babb 100644 (file)
@@ -995,7 +995,7 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
                goto out;
        fs_info->quota_enabled = 0;
        fs_info->pending_quota_state = 0;
-       btrfs_qgroup_wait_for_completion(fs_info);
+       btrfs_qgroup_wait_for_completion(fs_info, false);
        spin_lock(&fs_info->qgroup_lock);
        quota_root = fs_info->quota_root;
        fs_info->quota_root = NULL;
@@ -2186,6 +2186,21 @@ void assert_qgroups_uptodate(struct btrfs_trans_handle *trans)
 }
 
 /*
+ * Check if the leaf is the last leaf. Which means all node pointers
+ * are at their last position.
+ */
+static bool is_last_leaf(struct btrfs_path *path)
+{
+       int i;
+
+       for (i = 1; i < BTRFS_MAX_LEVEL && path->nodes[i]; i++) {
+               if (path->slots[i] != btrfs_header_nritems(path->nodes[i]) - 1)
+                       return false;
+       }
+       return true;
+}
+
+/*
  * returns < 0 on error, 0 when more leafs are to be scanned.
  * returns 1 when done.
  */
@@ -2198,6 +2213,7 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
        struct ulist *roots = NULL;
        struct seq_list tree_mod_seq_elem = SEQ_LIST_INIT(tree_mod_seq_elem);
        u64 num_bytes;
+       bool done;
        int slot;
        int ret;
 
@@ -2225,6 +2241,7 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
                mutex_unlock(&fs_info->qgroup_rescan_lock);
                return ret;
        }
+       done = is_last_leaf(path);
 
        btrfs_item_key_to_cpu(path->nodes[0], &found,
                              btrfs_header_nritems(path->nodes[0]) - 1);
@@ -2271,6 +2288,8 @@ out:
        }
        btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem);
 
+       if (done && !ret)
+               ret = 1;
        return ret;
 }
 
@@ -2309,9 +2328,6 @@ out:
        btrfs_free_path(path);
 
        mutex_lock(&fs_info->qgroup_rescan_lock);
-       if (!btrfs_fs_closing(fs_info))
-               fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
-
        if (err > 0 &&
            fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT) {
                fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
@@ -2327,16 +2343,30 @@ out:
        trans = btrfs_start_transaction(fs_info->quota_root, 1);
        if (IS_ERR(trans)) {
                err = PTR_ERR(trans);
+               trans = NULL;
                btrfs_err(fs_info,
                          "fail to start transaction for status update: %d\n",
                          err);
-               goto done;
        }
-       ret = update_qgroup_status_item(trans, fs_info, fs_info->quota_root);
-       if (ret < 0) {
-               err = ret;
-               btrfs_err(fs_info, "fail to update qgroup status: %d\n", err);
+
+       mutex_lock(&fs_info->qgroup_rescan_lock);
+       if (!btrfs_fs_closing(fs_info))
+               fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+       if (trans) {
+               ret = update_qgroup_status_item(trans, fs_info, fs_info->quota_root);
+               if (ret < 0) {
+                       err = ret;
+                       btrfs_err(fs_info, "fail to update qgroup status: %d",
+                                 err);
+               }
        }
+       fs_info->qgroup_rescan_running = false;
+       complete_all(&fs_info->qgroup_rescan_completion);
+       mutex_unlock(&fs_info->qgroup_rescan_lock);
+
+       if (!trans)
+               return;
+
        btrfs_end_transaction(trans, fs_info->quota_root);
 
        if (btrfs_fs_closing(fs_info)) {
@@ -2347,9 +2377,6 @@ out:
        } else {
                btrfs_err(fs_info, "qgroup scan failed with %d", err);
        }
-
-done:
-       complete_all(&fs_info->qgroup_rescan_completion);
 }
 
 /*
@@ -2390,6 +2417,7 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
                sizeof(fs_info->qgroup_rescan_progress));
        fs_info->qgroup_rescan_progress.objectid = progress_objectid;
        init_completion(&fs_info->qgroup_rescan_completion);
+       fs_info->qgroup_rescan_running = true;
 
        spin_unlock(&fs_info->qgroup_lock);
        mutex_unlock(&fs_info->qgroup_rescan_lock);
@@ -2423,6 +2451,7 @@ qgroup_rescan_zero_tracking(struct btrfs_fs_info *fs_info)
                qgroup->rfer_cmpr = 0;
                qgroup->excl = 0;
                qgroup->excl_cmpr = 0;
+               qgroup_dirty(fs_info, qgroup);
        }
        spin_unlock(&fs_info->qgroup_lock);
 }
@@ -2467,20 +2496,26 @@ btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info)
        return 0;
 }
 
-int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info)
+int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info,
+                                    bool interruptible)
 {
        int running;
        int ret = 0;
 
        mutex_lock(&fs_info->qgroup_rescan_lock);
        spin_lock(&fs_info->qgroup_lock);
-       running = fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+       running = fs_info->qgroup_rescan_running;
        spin_unlock(&fs_info->qgroup_lock);
        mutex_unlock(&fs_info->qgroup_rescan_lock);
 
-       if (running)
+       if (!running)
+               return 0;
+
+       if (interruptible)
                ret = wait_for_completion_interruptible(
                                        &fs_info->qgroup_rescan_completion);
+       else
+               wait_for_completion(&fs_info->qgroup_rescan_completion);
 
        return ret;
 }