Btrfs: fix page cache memory leak

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/bit-radix.c b/fs/btrfs/bit-radix.c
index 9fc42e9..e9ace32 100644
--- a/fs/btrfs/bit-radix.c
+++ b/fs/btrfs/bit-radix.c
@@ -22,9 +22,7 @@
 			return -ENOMEM;
 		memset(bits + 1, 0, BIT_ARRAY_BYTES - sizeof(unsigned long));
 		bits[0] = slot;
-		radix_tree_preload(GFP_NOFS);
 		ret = radix_tree_insert(radix, slot, bits);
-		radix_tree_preload_end();
 		if (ret)
 			return ret;
 	}
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index e6bf991..b5855a5 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -8,6 +8,9 @@
 
 struct btrfs_trans_handle;
 struct btrfs_transaction;
+extern struct kmem_cache *btrfs_trans_handle_cachep;
+extern struct kmem_cache *btrfs_transaction_cachep;
+extern struct kmem_cache *btrfs_bit_radix_cachep;
 extern struct kmem_cache *btrfs_path_cachep;
 
 #define BTRFS_MAGIC "_BtRfS_M"
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 7a7e984..00a28d9 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -4,11 +4,12 @@
 #include "hash.h"
 #include "transaction.h"
 
-struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle *trans,
-					    struct btrfs_root *root,
-					    struct btrfs_path *path,
-					    struct btrfs_key *cpu_key,
-					    u32 data_size)
+static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
+						   *trans,
+						   struct btrfs_root *root,
+						   struct btrfs_path *path,
+						   struct btrfs_key *cpu_key,
+						   u32 data_size)
 {
 	int ret;
 	char *ptr;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 354524a..5828a10 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -5,7 +5,7 @@
 #include <linux/scatterlist.h>
 #include <linux/swap.h>
 #include <linux/radix-tree.h>
-#include <linux/file.h>
+#include <linux/writeback.h>
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
@@ -694,7 +694,7 @@
 	return 0;
 }
 
-int del_fs_roots(struct btrfs_fs_info *fs_info)
+static int del_fs_roots(struct btrfs_fs_info *fs_info)
 {
 	int ret;
 	struct btrfs_root *gang[8];
@@ -781,3 +781,7 @@
 	brelse(buf);
 }
 
+void btrfs_btree_balance_dirty(struct btrfs_root *root)
+{
+	balance_dirty_pages_ratelimited(root->fs_info->btree_inode->i_mapping);
+}
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 1ee7d2a..822ccb8 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -55,4 +55,6 @@
 			   u64 num_blocks);
 int btrfs_map_bh_to_logical(struct btrfs_root *root, struct buffer_head *bh,
 			     u64 logical);
+int btrfs_releasepage(struct page *page, gfp_t flags);
+void btrfs_btree_balance_dirty(struct btrfs_root *root);
 #endif
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 2837fdd..0e20d1c 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -322,18 +322,10 @@
 	return 0;
 }
 
-static int try_remove_page(struct address_space *mapping, unsigned long index)
-{
-	int ret;
-	ret = invalidate_mapping_pages(mapping, index, index);
-	return ret;
-}
-
 int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
 			       btrfs_root *root)
 {
 	unsigned long gang[8];
-	struct inode *btree_inode = root->fs_info->btree_inode;
 	u64 first = 0;
 	int ret;
 	int i;
@@ -348,9 +340,6 @@
 			first = gang[0];
 		for (i = 0; i < ret; i++) {
 			clear_radix_bit(pinned_radix, gang[i]);
-			try_remove_page(btree_inode->i_mapping,
-					gang[i] << (PAGE_CACHE_SHIFT -
-						    btree_inode->i_blkbits));
 		}
 	}
 	return 0;
@@ -983,6 +972,7 @@
 			break;
 		if (wret < 0)
 			ret = wret;
+		btrfs_btree_balance_dirty(root);
 	}
 	for (i = 0; i <= orig_level; i++) {
 		if (path->nodes[i]) {
diff --git a/fs/btrfs/hash.c b/fs/btrfs/hash.c
index 22519b8..32de1ea 100644
--- a/fs/btrfs/hash.c
+++ b/fs/btrfs/hash.c
@@ -11,6 +11,7 @@
  */
 
 #include <linux/types.h>
+#include "hash.h"
 #define DELTA 0x9E3779B9
 
 static void TEA_transform(__u32 buf[2], __u32 const in[])
diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
index 2f95fc6..2881341 100644
--- a/fs/btrfs/print-tree.c
+++ b/fs/btrfs/print-tree.c
@@ -1,6 +1,7 @@
 #include <linux/module.h>
 #include "ctree.h"
 #include "disk-io.h"
+#include "print-tree.h"
 
 void btrfs_print_leaf(struct btrfs_root *root, struct btrfs_leaf *l)
 {
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index a29a781b..130a1d3 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -18,14 +18,14 @@
 #include "btrfs_inode.h"
 #include "ioctl.h"
 
-void btrfs_fsinfo_release(struct kobject *obj)
+static void btrfs_fsinfo_release(struct kobject *obj)
 {
 	struct btrfs_fs_info *fsinfo = container_of(obj,
 					    struct btrfs_fs_info, kobj);
 	kfree(fsinfo);
 }
 
-struct kobj_type btrfs_fsinfo_ktype = {
+static struct kobj_type btrfs_fsinfo_ktype = {
 	.release = btrfs_fsinfo_release,
 };
 
@@ -148,7 +148,6 @@
 				    BTRFS_I(inode)->block_group->key.objectid);
 }
 
-
 static int btrfs_update_inode(struct btrfs_trans_handle *trans,
 			      struct btrfs_root *root,
 			      struct inode *inode)
@@ -251,6 +250,7 @@
 	ret = btrfs_unlink_trans(trans, root, dir, dentry);
 	btrfs_end_transaction(trans, root);
 	mutex_unlock(&root->fs_info->fs_mutex);
+	btrfs_btree_balance_dirty(root);
 	return ret;
 }
 
@@ -324,6 +324,7 @@
 	btrfs_free_path(path);
 	mutex_unlock(&root->fs_info->fs_mutex);
 	ret = btrfs_end_transaction(trans, root);
+	btrfs_btree_balance_dirty(root);
 	if (ret && !err)
 		err = ret;
 	return err;
@@ -449,6 +450,7 @@
 	btrfs_free_inode(trans, root, inode);
 	btrfs_end_transaction(trans, root);
 	mutex_unlock(&root->fs_info->fs_mutex);
+	btrfs_btree_balance_dirty(root);
 	return;
 no_delete:
 	clear_inode(inode);
@@ -481,7 +483,7 @@
 	return ret;
 }
 
-int fixup_tree_root_location(struct btrfs_root *root,
+static int fixup_tree_root_location(struct btrfs_root *root,
 			     struct btrfs_key *location,
 			     struct btrfs_root **sub_root)
 {
@@ -512,7 +514,7 @@
 	return 0;
 }
 
-int btrfs_init_locked_inode(struct inode *inode, void *p)
+static int btrfs_init_locked_inode(struct inode *inode, void *p)
 {
 	struct btrfs_iget_args *args = p;
 	inode->i_ino = args->ino;
@@ -520,15 +522,15 @@
 	return 0;
 }
 
-int btrfs_find_actor(struct inode *inode, void *opaque)
+static int btrfs_find_actor(struct inode *inode, void *opaque)
 {
 	struct btrfs_iget_args *args = opaque;
 	return (args->ino == inode->i_ino &&
 		args->root == BTRFS_I(inode)->root);
 }
 
-struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
-				struct btrfs_root *root)
+static struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
+				       struct btrfs_root *root)
 {
 	struct inode *inode;
 	struct btrfs_iget_args args;
@@ -790,6 +792,7 @@
 	btrfs_update_inode(trans, root, inode);
 	btrfs_end_transaction(trans, root);
 	mutex_unlock(&root->fs_info->fs_mutex);
+	btrfs_btree_balance_dirty(root);
 }
 
 static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
@@ -913,6 +916,7 @@
 		inode_dec_link_count(inode);
 		iput(inode);
 	}
+	btrfs_btree_balance_dirty(root);
 	return err;
 }
 
@@ -1002,6 +1006,7 @@
 	mutex_unlock(&root->fs_info->fs_mutex);
 	if (drop_on_err)
 		iput(inode);
+	btrfs_btree_balance_dirty(root);
 	return err;
 }
 
@@ -1099,7 +1104,6 @@
 	    found_type != BTRFS_EXTENT_DATA_KEY) {
 		extent_end = 0;
 		extent_start = 0;
-		btrfs_release_path(root, path);
 		goto out;
 	}
 	found_type = btrfs_file_extent_type(item);
@@ -1135,7 +1139,6 @@
 		btrfs_map_bh_to_logical(root, result, 0);
 	}
 out:
-	btrfs_release_path(root, path);
 	btrfs_free_path(path);
 	return err;
 }
@@ -1231,13 +1234,13 @@
 		} else if (!buffer_mapped(bh) && buffer_dirty(bh)) {
 			WARN_ON(bh->b_size != blocksize);
 			err = btrfs_get_block(inode, block, bh, 0);
-			if (err)
+			if (err) {
+printk("writepage going to recovery err %d\n", err);
 				goto recover;
+			}
 			if (buffer_new(bh)) {
 				/* blockdev mappings never come here */
 				clear_buffer_new(bh);
-				unmap_underlying_metadata(bh->b_bdev,
-							bh->b_blocknr);
 			}
 		}
 		bh = bh->b_this_page;
@@ -1303,11 +1306,6 @@
 		if (uptodate)
 			SetPageUptodate(page);
 		end_page_writeback(page);
-		/*
-		 * The page and buffer_heads can be released at any time from
-		 * here on.
-		 */
-		wbc->pages_skipped++;	/* We didn't write this page */
 	}
 	return err;
 
@@ -1409,10 +1407,11 @@
 	btrfs_set_trans_block_group(trans, inode);
 	ret = btrfs_truncate_in_trans(trans, root, inode);
 	BUG_ON(ret);
+	btrfs_update_inode(trans, root, inode);
 	ret = btrfs_end_transaction(trans, root);
 	BUG_ON(ret);
 	mutex_unlock(&root->fs_info->fs_mutex);
-	mark_inode_dirty(inode);
+	btrfs_btree_balance_dirty(root);
 }
 
 /*
@@ -1777,10 +1776,15 @@
 			err = -ENOMEM;
 			goto failed_release;
 		}
+		cancel_dirty_page(pages[i], PAGE_CACHE_SIZE);
+		wait_on_page_writeback(pages[i]);
 		offset = pos & (PAGE_CACHE_SIZE -1);
 		this_write = min(PAGE_CACHE_SIZE - offset, write_bytes);
-		create_empty_buffers(pages[i], root->fs_info->sb->s_blocksize,
-				     (1 << BH_Uptodate));
+		if (!page_has_buffers(pages[i])) {
+			create_empty_buffers(pages[i],
+					     root->fs_info->sb->s_blocksize,
+					     (1 << BH_Uptodate));
+		}
 		head = page_buffers(pages[i]);
 		bh = head;
 		do {
@@ -1820,7 +1824,7 @@
 	struct inode *inode = file->f_path.dentry->d_inode;
 	struct btrfs_root *root = BTRFS_I(inode)->root;
 	struct page *pages[8];
-	struct page *pinned[2] = { NULL, NULL };
+	struct page *pinned[2];
 	unsigned long first_index;
 	unsigned long last_index;
 	u64 start_pos;
@@ -1829,6 +1833,8 @@
 	struct btrfs_trans_handle *trans;
 	struct btrfs_key ins;
 
+	pinned[0] = NULL;
+	pinned[1] = NULL;
 	if (file->f_flags & O_DIRECT)
 		return -EINVAL;
 	pos = *ppos;
@@ -1858,6 +1864,7 @@
 		if (!PageUptodate(pinned[0])) {
 			ret = mpage_readpage(pinned[0], btrfs_get_block);
 			BUG_ON(ret);
+			wait_on_page_locked(pinned[0]);
 		} else {
 			unlock_page(pinned[0]);
 		}
@@ -1869,6 +1876,7 @@
 		if (!PageUptodate(pinned[1])) {
 			ret = mpage_readpage(pinned[1], btrfs_get_block);
 			BUG_ON(ret);
+			wait_on_page_locked(pinned[1]);
 		} else {
 			unlock_page(pinned[1]);
 		}
@@ -1940,6 +1948,7 @@
 		num_written += write_bytes;
 
 		balance_dirty_pages_ratelimited(inode->i_mapping);
+		btrfs_btree_balance_dirty(root);
 		cond_resched();
 	}
 out_unlock:
@@ -2165,6 +2174,7 @@
 	iput(inode);
 
 	mutex_unlock(&root->fs_info->fs_mutex);
+	btrfs_btree_balance_dirty(root);
 	return 0;
 }
 
@@ -2220,6 +2230,7 @@
 	ret = btrfs_commit_transaction(trans, root);
 	BUG_ON(ret);
 	mutex_unlock(&root->fs_info->fs_mutex);
+	btrfs_btree_balance_dirty(root);
 	return 0;
 }
 
@@ -2295,6 +2306,7 @@
 	mutex_unlock(&root->fs_info->fs_mutex);
 out_nolock:
 	btrfs_free_path(path);
+	btrfs_btree_balance_dirty(root);
 
 	return ret;
 }
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index bdbf514..8bbe910 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -199,8 +199,9 @@
 	struct btrfs_root *root;
 };
 
-int add_dirty_roots(struct btrfs_trans_handle *trans,
-		    struct radix_tree_root *radix, struct list_head *list)
+static int add_dirty_roots(struct btrfs_trans_handle *trans,
+			   struct radix_tree_root *radix,
+			   struct list_head *list)
 {
 	struct dirty_root *dirty;
 	struct btrfs_root *gang[8];
@@ -246,7 +247,8 @@
 	return 0;
 }
 
-int drop_dirty_roots(struct btrfs_root *tree_root, struct list_head *list)
+static int drop_dirty_roots(struct btrfs_root *tree_root,
+			    struct list_head *list)
 {
 	struct dirty_root *dirty;
 	struct btrfs_trans_handle *trans;