Btrfs: Replace the big fs_mutex with a collection of other locks

Extent alloctions are still protected by a large alloc_mutex.
Objectid allocations are covered by a objectid mutex
Other btree operations are protected by a lock on individual btree nodes

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 1ed433a..5a1ee06 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -370,6 +370,7 @@
 	struct btrfs_trans_handle *trans;
 	unsigned long nr;
 
+	smp_mb();
 	if (root->defrag_running)
 		return 0;
 	trans = btrfs_start_transaction(root, 1);
@@ -378,16 +379,15 @@
 		ret = btrfs_defrag_leaves(trans, root, cacheonly);
 		nr = trans->blocks_used;
 		btrfs_end_transaction(trans, root);
-		mutex_unlock(&info->fs_mutex);
 		btrfs_btree_balance_dirty(info->tree_root, nr);
 		cond_resched();
 
-		mutex_lock(&info->fs_mutex);
 		trans = btrfs_start_transaction(root, 1);
 		if (ret != -EAGAIN)
 			break;
 	}
 	root->defrag_running = 0;
+	smp_mb();
 	radix_tree_tag_clear(&info->fs_roots_radix,
 		     (unsigned long)root->root_key.objectid,
 		     BTRFS_ROOT_DEFRAG_TAG);
@@ -435,14 +435,14 @@
 	while(!list_empty(list)) {
 		struct btrfs_root *root;
 
-		mutex_lock(&tree_root->fs_info->fs_mutex);
 		dirty = list_entry(list->next, struct dirty_root, list);
 		list_del_init(&dirty->list);
 
 		num_bytes = btrfs_root_used(&dirty->root->root_item);
 		root = dirty->latest_root;
-		root->fs_info->throttles++;
+		atomic_inc(&root->fs_info->throttles);
 
+		mutex_lock(&root->fs_info->drop_mutex);
 		while(1) {
 			trans = btrfs_start_transaction(tree_root, 1);
 			ret = btrfs_drop_snapshot(trans, dirty->root);
@@ -459,14 +459,16 @@
 			nr = trans->blocks_used;
 			ret = btrfs_end_transaction(trans, tree_root);
 			BUG_ON(ret);
-			mutex_unlock(&tree_root->fs_info->fs_mutex);
+
+			mutex_unlock(&root->fs_info->drop_mutex);
 			btrfs_btree_balance_dirty(tree_root, nr);
 			cond_resched();
-			mutex_lock(&tree_root->fs_info->fs_mutex);
+			mutex_lock(&root->fs_info->drop_mutex);
 		}
 		BUG_ON(ret);
-		root->fs_info->throttles--;
+		atomic_dec(&root->fs_info->throttles);
 
+		mutex_lock(&root->fs_info->alloc_mutex);
 		num_bytes -= btrfs_root_used(&dirty->root->root_item);
 		bytes_used = btrfs_root_used(&root->root_item);
 		if (num_bytes) {
@@ -474,11 +476,15 @@
 			btrfs_set_root_used(&root->root_item,
 					    bytes_used - num_bytes);
 		}
+		mutex_unlock(&root->fs_info->alloc_mutex);
+
 		ret = btrfs_del_root(trans, tree_root, &dirty->root->root_key);
 		if (ret) {
 			BUG();
 			break;
 		}
+		mutex_unlock(&root->fs_info->drop_mutex);
+
 		nr = trans->blocks_used;
 		ret = btrfs_end_transaction(trans, tree_root);
 		BUG_ON(ret);
@@ -486,7 +492,6 @@
 		free_extent_buffer(dirty->root->node);
 		kfree(dirty->root);
 		kfree(dirty);
-		mutex_unlock(&tree_root->fs_info->fs_mutex);
 
 		btrfs_btree_balance_dirty(tree_root, nr);
 		cond_resched();
@@ -503,7 +508,7 @@
 	u64 objectid = 0;
 	int ret;
 
-	root->fs_info->throttles++;
+	atomic_inc(&root->fs_info->throttles);
 	while(1) {
 		ret = btrfs_find_first_ordered_inode(
 				&cur_trans->ordered_inode_tree,
@@ -512,7 +517,6 @@
 			break;
 
 		mutex_unlock(&root->fs_info->trans_mutex);
-		mutex_unlock(&root->fs_info->fs_mutex);
 
 		if (S_ISREG(inode->i_mode)) {
 			atomic_inc(&BTRFS_I(inode)->ordered_writeback);
@@ -521,7 +525,6 @@
 		}
 		iput(inode);
 
-		mutex_lock(&root->fs_info->fs_mutex);
 		mutex_lock(&root->fs_info->trans_mutex);
 	}
 	while(1) {
@@ -533,7 +536,6 @@
 		if (!ret)
 			break;
 		mutex_unlock(&root->fs_info->trans_mutex);
-		mutex_unlock(&root->fs_info->fs_mutex);
 
 		if (S_ISREG(inode->i_mode)) {
 			atomic_inc(&BTRFS_I(inode)->ordered_writeback);
@@ -543,10 +545,9 @@
 		atomic_dec(&inode->i_count);
 		iput(inode);
 
-		mutex_lock(&root->fs_info->fs_mutex);
 		mutex_lock(&root->fs_info->trans_mutex);
 	}
-	root->fs_info->throttles--;
+	atomic_dec(&root->fs_info->throttles);
 	return 0;
 }
 
@@ -661,7 +662,6 @@
 		mutex_unlock(&root->fs_info->trans_mutex);
 		btrfs_end_transaction(trans, root);
 
-		mutex_unlock(&root->fs_info->fs_mutex);
 		ret = wait_for_commit(root, cur_trans);
 		BUG_ON(ret);
 
@@ -669,7 +669,6 @@
 		put_transaction(cur_trans);
 		mutex_unlock(&root->fs_info->trans_mutex);
 
-		mutex_lock(&root->fs_info->fs_mutex);
 		return 0;
 	}
 
@@ -687,12 +686,10 @@
 					struct btrfs_transaction, list);
 		if (!prev_trans->commit_done) {
 			prev_trans->use_count++;
-			mutex_unlock(&root->fs_info->fs_mutex);
 			mutex_unlock(&root->fs_info->trans_mutex);
 
 			wait_for_commit(root, prev_trans);
 
-			mutex_lock(&root->fs_info->fs_mutex);
 			mutex_lock(&root->fs_info->trans_mutex);
 			put_transaction(prev_trans);
 		}
@@ -709,12 +706,10 @@
 		else
 			timeout = 1;
 
-		mutex_unlock(&root->fs_info->fs_mutex);
 		mutex_unlock(&root->fs_info->trans_mutex);
 
 		schedule_timeout(timeout);
 
-		mutex_lock(&root->fs_info->fs_mutex);
 		mutex_lock(&root->fs_info->trans_mutex);
 		finish_wait(&cur_trans->writer_wait, &wait);
 		ret = btrfs_write_ordered_inodes(trans, root);
@@ -755,12 +750,10 @@
 	btrfs_copy_pinned(root, pinned_copy);
 
 	mutex_unlock(&root->fs_info->trans_mutex);
-	mutex_unlock(&root->fs_info->fs_mutex);
 	ret = btrfs_write_and_wait_transaction(trans, root);
 	BUG_ON(ret);
 	write_ctree_super(trans, root);
 
-	mutex_lock(&root->fs_info->fs_mutex);
 	btrfs_finish_extent_commit(trans, root, pinned_copy);
 	mutex_lock(&root->fs_info->trans_mutex);
 
@@ -781,9 +774,7 @@
 	kmem_cache_free(btrfs_trans_handle_cachep, trans);
 
 	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;
 }
@@ -823,7 +814,7 @@
 	unsigned long delay = HZ * 30;
 	int ret;
 
-	mutex_lock(&root->fs_info->fs_mutex);
+	smp_mb();
 	if (root->fs_info->closing)
 		goto out;
 
@@ -844,7 +835,6 @@
 	trans = btrfs_start_transaction(root, 1);
 	ret = btrfs_commit_transaction(trans, root);
 out:
-	mutex_unlock(&root->fs_info->fs_mutex);
 	btrfs_clean_old_snapshots(root);
 	btrfs_transaction_queue_work(root, delay);
 }