|
@@ -50,6 +50,7 @@
|
|
|
#include "tree-log.h"
|
|
#include "tree-log.h"
|
|
|
#include "compression.h"
|
|
#include "compression.h"
|
|
|
#include "locking.h"
|
|
#include "locking.h"
|
|
|
|
|
+#include "free-space-cache.h"
|
|
|
|
|
|
|
|
struct btrfs_iget_args {
|
|
struct btrfs_iget_args {
|
|
|
u64 ino;
|
|
u64 ino;
|
|
@@ -70,6 +71,7 @@ static struct kmem_cache *btrfs_inode_cachep;
|
|
|
struct kmem_cache *btrfs_trans_handle_cachep;
|
|
struct kmem_cache *btrfs_trans_handle_cachep;
|
|
|
struct kmem_cache *btrfs_transaction_cachep;
|
|
struct kmem_cache *btrfs_transaction_cachep;
|
|
|
struct kmem_cache *btrfs_path_cachep;
|
|
struct kmem_cache *btrfs_path_cachep;
|
|
|
|
|
+struct kmem_cache *btrfs_free_space_cachep;
|
|
|
|
|
|
|
|
#define S_SHIFT 12
|
|
#define S_SHIFT 12
|
|
|
static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
|
|
static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
|
|
@@ -82,7 +84,8 @@ static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
|
|
|
[S_IFLNK >> S_SHIFT] = BTRFS_FT_SYMLINK,
|
|
[S_IFLNK >> S_SHIFT] = BTRFS_FT_SYMLINK,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-static void btrfs_truncate(struct inode *inode);
|
|
|
|
|
|
|
+static int btrfs_setsize(struct inode *inode, loff_t newsize);
|
|
|
|
|
+static int btrfs_truncate(struct inode *inode);
|
|
|
static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end);
|
|
static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end);
|
|
|
static noinline int cow_file_range(struct inode *inode,
|
|
static noinline int cow_file_range(struct inode *inode,
|
|
|
struct page *locked_page,
|
|
struct page *locked_page,
|
|
@@ -288,6 +291,7 @@ static noinline int add_async_extent(struct async_cow *cow,
|
|
|
struct async_extent *async_extent;
|
|
struct async_extent *async_extent;
|
|
|
|
|
|
|
|
async_extent = kmalloc(sizeof(*async_extent), GFP_NOFS);
|
|
async_extent = kmalloc(sizeof(*async_extent), GFP_NOFS);
|
|
|
|
|
+ BUG_ON(!async_extent);
|
|
|
async_extent->start = start;
|
|
async_extent->start = start;
|
|
|
async_extent->ram_size = ram_size;
|
|
async_extent->ram_size = ram_size;
|
|
|
async_extent->compressed_size = compressed_size;
|
|
async_extent->compressed_size = compressed_size;
|
|
@@ -382,9 +386,11 @@ static noinline int compress_file_range(struct inode *inode,
|
|
|
*/
|
|
*/
|
|
|
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
|
|
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
|
|
|
(btrfs_test_opt(root, COMPRESS) ||
|
|
(btrfs_test_opt(root, COMPRESS) ||
|
|
|
- (BTRFS_I(inode)->force_compress))) {
|
|
|
|
|
|
|
+ (BTRFS_I(inode)->force_compress) ||
|
|
|
|
|
+ (BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS))) {
|
|
|
WARN_ON(pages);
|
|
WARN_ON(pages);
|
|
|
pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
|
|
pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
|
|
|
|
|
+ BUG_ON(!pages);
|
|
|
|
|
|
|
|
if (BTRFS_I(inode)->force_compress)
|
|
if (BTRFS_I(inode)->force_compress)
|
|
|
compress_type = BTRFS_I(inode)->force_compress;
|
|
compress_type = BTRFS_I(inode)->force_compress;
|
|
@@ -1254,7 +1260,8 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
|
|
|
ret = run_delalloc_nocow(inode, locked_page, start, end,
|
|
ret = run_delalloc_nocow(inode, locked_page, start, end,
|
|
|
page_started, 0, nr_written);
|
|
page_started, 0, nr_written);
|
|
|
else if (!btrfs_test_opt(root, COMPRESS) &&
|
|
else if (!btrfs_test_opt(root, COMPRESS) &&
|
|
|
- !(BTRFS_I(inode)->force_compress))
|
|
|
|
|
|
|
+ !(BTRFS_I(inode)->force_compress) &&
|
|
|
|
|
+ !(BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS))
|
|
|
ret = cow_file_range(inode, locked_page, start, end,
|
|
ret = cow_file_range(inode, locked_page, start, end,
|
|
|
page_started, nr_written, 1);
|
|
page_started, nr_written, 1);
|
|
|
else
|
|
else
|
|
@@ -1461,8 +1468,11 @@ static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
|
|
|
if (bio_flags & EXTENT_BIO_COMPRESSED) {
|
|
if (bio_flags & EXTENT_BIO_COMPRESSED) {
|
|
|
return btrfs_submit_compressed_read(inode, bio,
|
|
return btrfs_submit_compressed_read(inode, bio,
|
|
|
mirror_num, bio_flags);
|
|
mirror_num, bio_flags);
|
|
|
- } else if (!skip_sum)
|
|
|
|
|
- btrfs_lookup_bio_sums(root, inode, bio, NULL);
|
|
|
|
|
|
|
+ } else if (!skip_sum) {
|
|
|
|
|
+ ret = btrfs_lookup_bio_sums(root, inode, bio, NULL);
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ }
|
|
|
goto mapit;
|
|
goto mapit;
|
|
|
} else if (!skip_sum) {
|
|
} else if (!skip_sum) {
|
|
|
/* csum items have already been cloned */
|
|
/* csum items have already been cloned */
|
|
@@ -1785,6 +1795,8 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
|
|
|
static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
|
|
static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
|
|
|
struct extent_state *state, int uptodate)
|
|
struct extent_state *state, int uptodate)
|
|
|
{
|
|
{
|
|
|
|
|
+ trace_btrfs_writepage_end_io_hook(page, start, end, uptodate);
|
|
|
|
|
+
|
|
|
ClearPagePrivate2(page);
|
|
ClearPagePrivate2(page);
|
|
|
return btrfs_finish_ordered_io(page->mapping->host, start, end);
|
|
return btrfs_finish_ordered_io(page->mapping->host, start, end);
|
|
|
}
|
|
}
|
|
@@ -1895,10 +1907,10 @@ static int btrfs_io_failed_hook(struct bio *failed_bio,
|
|
|
else
|
|
else
|
|
|
rw = READ;
|
|
rw = READ;
|
|
|
|
|
|
|
|
- BTRFS_I(inode)->io_tree.ops->submit_bio_hook(inode, rw, bio,
|
|
|
|
|
|
|
+ ret = BTRFS_I(inode)->io_tree.ops->submit_bio_hook(inode, rw, bio,
|
|
|
failrec->last_mirror,
|
|
failrec->last_mirror,
|
|
|
failrec->bio_flags, 0);
|
|
failrec->bio_flags, 0);
|
|
|
- return 0;
|
|
|
|
|
|
|
+ return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -2282,7 +2294,7 @@ int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode)
|
|
|
* this cleans up any orphans that may be left on the list from the last use
|
|
* this cleans up any orphans that may be left on the list from the last use
|
|
|
* of this root.
|
|
* of this root.
|
|
|
*/
|
|
*/
|
|
|
-void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
|
|
|
|
+int btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
{
|
|
{
|
|
|
struct btrfs_path *path;
|
|
struct btrfs_path *path;
|
|
|
struct extent_buffer *leaf;
|
|
struct extent_buffer *leaf;
|
|
@@ -2292,10 +2304,13 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
int ret = 0, nr_unlink = 0, nr_truncate = 0;
|
|
int ret = 0, nr_unlink = 0, nr_truncate = 0;
|
|
|
|
|
|
|
|
if (cmpxchg(&root->orphan_cleanup_state, 0, ORPHAN_CLEANUP_STARTED))
|
|
if (cmpxchg(&root->orphan_cleanup_state, 0, ORPHAN_CLEANUP_STARTED))
|
|
|
- return;
|
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
path = btrfs_alloc_path();
|
|
|
- BUG_ON(!path);
|
|
|
|
|
|
|
+ if (!path) {
|
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
|
+ goto out;
|
|
|
|
|
+ }
|
|
|
path->reada = -1;
|
|
path->reada = -1;
|
|
|
|
|
|
|
|
key.objectid = BTRFS_ORPHAN_OBJECTID;
|
|
key.objectid = BTRFS_ORPHAN_OBJECTID;
|
|
@@ -2304,11 +2319,8 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
|
|
|
|
|
while (1) {
|
|
while (1) {
|
|
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
|
- if (ret < 0) {
|
|
|
|
|
- printk(KERN_ERR "Error searching slot for orphan: %d"
|
|
|
|
|
- "\n", ret);
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
|
+ goto out;
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
|
* if ret == 0 means we found what we were searching for, which
|
|
* if ret == 0 means we found what we were searching for, which
|
|
@@ -2316,6 +2328,7 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
* find the key and see if we have stuff that matches
|
|
* find the key and see if we have stuff that matches
|
|
|
*/
|
|
*/
|
|
|
if (ret > 0) {
|
|
if (ret > 0) {
|
|
|
|
|
+ ret = 0;
|
|
|
if (path->slots[0] == 0)
|
|
if (path->slots[0] == 0)
|
|
|
break;
|
|
break;
|
|
|
path->slots[0]--;
|
|
path->slots[0]--;
|
|
@@ -2343,7 +2356,10 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
found_key.type = BTRFS_INODE_ITEM_KEY;
|
|
found_key.type = BTRFS_INODE_ITEM_KEY;
|
|
|
found_key.offset = 0;
|
|
found_key.offset = 0;
|
|
|
inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL);
|
|
inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL);
|
|
|
- BUG_ON(IS_ERR(inode));
|
|
|
|
|
|
|
+ if (IS_ERR(inode)) {
|
|
|
|
|
+ ret = PTR_ERR(inode);
|
|
|
|
|
+ goto out;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
|
* add this inode to the orphan list so btrfs_orphan_del does
|
|
* add this inode to the orphan list so btrfs_orphan_del does
|
|
@@ -2361,7 +2377,10 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
*/
|
|
*/
|
|
|
if (is_bad_inode(inode)) {
|
|
if (is_bad_inode(inode)) {
|
|
|
trans = btrfs_start_transaction(root, 0);
|
|
trans = btrfs_start_transaction(root, 0);
|
|
|
- BUG_ON(IS_ERR(trans));
|
|
|
|
|
|
|
+ if (IS_ERR(trans)) {
|
|
|
|
|
+ ret = PTR_ERR(trans);
|
|
|
|
|
+ goto out;
|
|
|
|
|
+ }
|
|
|
btrfs_orphan_del(trans, inode);
|
|
btrfs_orphan_del(trans, inode);
|
|
|
btrfs_end_transaction(trans, root);
|
|
btrfs_end_transaction(trans, root);
|
|
|
iput(inode);
|
|
iput(inode);
|
|
@@ -2370,17 +2389,22 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
|
|
|
|
|
/* if we have links, this was a truncate, lets do that */
|
|
/* if we have links, this was a truncate, lets do that */
|
|
|
if (inode->i_nlink) {
|
|
if (inode->i_nlink) {
|
|
|
|
|
+ if (!S_ISREG(inode->i_mode)) {
|
|
|
|
|
+ WARN_ON(1);
|
|
|
|
|
+ iput(inode);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
nr_truncate++;
|
|
nr_truncate++;
|
|
|
- btrfs_truncate(inode);
|
|
|
|
|
|
|
+ ret = btrfs_truncate(inode);
|
|
|
} else {
|
|
} else {
|
|
|
nr_unlink++;
|
|
nr_unlink++;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* this will do delete_inode and everything for us */
|
|
/* this will do delete_inode and everything for us */
|
|
|
iput(inode);
|
|
iput(inode);
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ goto out;
|
|
|
}
|
|
}
|
|
|
- btrfs_free_path(path);
|
|
|
|
|
-
|
|
|
|
|
root->orphan_cleanup_state = ORPHAN_CLEANUP_DONE;
|
|
root->orphan_cleanup_state = ORPHAN_CLEANUP_DONE;
|
|
|
|
|
|
|
|
if (root->orphan_block_rsv)
|
|
if (root->orphan_block_rsv)
|
|
@@ -2389,14 +2413,20 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
|
|
|
|
|
if (root->orphan_block_rsv || root->orphan_item_inserted) {
|
|
if (root->orphan_block_rsv || root->orphan_item_inserted) {
|
|
|
trans = btrfs_join_transaction(root, 1);
|
|
trans = btrfs_join_transaction(root, 1);
|
|
|
- BUG_ON(IS_ERR(trans));
|
|
|
|
|
- btrfs_end_transaction(trans, root);
|
|
|
|
|
|
|
+ if (!IS_ERR(trans))
|
|
|
|
|
+ btrfs_end_transaction(trans, root);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (nr_unlink)
|
|
if (nr_unlink)
|
|
|
printk(KERN_INFO "btrfs: unlinked %d orphans\n", nr_unlink);
|
|
printk(KERN_INFO "btrfs: unlinked %d orphans\n", nr_unlink);
|
|
|
if (nr_truncate)
|
|
if (nr_truncate)
|
|
|
printk(KERN_INFO "btrfs: truncated %d orphans\n", nr_truncate);
|
|
printk(KERN_INFO "btrfs: truncated %d orphans\n", nr_truncate);
|
|
|
|
|
+
|
|
|
|
|
+out:
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ printk(KERN_CRIT "btrfs: could not do orphan cleanup %d\n", ret);
|
|
|
|
|
+ btrfs_free_path(path);
|
|
|
|
|
+ return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -2507,6 +2537,8 @@ static void btrfs_read_locked_inode(struct inode *inode)
|
|
|
BTRFS_I(inode)->flags = btrfs_inode_flags(leaf, inode_item);
|
|
BTRFS_I(inode)->flags = btrfs_inode_flags(leaf, inode_item);
|
|
|
|
|
|
|
|
alloc_group_block = btrfs_inode_block_group(leaf, inode_item);
|
|
alloc_group_block = btrfs_inode_block_group(leaf, inode_item);
|
|
|
|
|
+ if (location.objectid == BTRFS_FREE_SPACE_OBJECTID)
|
|
|
|
|
+ inode->i_mapping->flags &= ~__GFP_FS;
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
|
* try to precache a NULL acl entry for files that don't have
|
|
* try to precache a NULL acl entry for files that don't have
|
|
@@ -2635,10 +2667,10 @@ noinline int btrfs_update_inode(struct btrfs_trans_handle *trans,
|
|
|
* recovery code. It remove a link in a directory with a given name, and
|
|
* recovery code. It remove a link in a directory with a given name, and
|
|
|
* also drops the back refs in the inode to the directory
|
|
* also drops the back refs in the inode to the directory
|
|
|
*/
|
|
*/
|
|
|
-int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
|
|
|
|
- struct btrfs_root *root,
|
|
|
|
|
- struct inode *dir, struct inode *inode,
|
|
|
|
|
- const char *name, int name_len)
|
|
|
|
|
|
|
+static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
|
|
|
|
+ struct btrfs_root *root,
|
|
|
|
|
+ struct inode *dir, struct inode *inode,
|
|
|
|
|
+ const char *name, int name_len)
|
|
|
{
|
|
{
|
|
|
struct btrfs_path *path;
|
|
struct btrfs_path *path;
|
|
|
int ret = 0;
|
|
int ret = 0;
|
|
@@ -2710,12 +2742,25 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
|
|
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
|
|
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
|
|
|
inode->i_ctime = dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
|
inode->i_ctime = dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
|
|
btrfs_update_inode(trans, root, dir);
|
|
btrfs_update_inode(trans, root, dir);
|
|
|
- btrfs_drop_nlink(inode);
|
|
|
|
|
- ret = btrfs_update_inode(trans, root, inode);
|
|
|
|
|
out:
|
|
out:
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
|
|
|
|
+ struct btrfs_root *root,
|
|
|
|
|
+ struct inode *dir, struct inode *inode,
|
|
|
|
|
+ const char *name, int name_len)
|
|
|
|
|
+{
|
|
|
|
|
+ int ret;
|
|
|
|
|
+ ret = __btrfs_unlink_inode(trans, root, dir, inode, name, name_len);
|
|
|
|
|
+ if (!ret) {
|
|
|
|
|
+ btrfs_drop_nlink(inode);
|
|
|
|
|
+ ret = btrfs_update_inode(trans, root, inode);
|
|
|
|
|
+ }
|
|
|
|
|
+ return ret;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
/* helper to check if there is any shared block in the path */
|
|
/* helper to check if there is any shared block in the path */
|
|
|
static int check_path_shared(struct btrfs_root *root,
|
|
static int check_path_shared(struct btrfs_root *root,
|
|
|
struct btrfs_path *path)
|
|
struct btrfs_path *path)
|
|
@@ -3537,7 +3582,13 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from)
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-int btrfs_cont_expand(struct inode *inode, loff_t size)
|
|
|
|
|
|
|
+/*
|
|
|
|
|
+ * This function puts in dummy file extents for the area we're creating a hole
|
|
|
|
|
+ * for. So if we are truncating this file to a larger size we need to insert
|
|
|
|
|
+ * these file extents so that btrfs_get_extent will return a EXTENT_MAP_HOLE for
|
|
|
|
|
+ * the range between oldsize and size
|
|
|
|
|
+ */
|
|
|
|
|
+int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
|
|
|
{
|
|
{
|
|
|
struct btrfs_trans_handle *trans;
|
|
struct btrfs_trans_handle *trans;
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
@@ -3545,7 +3596,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
|
|
|
struct extent_map *em = NULL;
|
|
struct extent_map *em = NULL;
|
|
|
struct extent_state *cached_state = NULL;
|
|
struct extent_state *cached_state = NULL;
|
|
|
u64 mask = root->sectorsize - 1;
|
|
u64 mask = root->sectorsize - 1;
|
|
|
- u64 hole_start = (inode->i_size + mask) & ~mask;
|
|
|
|
|
|
|
+ u64 hole_start = (oldsize + mask) & ~mask;
|
|
|
u64 block_end = (size + mask) & ~mask;
|
|
u64 block_end = (size + mask) & ~mask;
|
|
|
u64 last_byte;
|
|
u64 last_byte;
|
|
|
u64 cur_offset;
|
|
u64 cur_offset;
|
|
@@ -3590,13 +3641,15 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
|
|
|
err = btrfs_drop_extents(trans, inode, cur_offset,
|
|
err = btrfs_drop_extents(trans, inode, cur_offset,
|
|
|
cur_offset + hole_size,
|
|
cur_offset + hole_size,
|
|
|
&hint_byte, 1);
|
|
&hint_byte, 1);
|
|
|
- BUG_ON(err);
|
|
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
err = btrfs_insert_file_extent(trans, root,
|
|
err = btrfs_insert_file_extent(trans, root,
|
|
|
inode->i_ino, cur_offset, 0,
|
|
inode->i_ino, cur_offset, 0,
|
|
|
0, hole_size, 0, hole_size,
|
|
0, hole_size, 0, hole_size,
|
|
|
0, 0, 0);
|
|
0, 0, 0);
|
|
|
- BUG_ON(err);
|
|
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
btrfs_drop_extent_cache(inode, hole_start,
|
|
btrfs_drop_extent_cache(inode, hole_start,
|
|
|
last_byte - 1, 0);
|
|
last_byte - 1, 0);
|
|
@@ -3616,81 +3669,41 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
|
|
|
return err;
|
|
return err;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static int btrfs_setattr_size(struct inode *inode, struct iattr *attr)
|
|
|
|
|
|
|
+static int btrfs_setsize(struct inode *inode, loff_t newsize)
|
|
|
{
|
|
{
|
|
|
- struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
|
- struct btrfs_trans_handle *trans;
|
|
|
|
|
- unsigned long nr;
|
|
|
|
|
|
|
+ loff_t oldsize = i_size_read(inode);
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
- if (attr->ia_size == inode->i_size)
|
|
|
|
|
|
|
+ if (newsize == oldsize)
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
- if (attr->ia_size > inode->i_size) {
|
|
|
|
|
- unsigned long limit;
|
|
|
|
|
- limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
|
|
|
|
|
- if (attr->ia_size > inode->i_sb->s_maxbytes)
|
|
|
|
|
- return -EFBIG;
|
|
|
|
|
- if (limit != RLIM_INFINITY && attr->ia_size > limit) {
|
|
|
|
|
- send_sig(SIGXFSZ, current, 0);
|
|
|
|
|
- return -EFBIG;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- trans = btrfs_start_transaction(root, 5);
|
|
|
|
|
- if (IS_ERR(trans))
|
|
|
|
|
- return PTR_ERR(trans);
|
|
|
|
|
-
|
|
|
|
|
- btrfs_set_trans_block_group(trans, inode);
|
|
|
|
|
-
|
|
|
|
|
- ret = btrfs_orphan_add(trans, inode);
|
|
|
|
|
- BUG_ON(ret);
|
|
|
|
|
-
|
|
|
|
|
- nr = trans->blocks_used;
|
|
|
|
|
- btrfs_end_transaction(trans, root);
|
|
|
|
|
- btrfs_btree_balance_dirty(root, nr);
|
|
|
|
|
-
|
|
|
|
|
- if (attr->ia_size > inode->i_size) {
|
|
|
|
|
- ret = btrfs_cont_expand(inode, attr->ia_size);
|
|
|
|
|
|
|
+ if (newsize > oldsize) {
|
|
|
|
|
+ i_size_write(inode, newsize);
|
|
|
|
|
+ btrfs_ordered_update_i_size(inode, i_size_read(inode), NULL);
|
|
|
|
|
+ truncate_pagecache(inode, oldsize, newsize);
|
|
|
|
|
+ ret = btrfs_cont_expand(inode, oldsize, newsize);
|
|
|
if (ret) {
|
|
if (ret) {
|
|
|
- btrfs_truncate(inode);
|
|
|
|
|
|
|
+ btrfs_setsize(inode, oldsize);
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- i_size_write(inode, attr->ia_size);
|
|
|
|
|
- btrfs_ordered_update_i_size(inode, inode->i_size, NULL);
|
|
|
|
|
|
|
+ mark_inode_dirty(inode);
|
|
|
|
|
+ } else {
|
|
|
|
|
|
|
|
- trans = btrfs_start_transaction(root, 0);
|
|
|
|
|
- BUG_ON(IS_ERR(trans));
|
|
|
|
|
- btrfs_set_trans_block_group(trans, inode);
|
|
|
|
|
- trans->block_rsv = root->orphan_block_rsv;
|
|
|
|
|
- BUG_ON(!trans->block_rsv);
|
|
|
|
|
|
|
+ /*
|
|
|
|
|
+ * We're truncating a file that used to have good data down to
|
|
|
|
|
+ * zero. Make sure it gets into the ordered flush list so that
|
|
|
|
|
+ * any new writes get down to disk quickly.
|
|
|
|
|
+ */
|
|
|
|
|
+ if (newsize == 0)
|
|
|
|
|
+ BTRFS_I(inode)->ordered_data_close = 1;
|
|
|
|
|
|
|
|
- ret = btrfs_update_inode(trans, root, inode);
|
|
|
|
|
- BUG_ON(ret);
|
|
|
|
|
- if (inode->i_nlink > 0) {
|
|
|
|
|
- ret = btrfs_orphan_del(trans, inode);
|
|
|
|
|
- BUG_ON(ret);
|
|
|
|
|
- }
|
|
|
|
|
- nr = trans->blocks_used;
|
|
|
|
|
- btrfs_end_transaction(trans, root);
|
|
|
|
|
- btrfs_btree_balance_dirty(root, nr);
|
|
|
|
|
- return 0;
|
|
|
|
|
|
|
+ /* we don't support swapfiles, so vmtruncate shouldn't fail */
|
|
|
|
|
+ truncate_setsize(inode, newsize);
|
|
|
|
|
+ ret = btrfs_truncate(inode);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /*
|
|
|
|
|
- * We're truncating a file that used to have good data down to
|
|
|
|
|
- * zero. Make sure it gets into the ordered flush list so that
|
|
|
|
|
- * any new writes get down to disk quickly.
|
|
|
|
|
- */
|
|
|
|
|
- if (attr->ia_size == 0)
|
|
|
|
|
- BTRFS_I(inode)->ordered_data_close = 1;
|
|
|
|
|
-
|
|
|
|
|
- /* we don't support swapfiles, so vmtruncate shouldn't fail */
|
|
|
|
|
- ret = vmtruncate(inode, attr->ia_size);
|
|
|
|
|
- BUG_ON(ret);
|
|
|
|
|
-
|
|
|
|
|
- return 0;
|
|
|
|
|
|
|
+ return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
@@ -3707,7 +3720,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
return err;
|
|
return err;
|
|
|
|
|
|
|
|
if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
|
|
if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
|
|
|
- err = btrfs_setattr_size(inode, attr);
|
|
|
|
|
|
|
+ err = btrfs_setsize(inode, attr->ia_size);
|
|
|
if (err)
|
|
if (err)
|
|
|
return err;
|
|
return err;
|
|
|
}
|
|
}
|
|
@@ -3730,6 +3743,8 @@ void btrfs_evict_inode(struct inode *inode)
|
|
|
unsigned long nr;
|
|
unsigned long nr;
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
+ trace_btrfs_inode_evict(inode);
|
|
|
|
|
+
|
|
|
truncate_inode_pages(&inode->i_data, 0);
|
|
truncate_inode_pages(&inode->i_data, 0);
|
|
|
if (inode->i_nlink && (btrfs_root_refs(&root->root_item) != 0 ||
|
|
if (inode->i_nlink && (btrfs_root_refs(&root->root_item) != 0 ||
|
|
|
root == root->fs_info->tree_root))
|
|
root == root->fs_info->tree_root))
|
|
@@ -4072,7 +4087,6 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
|
|
|
BTRFS_I(inode)->root = root;
|
|
BTRFS_I(inode)->root = root;
|
|
|
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
|
|
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
|
|
|
btrfs_read_locked_inode(inode);
|
|
btrfs_read_locked_inode(inode);
|
|
|
-
|
|
|
|
|
inode_tree_add(inode);
|
|
inode_tree_add(inode);
|
|
|
unlock_new_inode(inode);
|
|
unlock_new_inode(inode);
|
|
|
if (new)
|
|
if (new)
|
|
@@ -4147,8 +4161,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
|
|
|
if (!IS_ERR(inode) && root != sub_root) {
|
|
if (!IS_ERR(inode) && root != sub_root) {
|
|
|
down_read(&root->fs_info->cleanup_work_sem);
|
|
down_read(&root->fs_info->cleanup_work_sem);
|
|
|
if (!(inode->i_sb->s_flags & MS_RDONLY))
|
|
if (!(inode->i_sb->s_flags & MS_RDONLY))
|
|
|
- btrfs_orphan_cleanup(sub_root);
|
|
|
|
|
|
|
+ ret = btrfs_orphan_cleanup(sub_root);
|
|
|
up_read(&root->fs_info->cleanup_work_sem);
|
|
up_read(&root->fs_info->cleanup_work_sem);
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ inode = ERR_PTR(ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return inode;
|
|
return inode;
|
|
@@ -4282,6 +4298,9 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
|
|
|
while (di_cur < di_total) {
|
|
while (di_cur < di_total) {
|
|
|
struct btrfs_key location;
|
|
struct btrfs_key location;
|
|
|
|
|
|
|
|
|
|
+ if (verify_dir_item(root, leaf, di))
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
name_len = btrfs_dir_name_len(leaf, di);
|
|
name_len = btrfs_dir_name_len(leaf, di);
|
|
|
if (name_len <= sizeof(tmp_name)) {
|
|
if (name_len <= sizeof(tmp_name)) {
|
|
|
name_ptr = tmp_name;
|
|
name_ptr = tmp_name;
|
|
@@ -4517,6 +4536,8 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
if (dir) {
|
|
if (dir) {
|
|
|
|
|
+ trace_btrfs_inode_request(dir);
|
|
|
|
|
+
|
|
|
ret = btrfs_set_inode_index(dir, index);
|
|
ret = btrfs_set_inode_index(dir, index);
|
|
|
if (ret) {
|
|
if (ret) {
|
|
|
iput(inode);
|
|
iput(inode);
|
|
@@ -4585,12 +4606,16 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|
|
if ((mode & S_IFREG)) {
|
|
if ((mode & S_IFREG)) {
|
|
|
if (btrfs_test_opt(root, NODATASUM))
|
|
if (btrfs_test_opt(root, NODATASUM))
|
|
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
|
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
|
|
|
- if (btrfs_test_opt(root, NODATACOW))
|
|
|
|
|
|
|
+ if (btrfs_test_opt(root, NODATACOW) ||
|
|
|
|
|
+ (BTRFS_I(dir)->flags & BTRFS_INODE_NODATACOW))
|
|
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;
|
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
insert_inode_hash(inode);
|
|
insert_inode_hash(inode);
|
|
|
inode_tree_add(inode);
|
|
inode_tree_add(inode);
|
|
|
|
|
+
|
|
|
|
|
+ trace_btrfs_inode_new(inode);
|
|
|
|
|
+
|
|
|
return inode;
|
|
return inode;
|
|
|
fail:
|
|
fail:
|
|
|
if (dir)
|
|
if (dir)
|
|
@@ -4809,7 +4834,10 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
|
|
|
|
|
|
|
|
/* do not allow sys_link's with other subvols of the same device */
|
|
/* do not allow sys_link's with other subvols of the same device */
|
|
|
if (root->objectid != BTRFS_I(inode)->root->objectid)
|
|
if (root->objectid != BTRFS_I(inode)->root->objectid)
|
|
|
- return -EPERM;
|
|
|
|
|
|
|
+ return -EXDEV;
|
|
|
|
|
+
|
|
|
|
|
+ if (inode->i_nlink == ~0U)
|
|
|
|
|
+ return -EMLINK;
|
|
|
|
|
|
|
|
btrfs_inc_nlink(inode);
|
|
btrfs_inc_nlink(inode);
|
|
|
inode->i_ctime = CURRENT_TIME;
|
|
inode->i_ctime = CURRENT_TIME;
|
|
@@ -5265,6 +5293,9 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
|
|
|
}
|
|
}
|
|
|
write_unlock(&em_tree->lock);
|
|
write_unlock(&em_tree->lock);
|
|
|
out:
|
|
out:
|
|
|
|
|
+
|
|
|
|
|
+ trace_btrfs_get_extent(root, em);
|
|
|
|
|
+
|
|
|
if (path)
|
|
if (path)
|
|
|
btrfs_free_path(path);
|
|
btrfs_free_path(path);
|
|
|
if (trans) {
|
|
if (trans) {
|
|
@@ -5748,6 +5779,10 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
|
|
|
|
|
|
|
|
kfree(dip->csums);
|
|
kfree(dip->csums);
|
|
|
kfree(dip);
|
|
kfree(dip);
|
|
|
|
|
+
|
|
|
|
|
+ /* If we had a csum failure make sure to clear the uptodate flag */
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ clear_bit(BIO_UPTODATE, &bio->bi_flags);
|
|
|
dio_end_io(bio, err);
|
|
dio_end_io(bio, err);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -5849,6 +5884,10 @@ static void btrfs_endio_direct_write(struct bio *bio, int err)
|
|
|
|
|
|
|
|
kfree(dip->csums);
|
|
kfree(dip->csums);
|
|
|
kfree(dip);
|
|
kfree(dip);
|
|
|
|
|
+
|
|
|
|
|
+ /* If we had an error make sure to clear the uptodate flag */
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ clear_bit(BIO_UPTODATE, &bio->bi_flags);
|
|
|
dio_end_io(bio, err);
|
|
dio_end_io(bio, err);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -5922,9 +5961,12 @@ static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
|
|
|
__btrfs_submit_bio_start_direct_io,
|
|
__btrfs_submit_bio_start_direct_io,
|
|
|
__btrfs_submit_bio_done);
|
|
__btrfs_submit_bio_done);
|
|
|
goto err;
|
|
goto err;
|
|
|
- } else if (!skip_sum)
|
|
|
|
|
- btrfs_lookup_bio_sums_dio(root, inode, bio,
|
|
|
|
|
|
|
+ } else if (!skip_sum) {
|
|
|
|
|
+ ret = btrfs_lookup_bio_sums_dio(root, inode, bio,
|
|
|
file_offset, csums);
|
|
file_offset, csums);
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ goto err;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
ret = btrfs_map_bio(root, rw, bio, 0, 1);
|
|
ret = btrfs_map_bio(root, rw, bio, 0, 1);
|
|
|
err:
|
|
err:
|
|
@@ -5948,6 +5990,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
|
|
|
int nr_pages = 0;
|
|
int nr_pages = 0;
|
|
|
u32 *csums = dip->csums;
|
|
u32 *csums = dip->csums;
|
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
|
+ int write = rw & REQ_WRITE;
|
|
|
|
|
|
|
|
bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev, start_sector, GFP_NOFS);
|
|
bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev, start_sector, GFP_NOFS);
|
|
|
if (!bio)
|
|
if (!bio)
|
|
@@ -5984,7 +6027,8 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
|
|
|
goto out_err;
|
|
goto out_err;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!skip_sum)
|
|
|
|
|
|
|
+ /* Write's use the ordered csums */
|
|
|
|
|
+ if (!write && !skip_sum)
|
|
|
csums = csums + nr_pages;
|
|
csums = csums + nr_pages;
|
|
|
start_sector += submit_len >> 9;
|
|
start_sector += submit_len >> 9;
|
|
|
file_offset += submit_len;
|
|
file_offset += submit_len;
|
|
@@ -6052,7 +6096,8 @@ static void btrfs_submit_direct(int rw, struct bio *bio, struct inode *inode,
|
|
|
}
|
|
}
|
|
|
dip->csums = NULL;
|
|
dip->csums = NULL;
|
|
|
|
|
|
|
|
- if (!skip_sum) {
|
|
|
|
|
|
|
+ /* Write's use the ordered csum stuff, so we don't need dip->csums */
|
|
|
|
|
+ if (!write && !skip_sum) {
|
|
|
dip->csums = kmalloc(sizeof(u32) * bio->bi_vcnt, GFP_NOFS);
|
|
dip->csums = kmalloc(sizeof(u32) * bio->bi_vcnt, GFP_NOFS);
|
|
|
if (!dip->csums) {
|
|
if (!dip->csums) {
|
|
|
kfree(dip);
|
|
kfree(dip);
|
|
@@ -6474,28 +6519,42 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static void btrfs_truncate(struct inode *inode)
|
|
|
|
|
|
|
+static int btrfs_truncate(struct inode *inode)
|
|
|
{
|
|
{
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
+ int err = 0;
|
|
|
struct btrfs_trans_handle *trans;
|
|
struct btrfs_trans_handle *trans;
|
|
|
unsigned long nr;
|
|
unsigned long nr;
|
|
|
u64 mask = root->sectorsize - 1;
|
|
u64 mask = root->sectorsize - 1;
|
|
|
|
|
|
|
|
- if (!S_ISREG(inode->i_mode)) {
|
|
|
|
|
- WARN_ON(1);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
ret = btrfs_truncate_page(inode->i_mapping, inode->i_size);
|
|
ret = btrfs_truncate_page(inode->i_mapping, inode->i_size);
|
|
|
if (ret)
|
|
if (ret)
|
|
|
- return;
|
|
|
|
|
|
|
+ return ret;
|
|
|
|
|
|
|
|
btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
|
|
btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
|
|
|
btrfs_ordered_update_i_size(inode, inode->i_size, NULL);
|
|
btrfs_ordered_update_i_size(inode, inode->i_size, NULL);
|
|
|
|
|
|
|
|
|
|
+ trans = btrfs_start_transaction(root, 5);
|
|
|
|
|
+ if (IS_ERR(trans))
|
|
|
|
|
+ return PTR_ERR(trans);
|
|
|
|
|
+
|
|
|
|
|
+ btrfs_set_trans_block_group(trans, inode);
|
|
|
|
|
+
|
|
|
|
|
+ ret = btrfs_orphan_add(trans, inode);
|
|
|
|
|
+ if (ret) {
|
|
|
|
|
+ btrfs_end_transaction(trans, root);
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ nr = trans->blocks_used;
|
|
|
|
|
+ btrfs_end_transaction(trans, root);
|
|
|
|
|
+ btrfs_btree_balance_dirty(root, nr);
|
|
|
|
|
+
|
|
|
|
|
+ /* Now start a transaction for the truncate */
|
|
|
trans = btrfs_start_transaction(root, 0);
|
|
trans = btrfs_start_transaction(root, 0);
|
|
|
- BUG_ON(IS_ERR(trans));
|
|
|
|
|
|
|
+ if (IS_ERR(trans))
|
|
|
|
|
+ return PTR_ERR(trans);
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
|
trans->block_rsv = root->orphan_block_rsv;
|
|
trans->block_rsv = root->orphan_block_rsv;
|
|
|
|
|
|
|
@@ -6522,29 +6581,38 @@ static void btrfs_truncate(struct inode *inode)
|
|
|
while (1) {
|
|
while (1) {
|
|
|
if (!trans) {
|
|
if (!trans) {
|
|
|
trans = btrfs_start_transaction(root, 0);
|
|
trans = btrfs_start_transaction(root, 0);
|
|
|
- BUG_ON(IS_ERR(trans));
|
|
|
|
|
|
|
+ if (IS_ERR(trans))
|
|
|
|
|
+ return PTR_ERR(trans);
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
|
trans->block_rsv = root->orphan_block_rsv;
|
|
trans->block_rsv = root->orphan_block_rsv;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ret = btrfs_block_rsv_check(trans, root,
|
|
ret = btrfs_block_rsv_check(trans, root,
|
|
|
root->orphan_block_rsv, 0, 5);
|
|
root->orphan_block_rsv, 0, 5);
|
|
|
- if (ret) {
|
|
|
|
|
- BUG_ON(ret != -EAGAIN);
|
|
|
|
|
|
|
+ if (ret == -EAGAIN) {
|
|
|
ret = btrfs_commit_transaction(trans, root);
|
|
ret = btrfs_commit_transaction(trans, root);
|
|
|
- BUG_ON(ret);
|
|
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ return ret;
|
|
|
trans = NULL;
|
|
trans = NULL;
|
|
|
continue;
|
|
continue;
|
|
|
|
|
+ } else if (ret) {
|
|
|
|
|
+ err = ret;
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ret = btrfs_truncate_inode_items(trans, root, inode,
|
|
ret = btrfs_truncate_inode_items(trans, root, inode,
|
|
|
inode->i_size,
|
|
inode->i_size,
|
|
|
BTRFS_EXTENT_DATA_KEY);
|
|
BTRFS_EXTENT_DATA_KEY);
|
|
|
- if (ret != -EAGAIN)
|
|
|
|
|
|
|
+ if (ret != -EAGAIN) {
|
|
|
|
|
+ err = ret;
|
|
|
break;
|
|
break;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
ret = btrfs_update_inode(trans, root, inode);
|
|
ret = btrfs_update_inode(trans, root, inode);
|
|
|
- BUG_ON(ret);
|
|
|
|
|
|
|
+ if (ret) {
|
|
|
|
|
+ err = ret;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
nr = trans->blocks_used;
|
|
nr = trans->blocks_used;
|
|
|
btrfs_end_transaction(trans, root);
|
|
btrfs_end_transaction(trans, root);
|
|
@@ -6554,16 +6622,27 @@ static void btrfs_truncate(struct inode *inode)
|
|
|
|
|
|
|
|
if (ret == 0 && inode->i_nlink > 0) {
|
|
if (ret == 0 && inode->i_nlink > 0) {
|
|
|
ret = btrfs_orphan_del(trans, inode);
|
|
ret = btrfs_orphan_del(trans, inode);
|
|
|
- BUG_ON(ret);
|
|
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ err = ret;
|
|
|
|
|
+ } else if (ret && inode->i_nlink > 0) {
|
|
|
|
|
+ /*
|
|
|
|
|
+ * Failed to do the truncate, remove us from the in memory
|
|
|
|
|
+ * orphan list.
|
|
|
|
|
+ */
|
|
|
|
|
+ ret = btrfs_orphan_del(NULL, inode);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ret = btrfs_update_inode(trans, root, inode);
|
|
ret = btrfs_update_inode(trans, root, inode);
|
|
|
- BUG_ON(ret);
|
|
|
|
|
|
|
+ if (ret && !err)
|
|
|
|
|
+ err = ret;
|
|
|
|
|
|
|
|
nr = trans->blocks_used;
|
|
nr = trans->blocks_used;
|
|
|
ret = btrfs_end_transaction_throttle(trans, root);
|
|
ret = btrfs_end_transaction_throttle(trans, root);
|
|
|
- BUG_ON(ret);
|
|
|
|
|
|
|
+ if (ret && !err)
|
|
|
|
|
+ err = ret;
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
|
btrfs_btree_balance_dirty(root, nr);
|
|
|
|
|
+
|
|
|
|
|
+ return err;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -6630,9 +6709,8 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
|
|
|
ei->index_cnt = (u64)-1;
|
|
ei->index_cnt = (u64)-1;
|
|
|
ei->last_unlink_trans = 0;
|
|
ei->last_unlink_trans = 0;
|
|
|
|
|
|
|
|
- spin_lock_init(&ei->accounting_lock);
|
|
|
|
|
atomic_set(&ei->outstanding_extents, 0);
|
|
atomic_set(&ei->outstanding_extents, 0);
|
|
|
- ei->reserved_extents = 0;
|
|
|
|
|
|
|
+ atomic_set(&ei->reserved_extents, 0);
|
|
|
|
|
|
|
|
ei->ordered_data_close = 0;
|
|
ei->ordered_data_close = 0;
|
|
|
ei->orphan_meta_reserved = 0;
|
|
ei->orphan_meta_reserved = 0;
|
|
@@ -6668,7 +6746,7 @@ void btrfs_destroy_inode(struct inode *inode)
|
|
|
WARN_ON(!list_empty(&inode->i_dentry));
|
|
WARN_ON(!list_empty(&inode->i_dentry));
|
|
|
WARN_ON(inode->i_data.nrpages);
|
|
WARN_ON(inode->i_data.nrpages);
|
|
|
WARN_ON(atomic_read(&BTRFS_I(inode)->outstanding_extents));
|
|
WARN_ON(atomic_read(&BTRFS_I(inode)->outstanding_extents));
|
|
|
- WARN_ON(BTRFS_I(inode)->reserved_extents);
|
|
|
|
|
|
|
+ WARN_ON(atomic_read(&BTRFS_I(inode)->reserved_extents));
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
|
* This can happen where we create an inode, but somebody else also
|
|
* This can happen where we create an inode, but somebody else also
|
|
@@ -6760,6 +6838,8 @@ void btrfs_destroy_cachep(void)
|
|
|
kmem_cache_destroy(btrfs_transaction_cachep);
|
|
kmem_cache_destroy(btrfs_transaction_cachep);
|
|
|
if (btrfs_path_cachep)
|
|
if (btrfs_path_cachep)
|
|
|
kmem_cache_destroy(btrfs_path_cachep);
|
|
kmem_cache_destroy(btrfs_path_cachep);
|
|
|
|
|
+ if (btrfs_free_space_cachep)
|
|
|
|
|
+ kmem_cache_destroy(btrfs_free_space_cachep);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int btrfs_init_cachep(void)
|
|
int btrfs_init_cachep(void)
|
|
@@ -6788,6 +6868,12 @@ int btrfs_init_cachep(void)
|
|
|
if (!btrfs_path_cachep)
|
|
if (!btrfs_path_cachep)
|
|
|
goto fail;
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
+ btrfs_free_space_cachep = kmem_cache_create("btrfs_free_space_cache",
|
|
|
|
|
+ sizeof(struct btrfs_free_space), 0,
|
|
|
|
|
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
|
|
|
|
|
+ if (!btrfs_free_space_cachep)
|
|
|
|
|
+ goto fail;
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
fail:
|
|
fail:
|
|
|
btrfs_destroy_cachep();
|
|
btrfs_destroy_cachep();
|
|
@@ -6806,6 +6892,26 @@ static int btrfs_getattr(struct vfsmount *mnt,
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/*
|
|
|
|
|
+ * If a file is moved, it will inherit the cow and compression flags of the new
|
|
|
|
|
+ * directory.
|
|
|
|
|
+ */
|
|
|
|
|
+static void fixup_inode_flags(struct inode *dir, struct inode *inode)
|
|
|
|
|
+{
|
|
|
|
|
+ struct btrfs_inode *b_dir = BTRFS_I(dir);
|
|
|
|
|
+ struct btrfs_inode *b_inode = BTRFS_I(inode);
|
|
|
|
|
+
|
|
|
|
|
+ if (b_dir->flags & BTRFS_INODE_NODATACOW)
|
|
|
|
|
+ b_inode->flags |= BTRFS_INODE_NODATACOW;
|
|
|
|
|
+ else
|
|
|
|
|
+ b_inode->flags &= ~BTRFS_INODE_NODATACOW;
|
|
|
|
|
+
|
|
|
|
|
+ if (b_dir->flags & BTRFS_INODE_COMPRESS)
|
|
|
|
|
+ b_inode->flags |= BTRFS_INODE_COMPRESS;
|
|
|
|
|
+ else
|
|
|
|
|
+ b_inode->flags &= ~BTRFS_INODE_COMPRESS;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
struct inode *new_dir, struct dentry *new_dentry)
|
|
struct inode *new_dir, struct dentry *new_dentry)
|
|
|
{
|
|
{
|
|
@@ -6908,11 +7014,12 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
old_dentry->d_name.name,
|
|
old_dentry->d_name.name,
|
|
|
old_dentry->d_name.len);
|
|
old_dentry->d_name.len);
|
|
|
} else {
|
|
} else {
|
|
|
- btrfs_inc_nlink(old_dentry->d_inode);
|
|
|
|
|
- ret = btrfs_unlink_inode(trans, root, old_dir,
|
|
|
|
|
- old_dentry->d_inode,
|
|
|
|
|
- old_dentry->d_name.name,
|
|
|
|
|
- old_dentry->d_name.len);
|
|
|
|
|
|
|
+ ret = __btrfs_unlink_inode(trans, root, old_dir,
|
|
|
|
|
+ old_dentry->d_inode,
|
|
|
|
|
+ old_dentry->d_name.name,
|
|
|
|
|
+ old_dentry->d_name.len);
|
|
|
|
|
+ if (!ret)
|
|
|
|
|
+ ret = btrfs_update_inode(trans, root, old_inode);
|
|
|
}
|
|
}
|
|
|
BUG_ON(ret);
|
|
BUG_ON(ret);
|
|
|
|
|
|
|
@@ -6939,6 +7046,8 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ fixup_inode_flags(new_dir, old_inode);
|
|
|
|
|
+
|
|
|
ret = btrfs_add_link(trans, new_dir, old_inode,
|
|
ret = btrfs_add_link(trans, new_dir, old_inode,
|
|
|
new_dentry->d_name.name,
|
|
new_dentry->d_name.name,
|
|
|
new_dentry->d_name.len, 0, index);
|
|
new_dentry->d_name.len, 0, index);
|
|
@@ -7355,7 +7464,6 @@ static const struct address_space_operations btrfs_symlink_aops = {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
static const struct inode_operations btrfs_file_inode_operations = {
|
|
static const struct inode_operations btrfs_file_inode_operations = {
|
|
|
- .truncate = btrfs_truncate,
|
|
|
|
|
.getattr = btrfs_getattr,
|
|
.getattr = btrfs_getattr,
|
|
|
.setattr = btrfs_setattr,
|
|
.setattr = btrfs_setattr,
|
|
|
.setxattr = btrfs_setxattr,
|
|
.setxattr = btrfs_setxattr,
|