Commit 35b7e476 authored by Chris Mason's avatar Chris Mason Committed by David Woodhouse

Btrfs: fix page cache memory leak

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 090d1875
...@@ -22,9 +22,7 @@ int set_radix_bit(struct radix_tree_root *radix, unsigned long bit) ...@@ -22,9 +22,7 @@ int set_radix_bit(struct radix_tree_root *radix, unsigned long bit)
return -ENOMEM; return -ENOMEM;
memset(bits + 1, 0, BIT_ARRAY_BYTES - sizeof(unsigned long)); memset(bits + 1, 0, BIT_ARRAY_BYTES - sizeof(unsigned long));
bits[0] = slot; bits[0] = slot;
radix_tree_preload(GFP_NOFS);
ret = radix_tree_insert(radix, slot, bits); ret = radix_tree_insert(radix, slot, bits);
radix_tree_preload_end();
if (ret) if (ret)
return ret; return ret;
} }
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
struct btrfs_trans_handle; struct btrfs_trans_handle;
struct btrfs_transaction; 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; extern struct kmem_cache *btrfs_path_cachep;
#define BTRFS_MAGIC "_BtRfS_M" #define BTRFS_MAGIC "_BtRfS_M"
......
...@@ -4,11 +4,12 @@ ...@@ -4,11 +4,12 @@
#include "hash.h" #include "hash.h"
#include "transaction.h" #include "transaction.h"
struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle *trans, static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
struct btrfs_root *root, *trans,
struct btrfs_path *path, struct btrfs_root *root,
struct btrfs_key *cpu_key, struct btrfs_path *path,
u32 data_size) struct btrfs_key *cpu_key,
u32 data_size)
{ {
int ret; int ret;
char *ptr; char *ptr;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/radix-tree.h> #include <linux/radix-tree.h>
#include <linux/file.h> #include <linux/writeback.h>
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
#include "transaction.h" #include "transaction.h"
...@@ -694,7 +694,7 @@ static int free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root) ...@@ -694,7 +694,7 @@ static int free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
return 0; 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; int ret;
struct btrfs_root *gang[8]; struct btrfs_root *gang[8];
...@@ -781,3 +781,7 @@ void btrfs_block_release(struct btrfs_root *root, struct buffer_head *buf) ...@@ -781,3 +781,7 @@ void btrfs_block_release(struct btrfs_root *root, struct buffer_head *buf)
brelse(buf); brelse(buf);
} }
void btrfs_btree_balance_dirty(struct btrfs_root *root)
{
balance_dirty_pages_ratelimited(root->fs_info->btree_inode->i_mapping);
}
...@@ -55,4 +55,6 @@ int btrfs_insert_dev_radix(struct btrfs_root *root, ...@@ -55,4 +55,6 @@ int btrfs_insert_dev_radix(struct btrfs_root *root,
u64 num_blocks); u64 num_blocks);
int btrfs_map_bh_to_logical(struct btrfs_root *root, struct buffer_head *bh, int btrfs_map_bh_to_logical(struct btrfs_root *root, struct buffer_head *bh,
u64 logical); u64 logical);
int btrfs_releasepage(struct page *page, gfp_t flags);
void btrfs_btree_balance_dirty(struct btrfs_root *root);
#endif #endif
...@@ -322,18 +322,10 @@ static int update_block_group(struct btrfs_trans_handle *trans, ...@@ -322,18 +322,10 @@ static int update_block_group(struct btrfs_trans_handle *trans,
return 0; 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 int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
btrfs_root *root) btrfs_root *root)
{ {
unsigned long gang[8]; unsigned long gang[8];
struct inode *btree_inode = root->fs_info->btree_inode;
u64 first = 0; u64 first = 0;
int ret; int ret;
int i; int i;
...@@ -348,9 +340,6 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct ...@@ -348,9 +340,6 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
first = gang[0]; first = gang[0];
for (i = 0; i < ret; i++) { for (i = 0; i < ret; i++) {
clear_radix_bit(pinned_radix, gang[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; return 0;
...@@ -983,6 +972,7 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -983,6 +972,7 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
break; break;
if (wret < 0) if (wret < 0)
ret = wret; ret = wret;
btrfs_btree_balance_dirty(root);
} }
for (i = 0; i <= orig_level; i++) { for (i = 0; i <= orig_level; i++) {
if (path->nodes[i]) { if (path->nodes[i]) {
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/types.h> #include <linux/types.h>
#include "hash.h"
#define DELTA 0x9E3779B9 #define DELTA 0x9E3779B9
static void TEA_transform(__u32 buf[2], __u32 const in[]) static void TEA_transform(__u32 buf[2], __u32 const in[])
......
#include <linux/module.h> #include <linux/module.h>
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
#include "print-tree.h"
void btrfs_print_leaf(struct btrfs_root *root, struct btrfs_leaf *l) void btrfs_print_leaf(struct btrfs_root *root, struct btrfs_leaf *l)
{ {
......
...@@ -18,14 +18,14 @@ ...@@ -18,14 +18,14 @@
#include "btrfs_inode.h" #include "btrfs_inode.h"
#include "ioctl.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 *fsinfo = container_of(obj,
struct btrfs_fs_info, kobj); struct btrfs_fs_info, kobj);
kfree(fsinfo); kfree(fsinfo);
} }
struct kobj_type btrfs_fsinfo_ktype = { static struct kobj_type btrfs_fsinfo_ktype = {
.release = btrfs_fsinfo_release, .release = btrfs_fsinfo_release,
}; };
...@@ -148,7 +148,6 @@ static void fill_inode_item(struct btrfs_inode_item *item, ...@@ -148,7 +148,6 @@ static void fill_inode_item(struct btrfs_inode_item *item,
BTRFS_I(inode)->block_group->key.objectid); BTRFS_I(inode)->block_group->key.objectid);
} }
static int btrfs_update_inode(struct btrfs_trans_handle *trans, static int btrfs_update_inode(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct inode *inode) struct inode *inode)
...@@ -251,6 +250,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -251,6 +250,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
ret = btrfs_unlink_trans(trans, root, dir, dentry); ret = btrfs_unlink_trans(trans, root, dir, dentry);
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root);
return ret; return ret;
} }
...@@ -324,6 +324,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -324,6 +324,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
btrfs_free_path(path); btrfs_free_path(path);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
ret = btrfs_end_transaction(trans, root); ret = btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root);
if (ret && !err) if (ret && !err)
err = ret; err = ret;
return err; return err;
...@@ -449,6 +450,7 @@ static void btrfs_delete_inode(struct inode *inode) ...@@ -449,6 +450,7 @@ static void btrfs_delete_inode(struct inode *inode)
btrfs_free_inode(trans, root, inode); btrfs_free_inode(trans, root, inode);
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root);
return; return;
no_delete: no_delete:
clear_inode(inode); clear_inode(inode);
...@@ -481,7 +483,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry, ...@@ -481,7 +483,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
return ret; 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_key *location,
struct btrfs_root **sub_root) struct btrfs_root **sub_root)
{ {
...@@ -512,7 +514,7 @@ int fixup_tree_root_location(struct btrfs_root *root, ...@@ -512,7 +514,7 @@ int fixup_tree_root_location(struct btrfs_root *root,
return 0; 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; struct btrfs_iget_args *args = p;
inode->i_ino = args->ino; inode->i_ino = args->ino;
...@@ -520,15 +522,15 @@ int btrfs_init_locked_inode(struct inode *inode, void *p) ...@@ -520,15 +522,15 @@ int btrfs_init_locked_inode(struct inode *inode, void *p)
return 0; 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; struct btrfs_iget_args *args = opaque;
return (args->ino == inode->i_ino && return (args->ino == inode->i_ino &&
args->root == BTRFS_I(inode)->root); args->root == BTRFS_I(inode)->root);
} }
struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid, static struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
struct btrfs_root *root) struct btrfs_root *root)
{ {
struct inode *inode; struct inode *inode;
struct btrfs_iget_args args; struct btrfs_iget_args args;
...@@ -790,6 +792,7 @@ static void btrfs_dirty_inode(struct inode *inode) ...@@ -790,6 +792,7 @@ static void btrfs_dirty_inode(struct inode *inode)
btrfs_update_inode(trans, root, inode); btrfs_update_inode(trans, root, inode);
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root);
} }
static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
...@@ -913,6 +916,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, ...@@ -913,6 +916,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
inode_dec_link_count(inode); inode_dec_link_count(inode);
iput(inode); iput(inode);
} }
btrfs_btree_balance_dirty(root);
return err; return err;
} }
...@@ -1002,6 +1006,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -1002,6 +1006,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
if (drop_on_err) if (drop_on_err)
iput(inode); iput(inode);
btrfs_btree_balance_dirty(root);
return err; return err;
} }
...@@ -1099,7 +1104,6 @@ static int btrfs_get_block_lock(struct inode *inode, sector_t iblock, ...@@ -1099,7 +1104,6 @@ static int btrfs_get_block_lock(struct inode *inode, sector_t iblock,
found_type != BTRFS_EXTENT_DATA_KEY) { found_type != BTRFS_EXTENT_DATA_KEY) {
extent_end = 0; extent_end = 0;
extent_start = 0; extent_start = 0;
btrfs_release_path(root, path);
goto out; goto out;
} }
found_type = btrfs_file_extent_type(item); found_type = btrfs_file_extent_type(item);
...@@ -1135,7 +1139,6 @@ static int btrfs_get_block_lock(struct inode *inode, sector_t iblock, ...@@ -1135,7 +1139,6 @@ static int btrfs_get_block_lock(struct inode *inode, sector_t iblock,
btrfs_map_bh_to_logical(root, result, 0); btrfs_map_bh_to_logical(root, result, 0);
} }
out: out:
btrfs_release_path(root, path);
btrfs_free_path(path); btrfs_free_path(path);
return err; return err;
} }
...@@ -1231,13 +1234,13 @@ static int __btrfs_write_full_page(struct inode *inode, struct page *page, ...@@ -1231,13 +1234,13 @@ static int __btrfs_write_full_page(struct inode *inode, struct page *page,
} else if (!buffer_mapped(bh) && buffer_dirty(bh)) { } else if (!buffer_mapped(bh) && buffer_dirty(bh)) {
WARN_ON(bh->b_size != blocksize); WARN_ON(bh->b_size != blocksize);
err = btrfs_get_block(inode, block, bh, 0); err = btrfs_get_block(inode, block, bh, 0);
if (err) if (err) {
printk("writepage going to recovery err %d\n", err);
goto recover; goto recover;
}
if (buffer_new(bh)) { if (buffer_new(bh)) {
/* blockdev mappings never come here */ /* blockdev mappings never come here */
clear_buffer_new(bh); clear_buffer_new(bh);
unmap_underlying_metadata(bh->b_bdev,
bh->b_blocknr);
} }
} }
bh = bh->b_this_page; bh = bh->b_this_page;
...@@ -1303,11 +1306,6 @@ static int __btrfs_write_full_page(struct inode *inode, struct page *page, ...@@ -1303,11 +1306,6 @@ static int __btrfs_write_full_page(struct inode *inode, struct page *page,
if (uptodate) if (uptodate)
SetPageUptodate(page); SetPageUptodate(page);
end_page_writeback(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; return err;
...@@ -1409,10 +1407,11 @@ static void btrfs_truncate(struct inode *inode) ...@@ -1409,10 +1407,11 @@ static void btrfs_truncate(struct inode *inode)
btrfs_set_trans_block_group(trans, inode); btrfs_set_trans_block_group(trans, inode);
ret = btrfs_truncate_in_trans(trans, root, inode); ret = btrfs_truncate_in_trans(trans, root, inode);
BUG_ON(ret); BUG_ON(ret);
btrfs_update_inode(trans, root, inode);
ret = btrfs_end_transaction(trans, root); ret = btrfs_end_transaction(trans, root);
BUG_ON(ret); BUG_ON(ret);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
mark_inode_dirty(inode); btrfs_btree_balance_dirty(root);
} }
/* /*
...@@ -1777,10 +1776,15 @@ static int prepare_pages(struct btrfs_root *root, ...@@ -1777,10 +1776,15 @@ static int prepare_pages(struct btrfs_root *root,
err = -ENOMEM; err = -ENOMEM;
goto failed_release; goto failed_release;
} }
cancel_dirty_page(pages[i], PAGE_CACHE_SIZE);
wait_on_page_writeback(pages[i]);
offset = pos & (PAGE_CACHE_SIZE -1); offset = pos & (PAGE_CACHE_SIZE -1);
this_write = min(PAGE_CACHE_SIZE - offset, write_bytes); this_write = min(PAGE_CACHE_SIZE - offset, write_bytes);
create_empty_buffers(pages[i], root->fs_info->sb->s_blocksize, if (!page_has_buffers(pages[i])) {
(1 << BH_Uptodate)); create_empty_buffers(pages[i],
root->fs_info->sb->s_blocksize,
(1 << BH_Uptodate));
}
head = page_buffers(pages[i]); head = page_buffers(pages[i]);
bh = head; bh = head;
do { do {
...@@ -1820,7 +1824,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -1820,7 +1824,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
struct page *pages[8]; struct page *pages[8];
struct page *pinned[2] = { NULL, NULL }; struct page *pinned[2];
unsigned long first_index; unsigned long first_index;
unsigned long last_index; unsigned long last_index;
u64 start_pos; u64 start_pos;
...@@ -1829,6 +1833,8 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -1829,6 +1833,8 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
struct btrfs_key ins; struct btrfs_key ins;
pinned[0] = NULL;
pinned[1] = NULL;
if (file->f_flags & O_DIRECT) if (file->f_flags & O_DIRECT)
return -EINVAL; return -EINVAL;
pos = *ppos; pos = *ppos;
...@@ -1858,6 +1864,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -1858,6 +1864,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
if (!PageUptodate(pinned[0])) { if (!PageUptodate(pinned[0])) {
ret = mpage_readpage(pinned[0], btrfs_get_block); ret = mpage_readpage(pinned[0], btrfs_get_block);
BUG_ON(ret); BUG_ON(ret);
wait_on_page_locked(pinned[0]);
} else { } else {
unlock_page(pinned[0]); unlock_page(pinned[0]);
} }
...@@ -1869,6 +1876,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -1869,6 +1876,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
if (!PageUptodate(pinned[1])) { if (!PageUptodate(pinned[1])) {
ret = mpage_readpage(pinned[1], btrfs_get_block); ret = mpage_readpage(pinned[1], btrfs_get_block);
BUG_ON(ret); BUG_ON(ret);
wait_on_page_locked(pinned[1]);
} else { } else {
unlock_page(pinned[1]); unlock_page(pinned[1]);
} }
...@@ -1940,6 +1948,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -1940,6 +1948,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
num_written += write_bytes; num_written += write_bytes;
balance_dirty_pages_ratelimited(inode->i_mapping); balance_dirty_pages_ratelimited(inode->i_mapping);
btrfs_btree_balance_dirty(root);
cond_resched(); cond_resched();
} }
out_unlock: out_unlock:
...@@ -2165,6 +2174,7 @@ static int create_subvol(struct btrfs_root *root, char *name, int namelen) ...@@ -2165,6 +2174,7 @@ static int create_subvol(struct btrfs_root *root, char *name, int namelen)
iput(inode); iput(inode);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root);
return 0; return 0;
} }
...@@ -2220,6 +2230,7 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) ...@@ -2220,6 +2230,7 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
ret = btrfs_commit_transaction(trans, root); ret = btrfs_commit_transaction(trans, root);
BUG_ON(ret); BUG_ON(ret);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root);
return 0; return 0;
} }
...@@ -2295,6 +2306,7 @@ printk("insert failed %d\n", ret); ...@@ -2295,6 +2306,7 @@ printk("insert failed %d\n", ret);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
out_nolock: out_nolock:
btrfs_free_path(path); btrfs_free_path(path);
btrfs_btree_balance_dirty(root);
return ret; return ret;
} }
......
...@@ -199,8 +199,9 @@ struct dirty_root { ...@@ -199,8 +199,9 @@ struct dirty_root {
struct btrfs_root *root; struct btrfs_root *root;
}; };
int add_dirty_roots(struct btrfs_trans_handle *trans, static int add_dirty_roots(struct btrfs_trans_handle *trans,
struct radix_tree_root *radix, struct list_head *list) struct radix_tree_root *radix,
struct list_head *list)
{ {
struct dirty_root *dirty; struct dirty_root *dirty;
struct btrfs_root *gang[8]; struct btrfs_root *gang[8];
...@@ -246,7 +247,8 @@ int add_dirty_roots(struct btrfs_trans_handle *trans, ...@@ -246,7 +247,8 @@ int add_dirty_roots(struct btrfs_trans_handle *trans,
return 0; 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 dirty_root *dirty;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment