Commit be1a12a0 authored by Josef Bacik's avatar Josef Bacik

Btrfs: deal with the case that we run out of space in the cache

Currently we don't handle running out of space in the cache, so to fix this we
keep track of how far in the cache we are.  Then we only dirty the pages if we
successfully modify all of them, otherwise if we have an error or run out of
space we can just drop them and not worry about the vm writing them out.
Thanks,

Tested-by Johannes Hirte <johannes.hirte@fem.tu-ilmenau.de>
Signed-off-by: default avatarJosef Bacik <josef@redhat.com>
parent c9ddec74
...@@ -2576,6 +2576,11 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct inode *inode, ...@@ -2576,6 +2576,11 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct inode *inode,
int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
struct inode *inode, u64 start, u64 end); struct inode *inode, u64 start, u64 end);
int btrfs_release_file(struct inode *inode, struct file *file); int btrfs_release_file(struct inode *inode, struct file *file);
void btrfs_drop_pages(struct page **pages, size_t num_pages);
int btrfs_dirty_pages(struct btrfs_root *root, struct inode *inode,
struct page **pages, size_t num_pages,
loff_t pos, size_t write_bytes,
struct extent_state **cached);
/* tree-defrag.c */ /* tree-defrag.c */
int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
......
...@@ -104,7 +104,7 @@ static noinline int btrfs_copy_from_user(loff_t pos, int num_pages, ...@@ -104,7 +104,7 @@ static noinline int btrfs_copy_from_user(loff_t pos, int num_pages,
/* /*
* unlocks pages after btrfs_file_write is done with them * unlocks pages after btrfs_file_write is done with them
*/ */
static noinline void btrfs_drop_pages(struct page **pages, size_t num_pages) void btrfs_drop_pages(struct page **pages, size_t num_pages)
{ {
size_t i; size_t i;
for (i = 0; i < num_pages; i++) { for (i = 0; i < num_pages; i++) {
...@@ -127,16 +127,13 @@ static noinline void btrfs_drop_pages(struct page **pages, size_t num_pages) ...@@ -127,16 +127,13 @@ static noinline void btrfs_drop_pages(struct page **pages, size_t num_pages)
* this also makes the decision about creating an inline extent vs * this also makes the decision about creating an inline extent vs
* doing real data extents, marking pages dirty and delalloc as required. * doing real data extents, marking pages dirty and delalloc as required.
*/ */
static noinline int dirty_and_release_pages(struct btrfs_root *root, int btrfs_dirty_pages(struct btrfs_root *root, struct inode *inode,
struct file *file, struct page **pages, size_t num_pages,
struct page **pages, loff_t pos, size_t write_bytes,
size_t num_pages, struct extent_state **cached)
loff_t pos,
size_t write_bytes)
{ {
int err = 0; int err = 0;
int i; int i;
struct inode *inode = fdentry(file)->d_inode;
u64 num_bytes; u64 num_bytes;
u64 start_pos; u64 start_pos;
u64 end_of_last_block; u64 end_of_last_block;
...@@ -149,7 +146,7 @@ static noinline int dirty_and_release_pages(struct btrfs_root *root, ...@@ -149,7 +146,7 @@ static noinline int dirty_and_release_pages(struct btrfs_root *root,
end_of_last_block = start_pos + num_bytes - 1; end_of_last_block = start_pos + num_bytes - 1;
err = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block, err = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block,
NULL); cached);
if (err) if (err)
return err; return err;
...@@ -992,9 +989,9 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file, ...@@ -992,9 +989,9 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
} }
if (copied > 0) { if (copied > 0) {
ret = dirty_and_release_pages(root, file, pages, ret = btrfs_dirty_pages(root, inode, pages,
dirty_pages, pos, dirty_pages, pos, copied,
copied); NULL);
if (ret) { if (ret) {
btrfs_delalloc_release_space(inode, btrfs_delalloc_release_space(inode,
dirty_pages << PAGE_CACHE_SHIFT); dirty_pages << PAGE_CACHE_SHIFT);
......
...@@ -508,6 +508,7 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -508,6 +508,7 @@ int btrfs_write_out_cache(struct btrfs_root *root,
struct inode *inode; struct inode *inode;
struct rb_node *node; struct rb_node *node;
struct list_head *pos, *n; struct list_head *pos, *n;
struct page **pages;
struct page *page; struct page *page;
struct extent_state *cached_state = NULL; struct extent_state *cached_state = NULL;
struct btrfs_free_cluster *cluster = NULL; struct btrfs_free_cluster *cluster = NULL;
...@@ -517,13 +518,13 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -517,13 +518,13 @@ int btrfs_write_out_cache(struct btrfs_root *root,
u64 start, end, len; u64 start, end, len;
u64 bytes = 0; u64 bytes = 0;
u32 *crc, *checksums; u32 *crc, *checksums;
pgoff_t index = 0, last_index = 0;
unsigned long first_page_offset; unsigned long first_page_offset;
int num_checksums; int index = 0, num_pages = 0;
int entries = 0; int entries = 0;
int bitmaps = 0; int bitmaps = 0;
int ret = 0; int ret = 0;
bool next_page = false; bool next_page = false;
bool out_of_space = false;
root = root->fs_info->tree_root; root = root->fs_info->tree_root;
...@@ -551,24 +552,31 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -551,24 +552,31 @@ int btrfs_write_out_cache(struct btrfs_root *root,
return 0; return 0;
} }
last_index = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT; num_pages = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
PAGE_CACHE_SHIFT;
filemap_write_and_wait(inode->i_mapping); filemap_write_and_wait(inode->i_mapping);
btrfs_wait_ordered_range(inode, inode->i_size & btrfs_wait_ordered_range(inode, inode->i_size &
~(root->sectorsize - 1), (u64)-1); ~(root->sectorsize - 1), (u64)-1);
/* We need a checksum per page. */ /* We need a checksum per page. */
num_checksums = i_size_read(inode) / PAGE_CACHE_SIZE; crc = checksums = kzalloc(sizeof(u32) * num_pages, GFP_NOFS);
crc = checksums = kzalloc(sizeof(u32) * num_checksums, GFP_NOFS);
if (!crc) { if (!crc) {
iput(inode); iput(inode);
return 0; return 0;
} }
pages = kzalloc(sizeof(struct page *) * num_pages, GFP_NOFS);
if (!pages) {
kfree(crc);
iput(inode);
return 0;
}
/* Since the first page has all of our checksums and our generation we /* Since the first page has all of our checksums and our generation we
* need to calculate the offset into the page that we can start writing * need to calculate the offset into the page that we can start writing
* our entries. * our entries.
*/ */
first_page_offset = (sizeof(u32) * num_checksums) + sizeof(u64); first_page_offset = (sizeof(u32) * num_pages) + sizeof(u64);
/* Get the cluster for this block_group if it exists */ /* Get the cluster for this block_group if it exists */
if (!list_empty(&block_group->cluster_list)) if (!list_empty(&block_group->cluster_list))
...@@ -590,20 +598,18 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -590,20 +598,18 @@ int btrfs_write_out_cache(struct btrfs_root *root,
* after find_get_page at this point. Just putting this here so people * after find_get_page at this point. Just putting this here so people
* know and don't freak out. * know and don't freak out.
*/ */
while (index <= last_index) { while (index < num_pages) {
page = grab_cache_page(inode->i_mapping, index); page = grab_cache_page(inode->i_mapping, index);
if (!page) { if (!page) {
pgoff_t i = 0; int i;
while (i < index) { for (i = 0; i < num_pages; i++) {
page = find_get_page(inode->i_mapping, i); unlock_page(pages[i]);
unlock_page(page); page_cache_release(pages[i]);
page_cache_release(page);
page_cache_release(page);
i++;
} }
goto out_free; goto out_free;
} }
pages[index] = page;
index++; index++;
} }
...@@ -631,7 +637,12 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -631,7 +637,12 @@ int btrfs_write_out_cache(struct btrfs_root *root,
offset = start_offset; offset = start_offset;
} }
page = find_get_page(inode->i_mapping, index); if (index >= num_pages) {
out_of_space = true;
break;
}
page = pages[index];
addr = kmap(page); addr = kmap(page);
entry = addr + start_offset; entry = addr + start_offset;
...@@ -708,23 +719,6 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -708,23 +719,6 @@ int btrfs_write_out_cache(struct btrfs_root *root,
bytes += PAGE_CACHE_SIZE; bytes += PAGE_CACHE_SIZE;
ClearPageChecked(page);
set_page_extent_mapped(page);
SetPageUptodate(page);
set_page_dirty(page);
/*
* We need to release our reference we got for grab_cache_page,
* except for the first page which will hold our checksums, we
* do that below.
*/
if (index != 0) {
unlock_page(page);
page_cache_release(page);
}
page_cache_release(page);
index++; index++;
} while (node || next_page); } while (node || next_page);
...@@ -734,6 +728,10 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -734,6 +728,10 @@ int btrfs_write_out_cache(struct btrfs_root *root,
struct btrfs_free_space *entry = struct btrfs_free_space *entry =
list_entry(pos, struct btrfs_free_space, list); list_entry(pos, struct btrfs_free_space, list);
if (index >= num_pages) {
out_of_space = true;
break;
}
page = find_get_page(inode->i_mapping, index); page = find_get_page(inode->i_mapping, index);
addr = kmap(page); addr = kmap(page);
...@@ -745,64 +743,58 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -745,64 +743,58 @@ int btrfs_write_out_cache(struct btrfs_root *root,
crc++; crc++;
bytes += PAGE_CACHE_SIZE; bytes += PAGE_CACHE_SIZE;
ClearPageChecked(page);
set_page_extent_mapped(page);
SetPageUptodate(page);
set_page_dirty(page);
unlock_page(page);
page_cache_release(page);
page_cache_release(page);
list_del_init(&entry->list); list_del_init(&entry->list);
index++; index++;
} }
if (out_of_space) {
btrfs_drop_pages(pages, num_pages);
unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
i_size_read(inode) - 1, &cached_state,
GFP_NOFS);
ret = 0;
goto out_free;
}
/* Zero out the rest of the pages just to make sure */ /* Zero out the rest of the pages just to make sure */
while (index <= last_index) { while (index < num_pages) {
void *addr; void *addr;
page = find_get_page(inode->i_mapping, index); page = pages[index];
addr = kmap(page); addr = kmap(page);
memset(addr, 0, PAGE_CACHE_SIZE); memset(addr, 0, PAGE_CACHE_SIZE);
kunmap(page); kunmap(page);
ClearPageChecked(page);
set_page_extent_mapped(page);
SetPageUptodate(page);
set_page_dirty(page);
unlock_page(page);
page_cache_release(page);
page_cache_release(page);
bytes += PAGE_CACHE_SIZE; bytes += PAGE_CACHE_SIZE;
index++; index++;
} }
btrfs_set_extent_delalloc(inode, 0, bytes - 1, &cached_state);
/* Write the checksums and trans id to the first page */ /* Write the checksums and trans id to the first page */
{ {
void *addr; void *addr;
u64 *gen; u64 *gen;
page = find_get_page(inode->i_mapping, 0); page = pages[0];
addr = kmap(page); addr = kmap(page);
memcpy(addr, checksums, sizeof(u32) * num_checksums); memcpy(addr, checksums, sizeof(u32) * num_pages);
gen = addr + (sizeof(u32) * num_checksums); gen = addr + (sizeof(u32) * num_pages);
*gen = trans->transid; *gen = trans->transid;
kunmap(page); kunmap(page);
ClearPageChecked(page);
set_page_extent_mapped(page);
SetPageUptodate(page);
set_page_dirty(page);
unlock_page(page);
page_cache_release(page);
page_cache_release(page);
} }
BTRFS_I(inode)->generation = trans->transid;
ret = btrfs_dirty_pages(root, inode, pages, num_pages, 0,
bytes, &cached_state);
btrfs_drop_pages(pages, num_pages);
unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0, unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
i_size_read(inode) - 1, &cached_state, GFP_NOFS); i_size_read(inode) - 1, &cached_state, GFP_NOFS);
if (ret) {
ret = 0;
goto out_free;
}
BTRFS_I(inode)->generation = trans->transid;
filemap_write_and_wait(inode->i_mapping); filemap_write_and_wait(inode->i_mapping);
key.objectid = BTRFS_FREE_SPACE_OBJECTID; key.objectid = BTRFS_FREE_SPACE_OBJECTID;
...@@ -853,6 +845,7 @@ int btrfs_write_out_cache(struct btrfs_root *root, ...@@ -853,6 +845,7 @@ int btrfs_write_out_cache(struct btrfs_root *root,
BTRFS_I(inode)->generation = 0; BTRFS_I(inode)->generation = 0;
} }
kfree(checksums); kfree(checksums);
kfree(pages);
btrfs_update_inode(trans, root, inode); btrfs_update_inode(trans, root, inode);
iput(inode); iput(inode);
return ret; return ret;
......
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