Btrfs: Add efficient dirty accounting to the extent_map tree

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index de05525..eef4ab5 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -210,7 +210,7 @@
 {
 	struct extent_map_tree *tree;
 	tree = &BTRFS_I(mapping->host)->extent_tree;
-	if (wbc->sync_mode == WB_SYNC_NONE && current_is_pdflush()) {
+	if (wbc->sync_mode == WB_SYNC_NONE) {
 		u64 num_dirty;
 		u64 start = 0;
 		unsigned long thresh = 96 * 1024 * 1024;
@@ -218,6 +218,11 @@
 		if (wbc->for_kupdate)
 			return 0;
 
+		if (current_is_pdflush()) {
+			thresh = 96 * 1024 * 1024;
+		} else {
+			thresh = 8 * 1024 * 1024;
+		}
 		num_dirty = count_range_bits(tree, &start, thresh, EXTENT_DIRTY);
 		if (num_dirty < thresh) {
 			return 0;
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index b6a4974..06e4377 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -42,6 +42,7 @@
 	struct extent_map_tree *tree;
 	get_extent_t *get_extent;
 };
+
 int __init extent_map_init(void)
 {
 	extent_map_cache = btrfs_cache_create("extent_map",
@@ -94,6 +95,7 @@
 	tree->map.rb_node = NULL;
 	tree->state.rb_node = NULL;
 	tree->ops = NULL;
+	tree->dirty_bytes = 0;
 	rwlock_init(&tree->lock);
 	spin_lock_init(&tree->lru_lock);
 	tree->mapping = mapping;
@@ -414,6 +416,8 @@
 		printk("end < start %Lu %Lu\n", end, start);
 		WARN_ON(1);
 	}
+	if (bits & EXTENT_DIRTY)
+		tree->dirty_bytes += end - start + 1;
 	state->state |= bits;
 	state->start = start;
 	state->end = end;
@@ -476,6 +480,12 @@
 			    int delete)
 {
 	int ret = state->state & bits;
+
+	if ((bits & EXTENT_DIRTY) && (state->state & EXTENT_DIRTY)) {
+		u64 range = state->end - state->start + 1;
+		WARN_ON(range > tree->dirty_bytes);
+		tree->dirty_bytes -= range;
+	}
 	state->state &= ~bits;
 	if (wake)
 		wake_up(&state->wq);
@@ -668,6 +678,17 @@
 }
 EXPORT_SYMBOL(wait_extent_bit);
 
+static void set_state_bits(struct extent_map_tree *tree,
+			   struct extent_state *state,
+			   int bits)
+{
+	if ((bits & EXTENT_DIRTY) && !(state->state & EXTENT_DIRTY)) {
+		u64 range = state->end - state->start + 1;
+		tree->dirty_bytes += range;
+	}
+	state->state |= bits;
+}
+
 /*
  * set some bits on a range in the tree.  This may require allocations
  * or sleeping, so the gfp mask is used to indicate what is allowed.
@@ -727,7 +748,7 @@
 			err = -EEXIST;
 			goto out;
 		}
-		state->state |= bits;
+		set_state_bits(tree, state, bits);
 		start = state->end + 1;
 		merge_state(tree, state);
 		goto search_again;
@@ -762,7 +783,7 @@
 		if (err)
 			goto out;
 		if (state->end <= end) {
-			state->state |= bits;
+			set_state_bits(tree, state, bits);
 			start = state->end + 1;
 			merge_state(tree, state);
 		} else {
@@ -808,7 +829,7 @@
 		err = split_state(tree, state, prealloc, end + 1);
 		BUG_ON(err == -EEXIST);
 
-		prealloc->state |= bits;
+		set_state_bits(tree, prealloc, bits);
 		merge_state(tree, prealloc);
 		prealloc = NULL;
 		goto out;
@@ -1116,6 +1137,11 @@
 	int found = 0;
 
 	write_lock_irq(&tree->lock);
+	if (bits == EXTENT_DIRTY) {
+		*start = 0;
+		total_bytes = tree->dirty_bytes;
+		goto out;
+	}
 	/*
 	 * this search will find all the extents that end after
 	 * our range starts.
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 13c562f..2ffc0c7 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -40,6 +40,7 @@
 	struct rb_root map;
 	struct rb_root state;
 	struct address_space *mapping;
+	u64 dirty_bytes;
 	rwlock_t lock;
 	struct extent_map_ops *ops;
 	spinlock_t lru_lock;