Btrfs: get forced transaction commits via workqueue

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 74e6aad..e93ba1a 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -283,6 +283,7 @@
 struct crypto_hash;
 
 struct btrfs_fs_info {
+	spinlock_t hash_lock;
 	struct btrfs_root *extent_root;
 	struct btrfs_root *tree_root;
 	struct btrfs_root *dev_root;
@@ -308,10 +309,11 @@
 	struct mutex trans_mutex;
 	struct mutex fs_mutex;
 	struct list_head trans_list;
+	struct list_head dead_roots;
 	struct crypto_hash *hash_tfm;
 	struct delayed_work trans_work;
-	spinlock_t hash_lock;
 	int do_barriers;
+	int closing;
 };
 
 /*
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 048282f0..751069c 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -563,6 +563,7 @@
 	INIT_RADIX_TREE(&fs_info->block_group_radix, GFP_KERNEL);
 	INIT_RADIX_TREE(&fs_info->block_group_data_radix, GFP_KERNEL);
 	INIT_LIST_HEAD(&fs_info->trans_list);
+	INIT_LIST_HEAD(&fs_info->dead_roots);
 	sb_set_blocksize(sb, 4096);
 	fs_info->running_transaction = NULL;
 	fs_info->tree_root = tree_root;
@@ -577,6 +578,8 @@
 	fs_info->do_barriers = 1;
 	fs_info->extent_tree_insert_nr = 0;
 	fs_info->extent_tree_prealloc_nr = 0;
+	fs_info->closing = 0;
+
 	INIT_DELAYED_WORK(&fs_info->trans_work, btrfs_transaction_cleaner);
 	BTRFS_I(fs_info->btree_inode)->root = tree_root;
 	memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
@@ -746,6 +749,7 @@
 	struct btrfs_trans_handle *trans;
 	struct btrfs_fs_info *fs_info = root->fs_info;
 
+	fs_info->closing = 1;
 	btrfs_transaction_flush_work(root);
 	mutex_lock(&fs_info->fs_mutex);
 	trans = btrfs_start_transaction(root, 1);
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index bf7eef6..b859db3 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -12,12 +12,10 @@
 
 #define BTRFS_ROOT_TRANS_TAG 0
 
-#define TRANS_MAGIC 0xE1E10E
 static void put_transaction(struct btrfs_transaction *transaction)
 {
 	WARN_ON(transaction->use_count == 0);
 	transaction->use_count--;
-	WARN_ON(transaction->magic != TRANS_MAGIC);
 	if (transaction->use_count == 0) {
 		WARN_ON(total_trans == 0);
 		total_trans--;
@@ -42,7 +40,6 @@
 		cur_trans->transid = root->fs_info->generation;
 		init_waitqueue_head(&cur_trans->writer_wait);
 		init_waitqueue_head(&cur_trans->commit_wait);
-		cur_trans->magic = TRANS_MAGIC;
 		cur_trans->in_commit = 0;
 		cur_trans->use_count = 1;
 		cur_trans->commit_done = 0;
@@ -83,7 +80,6 @@
 	h->block_group = NULL;
 	root->fs_info->running_transaction->use_count++;
 	mutex_unlock(&root->fs_info->trans_mutex);
-	h->magic = h->magic2 = TRANS_MAGIC;
 	return h;
 }
 
@@ -92,8 +88,6 @@
 {
 	struct btrfs_transaction *cur_trans;
 
-	WARN_ON(trans->magic != TRANS_MAGIC);
-	WARN_ON(trans->magic2 != TRANS_MAGIC);
 	mutex_lock(&root->fs_info->trans_mutex);
 	cur_trans = root->fs_info->running_transaction;
 	WARN_ON(cur_trans->num_writers < 1);
@@ -257,8 +251,8 @@
 	struct dirty_root *dirty;
 	struct btrfs_trans_handle *trans;
 	int ret;
-
 	while(!list_empty(list)) {
+		mutex_lock(&tree_root->fs_info->fs_mutex);
 		dirty = list_entry(list->next, struct dirty_root, list);
 		list_del_init(&dirty->list);
 		trans = btrfs_start_transaction(tree_root, 1);
@@ -271,6 +265,7 @@
 		ret = btrfs_end_transaction(trans, tree_root);
 		BUG_ON(ret);
 		kfree(dirty);
+		mutex_unlock(&tree_root->fs_info->fs_mutex);
 	}
 	return 0;
 }
@@ -346,10 +341,18 @@
 	wake_up(&cur_trans->commit_wait);
 	put_transaction(cur_trans);
 	put_transaction(cur_trans);
+	if (root->fs_info->closing)
+		list_splice_init(&root->fs_info->dead_roots, &dirty_fs_roots);
+	else
+		list_splice_init(&dirty_fs_roots, &root->fs_info->dead_roots);
 	mutex_unlock(&root->fs_info->trans_mutex);
 	kmem_cache_free(btrfs_trans_handle_cachep, trans);
 
-	drop_dirty_roots(root->fs_info->tree_root, &dirty_fs_roots);
+	if (root->fs_info->closing) {
+		mutex_unlock(&root->fs_info->fs_mutex);
+		drop_dirty_roots(root->fs_info->tree_root, &dirty_fs_roots);
+		mutex_lock(&root->fs_info->fs_mutex);
+	}
 	return ret;
 }
 
@@ -362,11 +365,19 @@
 	struct btrfs_root *root = fs_info->tree_root;
 	struct btrfs_transaction *cur;
 	struct btrfs_trans_handle *trans;
+	struct list_head dirty_roots;
 	unsigned long now;
 	unsigned long delay = HZ * 30;
 	int ret;
 
-printk("btrfs transaction cleaner\n");
+	INIT_LIST_HEAD(&dirty_roots);
+	mutex_lock(&root->fs_info->trans_mutex);
+	list_splice_init(&root->fs_info->dead_roots, &dirty_roots);
+	mutex_unlock(&root->fs_info->trans_mutex);
+
+	if (!list_empty(&dirty_roots)) {
+		drop_dirty_roots(root, &dirty_roots);
+	}
 	mutex_lock(&root->fs_info->fs_mutex);
 	mutex_lock(&root->fs_info->trans_mutex);
 	cur = root->fs_info->running_transaction;
@@ -381,7 +392,6 @@
 		goto out;
 	}
 	mutex_unlock(&root->fs_info->trans_mutex);
-printk("forcing commit\n");
 	trans = btrfs_start_transaction(root, 1);
 	ret = btrfs_commit_transaction(trans, root);
 out:
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 0b08208..f25b490 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -8,7 +8,6 @@
 	int in_commit;
 	int use_count;
 	int commit_done;
-	int magic;
 	struct list_head list;
 	struct radix_tree_root dirty_pages;
 	unsigned long start_time;
@@ -17,13 +16,11 @@
 };
 
 struct btrfs_trans_handle {
-	int magic;
 	u64 transid;
 	unsigned long blocks_reserved;
 	unsigned long blocks_used;
 	struct btrfs_transaction *transaction;
 	struct btrfs_block_group_cache *block_group;
-	int magic2;
 };