aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-04-27 11:16:33 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-27 11:16:33 -0700
commit4ebf66233798347a73b01da5d30d5d2c0ef39f56 (patch)
tree3aae053ccbf3b6206fe6d17e62172559d35a7711 /fs
parent14b6084daa61bfd4da926f63e6e8bd0b6de87ad9 (diff)
parent46a53cca826e71effe59e3cb4f383622c33ebdcb (diff)
downloadkernel_samsung_tuna-4ebf66233798347a73b01da5d30d5d2c0ef39f56.zip
kernel_samsung_tuna-4ebf66233798347a73b01da5d30d5d2c0ef39f56.tar.gz
kernel_samsung_tuna-4ebf66233798347a73b01da5d30d5d2c0ef39f56.tar.bz2
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable: Btrfs: look for acls during btrfs_read_locked_inode Btrfs: fix acl caching Btrfs: Fix a bunch of printk() warnings. Btrfs: Fix a trivial warning using max() of u64 vs ULL. Btrfs: remove unused btrfs_bit_radix slab Btrfs: ratelimit IO error printks Btrfs: remove #if 0 code Btrfs: When shrinking, only update disk size on success Btrfs: fix deadlocks and stalls on dead root removal Btrfs: fix fallocate deadlock on inode extent lock Btrfs: kill btrfs_cache_create Btrfs: don't export symbols Btrfs: simplify makefile Btrfs: try to keep a healthy ratio of metadata vs data block groups
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/Makefile19
-rw-r--r--fs/btrfs/acl.c18
-rw-r--r--fs/btrfs/ctree.h6
-rw-r--r--fs/btrfs/disk-io.c93
-rw-r--r--fs/btrfs/extent-tree.c49
-rw-r--r--fs/btrfs/extent_io.c81
-rw-r--r--fs/btrfs/extent_map.c17
-rw-r--r--fs/btrfs/file.c89
-rw-r--r--fs/btrfs/free-space-cache.c15
-rw-r--r--fs/btrfs/inode-map.c2
-rw-r--r--fs/btrfs/inode.c147
-rw-r--r--fs/btrfs/ioctl.c9
-rw-r--r--fs/btrfs/super.c27
-rw-r--r--fs/btrfs/transaction.c6
-rw-r--r--fs/btrfs/tree-log.c2
-rw-r--r--fs/btrfs/volumes.c35
-rw-r--r--fs/btrfs/volumes.h3
17 files changed, 281 insertions, 337 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 9adf5e4..9421284 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -1,25 +1,10 @@
-ifneq ($(KERNELRELEASE),)
-# kbuild part of makefile
obj-$(CONFIG_BTRFS_FS) := btrfs.o
-btrfs-y := super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
+
+btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
file-item.o inode-item.o inode-map.o disk-io.o \
transaction.o inode.o file.o tree-defrag.o \
extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \
extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
ref-cache.o export.o tree-log.o acl.o free-space-cache.o zlib.o \
compression.o delayed-ref.o
-else
-
-# Normal Makefile
-
-KERNELDIR := /lib/modules/`uname -r`/build
-all:
- $(MAKE) -C $(KERNELDIR) M=`pwd` CONFIG_BTRFS_FS=m modules
-
-modules_install:
- $(MAKE) -C $(KERNELDIR) M=`pwd` modules_install
-clean:
- $(MAKE) -C $(KERNELDIR) M=`pwd` clean
-
-endif
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index 7fdd184..cbba000 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -60,15 +60,20 @@ static struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
return ERR_PTR(-EINVAL);
}
+ /* Handle the cached NULL acl case without locking */
+ acl = ACCESS_ONCE(*p_acl);
+ if (!acl)
+ return acl;
+
spin_lock(&inode->i_lock);
- if (*p_acl != BTRFS_ACL_NOT_CACHED)
- acl = posix_acl_dup(*p_acl);
+ acl = *p_acl;
+ if (acl != BTRFS_ACL_NOT_CACHED)
+ acl = posix_acl_dup(acl);
spin_unlock(&inode->i_lock);
- if (acl)
+ if (acl != BTRFS_ACL_NOT_CACHED)
return acl;
-
size = __btrfs_getxattr(inode, name, "", 0);
if (size > 0) {
value = kzalloc(size, GFP_NOFS);
@@ -80,9 +85,12 @@ static struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
btrfs_update_cached_acl(inode, p_acl, acl);
}
kfree(value);
- } else if (size == -ENOENT) {
+ } else if (size == -ENOENT || size == -ENODATA || size == 0) {
+ /* FIXME, who returns -ENOENT? I think nobody */
acl = NULL;
btrfs_update_cached_acl(inode, p_acl, acl);
+ } else {
+ acl = ERR_PTR(-EIO);
}
return acl;
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index ad96495..4414a5d 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -881,6 +881,9 @@ struct btrfs_fs_info {
u64 metadata_alloc_profile;
u64 system_alloc_profile;
+ unsigned data_chunk_allocations;
+ unsigned metadata_ratio;
+
void *bdev_holder;
};
@@ -2174,7 +2177,8 @@ int btrfs_check_file(struct btrfs_root *root, struct inode *inode);
extern struct file_operations btrfs_file_operations;
int btrfs_drop_extents(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode,
- u64 start, u64 end, u64 inline_limit, u64 *hint_block);
+ u64 start, u64 end, u64 locked_end,
+ u64 inline_limit, u64 *hint_block);
int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct inode *inode, u64 start, u64 end);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index a6b8374..0ff16d3 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -232,10 +232,14 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
memcpy(&found, result, csum_size);
read_extent_buffer(buf, &val, 0, csum_size);
- printk(KERN_INFO "btrfs: %s checksum verify failed "
- "on %llu wanted %X found %X level %d\n",
- root->fs_info->sb->s_id,
- buf->start, val, found, btrfs_header_level(buf));
+ if (printk_ratelimit()) {
+ printk(KERN_INFO "btrfs: %s checksum verify "
+ "failed on %llu wanted %X found %X "
+ "level %d\n",
+ root->fs_info->sb->s_id,
+ (unsigned long long)buf->start, val, found,
+ btrfs_header_level(buf));
+ }
if (result != (char *)&inline_result)
kfree(result);
return 1;
@@ -268,10 +272,13 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
ret = 0;
goto out;
}
- printk("parent transid verify failed on %llu wanted %llu found %llu\n",
- (unsigned long long)eb->start,
- (unsigned long long)parent_transid,
- (unsigned long long)btrfs_header_generation(eb));
+ if (printk_ratelimit()) {
+ printk("parent transid verify failed on %llu wanted %llu "
+ "found %llu\n",
+ (unsigned long long)eb->start,
+ (unsigned long long)parent_transid,
+ (unsigned long long)btrfs_header_generation(eb));
+ }
ret = 1;
clear_extent_buffer_uptodate(io_tree, eb);
out:
@@ -415,9 +422,12 @@ static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
found_start = btrfs_header_bytenr(eb);
if (found_start != start) {
- printk(KERN_INFO "btrfs bad tree block start %llu %llu\n",
- (unsigned long long)found_start,
- (unsigned long long)eb->start);
+ if (printk_ratelimit()) {
+ printk(KERN_INFO "btrfs bad tree block start "
+ "%llu %llu\n",
+ (unsigned long long)found_start,
+ (unsigned long long)eb->start);
+ }
ret = -EIO;
goto err;
}
@@ -429,8 +439,10 @@ static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
goto err;
}
if (check_tree_block_fsid(root, eb)) {
- printk(KERN_INFO "btrfs bad fsid on block %llu\n",
- (unsigned long long)eb->start);
+ if (printk_ratelimit()) {
+ printk(KERN_INFO "btrfs bad fsid on block %llu\n",
+ (unsigned long long)eb->start);
+ }
ret = -EIO;
goto err;
}
@@ -584,18 +596,7 @@ int btrfs_wq_submit_bio(struct btrfs_fs_info *fs_info, struct inode *inode,
btrfs_set_work_high_prio(&async->work);
btrfs_queue_worker(&fs_info->workers, &async->work);
-#if 0
- int limit = btrfs_async_submit_limit(fs_info);
- if (atomic_read(&fs_info->nr_async_submits) > limit) {
- wait_event_timeout(fs_info->async_submit_wait,
- (atomic_read(&fs_info->nr_async_submits) < limit),
- HZ/10);
- wait_event_timeout(fs_info->async_submit_wait,
- (atomic_read(&fs_info->nr_async_bios) < limit),
- HZ/10);
- }
-#endif
while (atomic_read(&fs_info->async_submit_draining) &&
atomic_read(&fs_info->nr_async_submits)) {
wait_event(fs_info->async_submit_wait,
@@ -770,27 +771,6 @@ static void btree_invalidatepage(struct page *page, unsigned long offset)
}
}
-#if 0
-static int btree_writepage(struct page *page, struct writeback_control *wbc)
-{
- struct buffer_head *bh;
- struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
- struct buffer_head *head;
- if (!page_has_buffers(page)) {
- create_empty_buffers(page, root->fs_info->sb->s_blocksize,
- (1 << BH_Dirty)|(1 << BH_Uptodate));
- }
- head = page_buffers(page);
- bh = head;
- do {
- if (buffer_dirty(bh))
- csum_tree_block(root, bh, 0);
- bh = bh->b_this_page;
- } while (bh != head);
- return block_write_full_page(page, btree_get_block, wbc);
-}
-#endif
-
static struct address_space_operations btree_aops = {
.readpage = btree_readpage,
.writepage = btree_writepage,
@@ -1278,11 +1258,7 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits)
int ret = 0;
struct btrfs_device *device;
struct backing_dev_info *bdi;
-#if 0
- if ((bdi_bits & (1 << BDI_write_congested)) &&
- btrfs_congested_async(info, 0))
- return 1;
-#endif
+
list_for_each_entry(device, &info->fs_devices->devices, dev_list) {
if (!device->bdev)
continue;
@@ -1604,6 +1580,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
fs_info->btree_inode = new_inode(sb);
fs_info->btree_inode->i_ino = 1;
fs_info->btree_inode->i_nlink = 1;
+ fs_info->metadata_ratio = 8;
fs_info->thread_pool_size = min_t(unsigned long,
num_online_cpus() + 2, 8);
@@ -1694,7 +1671,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
if (features) {
printk(KERN_ERR "BTRFS: couldn't mount because of "
"unsupported optional features (%Lx).\n",
- features);
+ (unsigned long long)features);
err = -EINVAL;
goto fail_iput;
}
@@ -1704,7 +1681,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
if (!(sb->s_flags & MS_RDONLY) && features) {
printk(KERN_ERR "BTRFS: couldn't mount RDWR because of "
"unsupported option features (%Lx).\n",
- features);
+ (unsigned long long)features);
err = -EINVAL;
goto fail_iput;
}
@@ -2296,7 +2273,7 @@ int close_ctree(struct btrfs_root *root)
if (fs_info->delalloc_bytes) {
printk(KERN_INFO "btrfs: at unmount delalloc count %llu\n",
- fs_info->delalloc_bytes);
+ (unsigned long long)fs_info->delalloc_bytes);
}
if (fs_info->total_ref_cache_size) {
printk(KERN_INFO "btrfs: at umount reference cache size %llu\n",
@@ -2333,16 +2310,6 @@ int close_ctree(struct btrfs_root *root)
btrfs_stop_workers(&fs_info->endio_write_workers);
btrfs_stop_workers(&fs_info->submit_workers);
-#if 0
- while (!list_empty(&fs_info->hashers)) {
- struct btrfs_hasher *hasher;
- hasher = list_entry(fs_info->hashers.next, struct btrfs_hasher,
- hashers);
- list_del(&hasher->hashers);
- crypto_free_hash(&fs_info->hash_tfm);
- kfree(hasher);
- }
-#endif
btrfs_close_devices(fs_info->fs_devices);
btrfs_mapping_tree_free(&fs_info->mapping_tree);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 178df4c..e496644 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1844,10 +1844,14 @@ again:
printk(KERN_ERR "no space left, need %llu, %llu delalloc bytes"
", %llu bytes_used, %llu bytes_reserved, "
"%llu bytes_pinned, %llu bytes_readonly, %llu may use"
- "%llu total\n", bytes, data_sinfo->bytes_delalloc,
- data_sinfo->bytes_used, data_sinfo->bytes_reserved,
- data_sinfo->bytes_pinned, data_sinfo->bytes_readonly,
- data_sinfo->bytes_may_use, data_sinfo->total_bytes);
+ "%llu total\n", (unsigned long long)bytes,
+ (unsigned long long)data_sinfo->bytes_delalloc,
+ (unsigned long long)data_sinfo->bytes_used,
+ (unsigned long long)data_sinfo->bytes_reserved,
+ (unsigned long long)data_sinfo->bytes_pinned,
+ (unsigned long long)data_sinfo->bytes_readonly,
+ (unsigned long long)data_sinfo->bytes_may_use,
+ (unsigned long long)data_sinfo->total_bytes);
return -ENOSPC;
}
data_sinfo->bytes_may_use += bytes;
@@ -1918,15 +1922,29 @@ void btrfs_delalloc_free_space(struct btrfs_root *root, struct inode *inode,
spin_unlock(&info->lock);
}
+static void force_metadata_allocation(struct btrfs_fs_info *info)
+{
+ struct list_head *head = &info->space_info;
+ struct btrfs_space_info *found;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(found, head, list) {
+ if (found->flags & BTRFS_BLOCK_GROUP_METADATA)
+ found->force_alloc = 1;
+ }
+ rcu_read_unlock();
+}
+
static int do_chunk_alloc(struct btrfs_trans_handle *trans,
struct btrfs_root *extent_root, u64 alloc_bytes,
u64 flags, int force)
{
struct btrfs_space_info *space_info;
+ struct btrfs_fs_info *fs_info = extent_root->fs_info;
u64 thresh;
int ret = 0;
- mutex_lock(&extent_root->fs_info->chunk_mutex);
+ mutex_lock(&fs_info->chunk_mutex);
flags = btrfs_reduce_alloc_profile(extent_root, flags);
@@ -1958,6 +1976,18 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
}
spin_unlock(&space_info->lock);
+ /*
+ * if we're doing a data chunk, go ahead and make sure that
+ * we keep a reasonable number of metadata chunks allocated in the
+ * FS as well.
+ */
+ if (flags & BTRFS_BLOCK_GROUP_DATA) {
+ fs_info->data_chunk_allocations++;
+ if (!(fs_info->data_chunk_allocations %
+ fs_info->metadata_ratio))
+ force_metadata_allocation(fs_info);
+ }
+
ret = btrfs_alloc_chunk(trans, extent_root, flags);
if (ret)
space_info->full = 1;
@@ -2798,9 +2828,12 @@ static void dump_space_info(struct btrfs_space_info *info, u64 bytes)
info->bytes_pinned - info->bytes_reserved),
(info->full) ? "" : "not ");
printk(KERN_INFO "space_info total=%llu, pinned=%llu, delalloc=%llu,"
- " may_use=%llu, used=%llu\n", info->total_bytes,
- info->bytes_pinned, info->bytes_delalloc, info->bytes_may_use,
- info->bytes_used);
+ " may_use=%llu, used=%llu\n",
+ (unsigned long long)info->total_bytes,
+ (unsigned long long)info->bytes_pinned,
+ (unsigned long long)info->bytes_delalloc,
+ (unsigned long long)info->bytes_may_use,
+ (unsigned long long)info->bytes_used);
down_read(&info->groups_sem);
list_for_each_entry(cache, &info->block_groups, list) {
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 05a1c42..fe9eb99 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -17,12 +17,6 @@
#include "ctree.h"
#include "btrfs_inode.h"
-/* temporary define until extent_map moves out of btrfs */
-struct kmem_cache *btrfs_cache_create(const char *name, size_t size,
- unsigned long extra_flags,
- void (*ctor)(void *, struct kmem_cache *,
- unsigned long));
-
static struct kmem_cache *extent_state_cache;
static struct kmem_cache *extent_buffer_cache;
@@ -58,15 +52,15 @@ struct extent_page_data {
int __init extent_io_init(void)
{
- extent_state_cache = btrfs_cache_create("extent_state",
- sizeof(struct extent_state), 0,
- NULL);
+ extent_state_cache = kmem_cache_create("extent_state",
+ sizeof(struct extent_state), 0,
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!extent_state_cache)
return -ENOMEM;
- extent_buffer_cache = btrfs_cache_create("extent_buffers",
- sizeof(struct extent_buffer), 0,
- NULL);
+ extent_buffer_cache = kmem_cache_create("extent_buffers",
+ sizeof(struct extent_buffer), 0,
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!extent_buffer_cache)
goto free_state_cache;
return 0;
@@ -1407,69 +1401,6 @@ out:
return total_bytes;
}
-#if 0
-/*
- * helper function to lock both pages and extents in the tree.
- * pages must be locked first.
- */
-static int lock_range(struct extent_io_tree *tree, u64 start, u64 end)
-{
- unsigned long index = start >> PAGE_CACHE_SHIFT;
- unsigned long end_index = end >> PAGE_CACHE_SHIFT;
- struct page *page;
- int err;
-
- while (index <= end_index) {
- page = grab_cache_page(tree->mapping, index);
- if (!page) {
- err = -ENOMEM;
- goto failed;
- }
- if (IS_ERR(page)) {
- err = PTR_ERR(page);
- goto failed;
- }
- index++;
- }
- lock_extent(tree, start, end, GFP_NOFS);
- return 0;
-
-failed:
- /*
- * we failed above in getting the page at 'index', so we undo here
- * up to but not including the page at 'index'
- */
- end_index = index;
- index = start >> PAGE_CACHE_SHIFT;
- while (index < end_index) {
- page = find_get_page(tree->mapping, index);
- unlock_page(page);
- page_cache_release(page);
- index++;
- }
- return err;
-}
-
-/*
- * helper function to unlock both pages and extents in the tree.
- */
-static int unlock_range(struct extent_io_tree *tree, u64 start, u64 end)
-{
- unsigned long index = start >> PAGE_CACHE_SHIFT;
- unsigned long end_index = end >> PAGE_CACHE_SHIFT;
- struct page *page;
-
- while (index <= end_index) {
- page = find_get_page(tree->mapping, index);
- unlock_page(page);
- page_cache_release(page);
- index++;
- }
- unlock_extent(tree, start, end, GFP_NOFS);
- return 0;
-}
-#endif
-
/*
* set the private field for a given byte offset in the tree. If there isn't
* an extent_state there already, this does nothing.
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index b187917..30c9365 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -6,19 +6,14 @@
#include <linux/hardirq.h>
#include "extent_map.h"
-/* temporary define until extent_map moves out of btrfs */
-struct kmem_cache *btrfs_cache_create(const char *name, size_t size,
- unsigned long extra_flags,
- void (*ctor)(void *, struct kmem_cache *,
- unsigned long));
static struct kmem_cache *extent_map_cache;
int __init extent_map_init(void)
{
- extent_map_cache = btrfs_cache_create("extent_map",
- sizeof(struct extent_map), 0,
- NULL);
+ extent_map_cache = kmem_cache_create("extent_map",
+ sizeof(struct extent_map), 0,
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!extent_map_cache)
return -ENOMEM;
return 0;
@@ -43,7 +38,6 @@ void extent_map_tree_init(struct extent_map_tree *tree, gfp_t mask)
tree->map.rb_node = NULL;
spin_lock_init(&tree->lock);
}
-EXPORT_SYMBOL(extent_map_tree_init);
/**
* alloc_extent_map - allocate new extent map structure
@@ -64,7 +58,6 @@ struct extent_map *alloc_extent_map(gfp_t mask)
atomic_set(&em->refs, 1);
return em;
}
-EXPORT_SYMBOL(alloc_extent_map);
/**
* free_extent_map - drop reference count of an extent_map
@@ -83,7 +76,6 @@ void free_extent_map(struct extent_map *em)
kmem_cache_free(extent_map_cache, em);
}
}
-EXPORT_SYMBOL(free_extent_map);
static struct rb_node *tree_insert(struct rb_root *root, u64 offset,
struct rb_node *node)
@@ -264,7 +256,6 @@ int add_extent_mapping(struct extent_map_tree *tree,
out:
return ret;
}
-EXPORT_SYMBOL(add_extent_mapping);
/* simple helper to do math around the end of an extent, handling wrap */
static u64 range_end(u64 start, u64 len)
@@ -326,7 +317,6 @@ found:
out:
return em;
}
-EXPORT_SYMBOL(lookup_extent_mapping);
/**
* remove_extent_mapping - removes an extent_map from the extent tree
@@ -346,4 +336,3 @@ int remove_extent_mapping(struct extent_map_tree *tree, struct extent_map *em)
em->in_tree = 0;
return ret;
}
-EXPORT_SYMBOL(remove_extent_mapping);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 482f8db..1d51dc3 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -272,83 +272,6 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
return 0;
}
-int btrfs_check_file(struct btrfs_root *root, struct inode *inode)
-{
- return 0;
-#if 0
- struct btrfs_path *path;
- struct btrfs_key found_key;
- struct extent_buffer *leaf;
- struct btrfs_file_extent_item *extent;
- u64 last_offset = 0;
- int nritems;
- int slot;
- int found_type;
- int ret;
- int err = 0;
- u64 extent_end = 0;
-
- path = btrfs_alloc_path();
- ret = btrfs_lookup_file_extent(NULL, root, path, inode->i_ino,
- last_offset, 0);
- while (1) {
- nritems = btrfs_header_nritems(path->nodes[0]);
- if (path->slots[0] >= nritems) {
- ret = btrfs_next_leaf(root, path);
- if (ret)
- goto out;
- nritems = btrfs_header_nritems(path->nodes[0]);
- }
- slot = path->slots[0];
- leaf = path->nodes[0];
- btrfs_item_key_to_cpu(leaf, &found_key, slot);
- if (found_key.objectid != inode->i_ino)
- break;
- if (found_key.type != BTRFS_EXTENT_DATA_KEY)
- goto out;
-
- if (found_key.offset < last_offset) {
- WARN_ON(1);
- btrfs_print_leaf(root, leaf);
- printk(KERN_ERR "inode %lu found offset %llu "
- "expected %llu\n", inode->i_ino,
- (unsigned long long)found_key.offset,
- (unsigned long long)last_offset);
- err = 1;
- goto out;
- }
- extent = btrfs_item_ptr(leaf, slot,
- struct btrfs_file_extent_item);
- found_type = btrfs_file_extent_type(leaf, extent);
- if (found_type == BTRFS_FILE_EXTENT_REG) {
- extent_end = found_key.offset +
- btrfs_file_extent_num_bytes(leaf, extent);
- } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
- struct btrfs_item *item;
- item = btrfs_item_nr(leaf, slot);
- extent_end = found_key.offset +
- btrfs_file_extent_inline_len(leaf, extent);
- extent_end = (extent_end + root->sectorsize - 1) &
- ~((u64)root->sectorsize - 1);
- }
- last_offset = extent_end;
- path->slots[0]++;
- }
- if (0 && last_offset < inode->i_size) {
- WARN_ON(1);
- btrfs_print_leaf(root, leaf);
- printk(KERN_ERR "inode %lu found offset %llu size %llu\n",
- inode->i_ino, (unsigned long long)last_offset,
- (unsigned long long)inode->i_size);
- err = 1;
-
- }
-out:
- btrfs_free_path(path);
- return err;
-#endif
-}
-
/*
* this is very complex, but the basic idea is to drop all extents
* in the range start - end. hint_block is filled in with a block number
@@ -363,15 +286,16 @@ out:
*/
noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode,
- u64 start, u64 end, u64 inline_limit, u64 *hint_byte)
+ u64 start, u64 end, u64 locked_end,
+ u64 inline_limit, u64 *hint_byte)
{
u64 extent_end = 0;
- u64 locked_end = end;
u64 search_start = start;
u64 leaf_start;
u64 ram_bytes = 0;
u64 orig_parent = 0;
u64 disk_bytenr = 0;
+ u64 orig_locked_end = locked_end;
u8 compression;
u8 encryption;
u16 other_encoding = 0;
@@ -684,11 +608,10 @@ next_slot:
}
out:
btrfs_free_path(path);
- if (locked_end > end) {
- unlock_extent(&BTRFS_I(inode)->io_tree, end, locked_end - 1,
- GFP_NOFS);
+ if (locked_end > orig_locked_end) {
+ unlock_extent(&BTRFS_I(inode)->io_tree, orig_locked_end,
+ locked_end - 1, GFP_NOFS);
}
- btrfs_check_file(root, inode);
return ret;
}
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 768b952..0bc9365 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -332,13 +332,17 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
printk(KERN_ERR "couldn't find space %llu to free\n",
(unsigned long long)offset);
printk(KERN_ERR "cached is %d, offset %llu bytes %llu\n",
- block_group->cached, block_group->key.objectid,
- block_group->key.offset);
+ block_group->cached,
+ (unsigned long long)block_group->key.objectid,
+ (unsigned long long)block_group->key.offset);
btrfs_dump_free_space(block_group, bytes);
} else if (info) {
printk(KERN_ERR "hmm, found offset=%llu bytes=%llu, "
"but wanted offset=%llu bytes=%llu\n",
- info->offset, info->bytes, offset, bytes);
+ (unsigned long long)info->offset,
+ (unsigned long long)info->bytes,
+ (unsigned long long)offset,
+ (unsigned long long)bytes);
}
WARN_ON(1);
}
@@ -357,8 +361,9 @@ void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group,
info = rb_entry(n, struct btrfs_free_space, offset_index);
if (info->bytes >= bytes)
count++;
- printk(KERN_ERR "entry offset %llu, bytes %llu\n", info->offset,
- info->bytes);
+ printk(KERN_ERR "entry offset %llu, bytes %llu\n",
+ (unsigned long long)info->offset,
+ (unsigned long long)info->bytes);
}
printk(KERN_INFO "%d blocks of free space at or bigger than bytes is"
"\n", count);
diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c
index cc7334d..9abbced 100644
--- a/fs/btrfs/inode-map.c
+++ b/fs/btrfs/inode-map.c
@@ -79,7 +79,7 @@ int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
}
path = btrfs_alloc_path();
BUG_ON(!path);
- search_start = max(search_start, BTRFS_FIRST_FREE_OBJECTID);
+ search_start = max(search_start, (u64)BTRFS_FIRST_FREE_OBJECTID);
search_key.objectid = search_start;
search_key.type = 0;
search_key.offset = 0;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 65219f6..90c23eb 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -70,7 +70,6 @@ static struct extent_io_ops btrfs_extent_io_ops;
static struct kmem_cache *btrfs_inode_cachep;
struct kmem_cache *btrfs_trans_handle_cachep;
struct kmem_cache *btrfs_transaction_cachep;
-struct kmem_cache *btrfs_bit_radix_cachep;
struct kmem_cache *btrfs_path_cachep;
#define S_SHIFT 12
@@ -234,7 +233,7 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans,
}
ret = btrfs_drop_extents(trans, root, inode, start,
- aligned_end, start, &hint_byte);
+ aligned_end, aligned_end, start, &hint_byte);
BUG_ON(ret);
if (isize > actual_end)
@@ -1439,6 +1438,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
struct inode *inode, u64 file_pos,
u64 disk_bytenr, u64 disk_num_bytes,
u64 num_bytes, u64 ram_bytes,
+ u64 locked_end,
u8 compression, u8 encryption,
u16 other_encoding, int extent_type)
{
@@ -1455,7 +1455,8 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
path->leave_spinning = 1;
ret = btrfs_drop_extents(trans, root, inode, file_pos,
- file_pos + num_bytes, file_pos, &hint);
+ file_pos + num_bytes, locked_end,
+ file_pos, &hint);
BUG_ON(ret);
ins.objectid = inode->i_ino;
@@ -1590,6 +1591,8 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
ordered_extent->disk_len,
ordered_extent->len,
ordered_extent->len,
+ ordered_extent->file_offset +
+ ordered_extent->len,
compressed, 0, 0,
BTRFS_FILE_EXTENT_REG);
BUG_ON(ret);
@@ -1819,10 +1822,12 @@ good:
return 0;
zeroit:
- printk(KERN_INFO "btrfs csum failed ino %lu off %llu csum %u "
- "private %llu\n", page->mapping->host->i_ino,
- (unsigned long long)start, csum,
- (unsigned long long)private);
+ if (printk_ratelimit()) {
+ printk(KERN_INFO "btrfs csum failed ino %lu off %llu csum %u "
+ "private %llu\n", page->mapping->host->i_ino,
+ (unsigned long long)start, csum,
+ (unsigned long long)private);
+ }
memset(kaddr + offset, 1, end - start + 1);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
@@ -2011,6 +2016,57 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
}
/*
+ * very simple check to peek ahead in the leaf looking for xattrs. If we
+ * don't find any xattrs, we know there can't be any acls.
+ *
+ * slot is the slot the inode is in, objectid is the objectid of the inode
+ */
+static noinline int acls_after_inode_item(struct extent_buffer *leaf,
+ int slot, u64 objectid)
+{
+ u32 nritems = btrfs_header_nritems(leaf);
+ struct btrfs_key found_key;
+ int scanned = 0;
+
+ slot++;
+ while (slot < nritems) {
+ btrfs_item_key_to_cpu(leaf, &found_key, slot);
+
+ /* we found a different objectid, there must not be acls */
+ if (found_key.objectid != objectid)
+ return 0;
+
+ /* we found an xattr, assume we've got an acl */
+ if (found_key.type == BTRFS_XATTR_ITEM_KEY)
+ return 1;
+
+ /*
+ * we found a key greater than an xattr key, there can't
+ * be any acls later on
+ */
+ if (found_key.type > BTRFS_XATTR_ITEM_KEY)
+ return 0;
+
+ slot++;
+ scanned++;
+
+ /*
+ * it goes inode, inode backrefs, xattrs, extents,
+ * so if there are a ton of hard links to an inode there can
+ * be a lot of backrefs. Don't waste time searching too hard,
+ * this is just an optimization
+ */
+ if (scanned >= 8)
+ break;
+ }
+ /* we hit the end of the leaf before we found an xattr or
+ * something larger than an xattr. We have to assume the inode
+ * has acls
+ */
+ return 1;
+}
+
+/*
* read an inode from the btree into the in-memory inode
*/
void btrfs_read_locked_inode(struct inode *inode)
@@ -2021,6 +2077,7 @@ void btrfs_read_locked_inode(struct inode *inode)
struct btrfs_timespec *tspec;
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_key location;
+ int maybe_acls;
u64 alloc_group_block;
u32 rdev;
int ret;
@@ -2067,6 +2124,16 @@ void btrfs_read_locked_inode(struct inode *inode)
alloc_group_block = btrfs_inode_block_group(leaf, inode_item);
+ /*
+ * try to precache a NULL acl entry for files that don't have
+ * any xattrs or acls
+ */
+ maybe_acls = acls_after_inode_item(leaf, path->slots[0], inode->i_ino);
+ if (!maybe_acls) {
+ BTRFS_I(inode)->i_acl = NULL;
+ BTRFS_I(inode)->i_default_acl = NULL;
+ }
+
BTRFS_I(inode)->block_group = btrfs_find_block_group(root, 0,
alloc_group_block, 0);
btrfs_free_path(path);
@@ -2877,6 +2944,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
err = btrfs_drop_extents(trans, root, inode,
cur_offset,
cur_offset + hole_size,
+ block_end,
cur_offset, &hint_byte);
if (err)
break;
@@ -3041,8 +3109,8 @@ static noinline void init_btrfs_i(struct inode *inode)
{
struct btrfs_inode *bi = BTRFS_I(inode);
- bi->i_acl = NULL;
- bi->i_default_acl = NULL;
+ bi->i_acl = BTRFS_ACL_NOT_CACHED;
+ bi->i_default_acl = BTRFS_ACL_NOT_CACHED;
bi->generation = 0;
bi->sequence = 0;
@@ -4634,47 +4702,36 @@ void btrfs_destroy_cachep(void)
kmem_cache_destroy(btrfs_trans_handle_cachep);
if (btrfs_transaction_cachep)
kmem_cache_destroy(btrfs_transaction_cachep);
- if (btrfs_bit_radix_cachep)
- kmem_cache_destroy(btrfs_bit_radix_cachep);
if (btrfs_path_cachep)
kmem_cache_destroy(btrfs_path_cachep);
}
-struct kmem_cache *btrfs_cache_create(const char *name, size_t size,
- unsigned long extra_flags,
- void (*ctor)(void *))
-{
- return kmem_cache_create(name, size, 0, (SLAB_RECLAIM_ACCOUNT |
- SLAB_MEM_SPREAD | extra_flags), ctor);
-}
-
int btrfs_init_cachep(void)
{
- btrfs_inode_cachep = btrfs_cache_create("btrfs_inode_cache",
- sizeof(struct btrfs_inode),
- 0, init_once);
+ btrfs_inode_cachep = kmem_cache_create("btrfs_inode_cache",
+ sizeof(struct btrfs_inode), 0,
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, init_once);
if (!btrfs_inode_cachep)
goto fail;
- btrfs_trans_handle_cachep =
- btrfs_cache_create("btrfs_trans_handle_cache",
- sizeof(struct btrfs_trans_handle),
- 0, NULL);
+
+ btrfs_trans_handle_cachep = kmem_cache_create("btrfs_trans_handle_cache",
+ sizeof(struct btrfs_trans_handle), 0,
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!btrfs_trans_handle_cachep)
goto fail;
- btrfs_transaction_cachep = btrfs_cache_create("btrfs_transaction_cache",
- sizeof(struct btrfs_transaction),
- 0, NULL);
+
+ btrfs_transaction_cachep = kmem_cache_create("btrfs_transaction_cache",
+ sizeof(struct btrfs_transaction), 0,
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!btrfs_transaction_cachep)
goto fail;
- btrfs_path_cachep = btrfs_cache_create("btrfs_path_cache",
- sizeof(struct btrfs_path),
- 0, NULL);
+
+ btrfs_path_cachep = kmem_cache_create("btrfs_path_cache",
+ sizeof(struct btrfs_path), 0,
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
if (!btrfs_path_cachep)
goto fail;
- btrfs_bit_radix_cachep = btrfs_cache_create("btrfs_radix", 256,
- SLAB_DESTROY_BY_RCU, NULL);
- if (!btrfs_bit_radix_cachep)
- goto fail;
+
return 0;
fail:
btrfs_destroy_cachep();
@@ -4972,7 +5029,7 @@ out_fail:
static int prealloc_file_range(struct btrfs_trans_handle *trans,
struct inode *inode, u64 start, u64 end,
- u64 alloc_hint, int mode)
+ u64 locked_end, u64 alloc_hint, int mode)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_key ins;
@@ -4993,7 +5050,8 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans,
ret = insert_reserved_file_extent(trans, inode,
cur_offset, ins.objectid,
ins.offset, ins.offset,
- ins.offset, 0, 0, 0,
+ ins.offset, locked_end,
+ 0, 0, 0,
BTRFS_FILE_EXTENT_PREALLOC);
BUG_ON(ret);
num_bytes -= ins.offset;
@@ -5022,6 +5080,7 @@ static long btrfs_fallocate(struct inode *inode, int mode,
u64 alloc_start;
u64 alloc_end;
u64 alloc_hint = 0;
+ u64 locked_end;
u64 mask = BTRFS_I(inode)->root->sectorsize - 1;
struct extent_map *em;
struct btrfs_trans_handle *trans;
@@ -5043,6 +5102,7 @@ static long btrfs_fallocate(struct inode *inode, int mode,
goto out;
}
+ locked_end = alloc_end - 1;
while (1) {
struct btrfs_ordered_extent *ordered;
@@ -5055,8 +5115,8 @@ static long btrfs_fallocate(struct inode *inode, int mode,
/* the extent lock is ordered inside the running
* transaction
*/
- lock_extent(&BTRFS_I(inode)->io_tree, alloc_start,
- alloc_end - 1, GFP_NOFS);
+ lock_extent(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
+ GFP_NOFS);
ordered = btrfs_lookup_first_ordered_extent(inode,
alloc_end - 1);
if (ordered &&
@@ -5064,7 +5124,7 @@ static long btrfs_fallocate(struct inode *inode, int mode,
ordered->file_offset < alloc_end) {
btrfs_put_ordered_extent(ordered);
unlock_extent(&BTRFS_I(inode)->io_tree,
- alloc_start, alloc_end - 1, GFP_NOFS);
+ alloc_start, locked_end, GFP_NOFS);
btrfs_end_transaction(trans, BTRFS_I(inode)->root);
/*
@@ -5089,7 +5149,8 @@ static long btrfs_fallocate(struct inode *inode, int mode,
last_byte = (last_byte + mask) & ~mask;
if (em->block_start == EXTENT_MAP_HOLE) {
ret = prealloc_file_range(trans, inode, cur_offset,
- last_byte, alloc_hint, mode);
+ last_byte, locked_end + 1,
+ alloc_hint, mode);
if (ret < 0) {
free_extent_map(em);
break;
@@ -5105,7 +5166,7 @@ static long btrfs_fallocate(struct inode *inode, int mode,
break;
}
}
- unlock_extent(&BTRFS_I(inode)->io_tree, alloc_start, alloc_end - 1,
+ unlock_extent(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
GFP_NOFS);
btrfs_end_transaction(trans, BTRFS_I(inode)->root);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 9f135e8..5e94ea6 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -477,11 +477,13 @@ static int btrfs_ioctl_resize(struct btrfs_root *root, void __user *arg)
*devstr = '\0';
devstr = vol_args->name;
devid = simple_strtoull(devstr, &end, 10);
- printk(KERN_INFO "resizing devid %llu\n", devid);
+ printk(KERN_INFO "resizing devid %llu\n",
+ (unsigned long long)devid);
}
device = btrfs_find_device(root, devid, NULL, NULL);
if (!device) {
- printk(KERN_INFO "resizer unable to find device %llu\n", devid);
+ printk(KERN_INFO "resizer unable to find device %llu\n",
+ (unsigned long long)devid);
ret = -EINVAL;
goto out_unlock;
}
@@ -805,7 +807,8 @@ static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
BUG_ON(!trans);
/* punch hole in destination first */
- btrfs_drop_extents(trans, root, inode, off, off+len, 0, &hint_byte);
+ btrfs_drop_extents(trans, root, inode, off, off + len,
+ off + len, 0, &hint_byte);
/* clone data */
key.objectid = src->i_ino;
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index a7acfe6..3536bdb 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -68,7 +68,7 @@ enum {
Opt_degraded, Opt_subvol, Opt_device, Opt_nodatasum, Opt_nodatacow,
Opt_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier,
Opt_ssd, Opt_thread_pool, Opt_noacl, Opt_compress, Opt_notreelog,
- Opt_flushoncommit, Opt_err,
+ Opt_ratio, Opt_flushoncommit, Opt_err,
};
static match_table_t tokens = {
@@ -87,6 +87,7 @@ static match_table_t tokens = {
{Opt_noacl, "noacl"},
{Opt_notreelog, "notreelog"},
{Opt_flushoncommit, "flushoncommit"},
+ {Opt_ratio, "metadata_ratio=%d"},
{Opt_err, NULL},
};
@@ -195,7 +196,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
info->max_extent = max_t(u64,
info->max_extent, root->sectorsize);
printk(KERN_INFO "btrfs: max_extent at %llu\n",
- info->max_extent);
+ (unsigned long long)info->max_extent);
}
break;
case Opt_max_inline:
@@ -210,7 +211,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
root->sectorsize);
}
printk(KERN_INFO "btrfs: max_inline at %llu\n",
- info->max_inline);
+ (unsigned long long)info->max_inline);
}
break;
case Opt_alloc_start:
@@ -220,7 +221,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
kfree(num);
printk(KERN_INFO
"btrfs: allocations start at %llu\n",
- info->alloc_start);
+ (unsigned long long)info->alloc_start);
}
break;
case Opt_noacl:
@@ -234,6 +235,15 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
printk(KERN_INFO "btrfs: turning on flush-on-commit\n");
btrfs_set_opt(info->mount_opt, FLUSHONCOMMIT);
break;
+ case Opt_ratio:
+ intarg = 0;
+ match_int(&args[0], &intarg);
+ if (intarg) {
+ info->metadata_ratio = intarg;
+ printk(KERN_INFO "btrfs: metadata ratio %d\n",
+ info->metadata_ratio);
+ }
+ break;
default:
break;
}
@@ -410,11 +420,14 @@ static int btrfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
if (btrfs_test_opt(root, NOBARRIER))
seq_puts(seq, ",nobarrier");
if (info->max_extent != (u64)-1)
- seq_printf(seq, ",max_extent=%llu", info->max_extent);
+ seq_printf(seq, ",max_extent=%llu",
+ (unsigned long long)info->max_extent);
if (info->max_inline != 8192 * 1024)
- seq_printf(seq, ",max_inline=%llu", info->max_inline);
+ seq_printf(seq, ",max_inline=%llu",
+ (unsigned long long)info->max_inline);
if (info->alloc_start != 0)
- seq_printf(seq, ",alloc_start=%llu", info->alloc_start);
+ seq_printf(seq, ",alloc_start=%llu",
+ (unsigned long long)info->alloc_start);
if (info->thread_pool_size != min_t(unsigned long,
num_online_cpus() + 2, 8))
seq_printf(seq, ",thread_pool=%d", info->thread_pool_size);
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 2869b33..01b1436 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -687,7 +687,13 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info)
prepare_to_wait(&info->transaction_wait, &wait,
TASK_UNINTERRUPTIBLE);
mutex_unlock(&info->trans_mutex);
+
+ atomic_dec(&info->throttles);
+ wake_up(&info->transaction_throttle);
+
schedule();
+
+ atomic_inc(&info->throttles);
mutex_lock(&info->trans_mutex);
finish_wait(&info->transaction_wait, &wait);
}
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 25f20ea..db5e212 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -536,7 +536,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
saved_nbytes = inode_get_bytes(inode);
/* drop any overlapping extents */
ret = btrfs_drop_extents(trans, root, inode,
- start, extent_end, start, &alloc_hint);
+ start, extent_end, extent_end, start, &alloc_hint);
BUG_ON(ret);
if (found_type == BTRFS_FILE_EXTENT_REG ||
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index e53835b..5f01dad 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -1543,7 +1543,7 @@ static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
btrfs_set_device_io_align(leaf, dev_item, device->io_align);
btrfs_set_device_io_width(leaf, dev_item, device->io_width);
btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
- btrfs_set_device_total_bytes(leaf, dev_item, device->total_bytes);
+ btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
btrfs_mark_buffer_dirty(leaf);
@@ -1940,14 +1940,6 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
device->total_bytes = new_size;
if (device->writeable)
device->fs_devices->total_rw_bytes -= diff;
- ret = btrfs_update_device(trans, device);
- if (ret) {
- unlock_chunks(root);
- btrfs_end_transaction(trans, root);
- goto done;
- }
- WARN_ON(diff > old_total);
- btrfs_set_super_total_bytes(super_copy, old_total - diff);
unlock_chunks(root);
btrfs_end_transaction(trans, root);
@@ -1979,7 +1971,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
length = btrfs_dev_extent_length(l, dev_extent);
if (key.offset + length <= new_size)
- goto done;
+ break;
chunk_tree = btrfs_dev_extent_chunk_tree(l, dev_extent);
chunk_objectid = btrfs_dev_extent_chunk_objectid(l, dev_extent);
@@ -1992,6 +1984,26 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
goto done;
}
+ /* Shrinking succeeded, else we would be at "done". */
+ trans = btrfs_start_transaction(root, 1);
+ if (!trans) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ lock_chunks(root);
+
+ device->disk_total_bytes = new_size;
+ /* Now btrfs_update_device() will change the on-disk size. */
+ ret = btrfs_update_device(trans, device);
+ if (ret) {
+ unlock_chunks(root);
+ btrfs_end_transaction(trans, root);
+ goto done;
+ }
+ WARN_ON(diff > old_total);
+ btrfs_set_super_total_bytes(super_copy, old_total - diff);
+ unlock_chunks(root);
+ btrfs_end_transaction(trans, root);
done:
btrfs_free_path(path);
return ret;
@@ -3076,7 +3088,8 @@ static int fill_device_from_item(struct extent_buffer *leaf,
unsigned long ptr;
device->devid = btrfs_device_id(leaf, dev_item);
- device->total_bytes = btrfs_device_total_bytes(leaf, dev_item);
+ device->disk_total_bytes = btrfs_device_total_bytes(leaf, dev_item);
+ device->total_bytes = device->disk_total_bytes;
device->bytes_used = btrfs_device_bytes_used(leaf, dev_item);
device->type = btrfs_device_type(leaf, dev_item);
device->io_align = btrfs_device_io_align(leaf, dev_item);
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 5836327..5c3ff6d 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -61,6 +61,9 @@ struct btrfs_device {
/* size of the device */
u64 total_bytes;
+ /* size of the disk */
+ u64 disk_total_bytes;
+
/* bytes used */
u64 bytes_used;