Btrfs: Cache free inode numbers in memory
Currently btrfs stores the highest objectid of the fs tree, and it always returns (highest+1) inode number when we create a file, so inode numbers won't be reclaimed when we delete files, so we'll run out of inode numbers as we keep create/delete files in 32bits machines. This fixes it, and it works similarly to how we cache free space in block cgroups. We start a kernel thread to read the file tree. By scanning inode items, we know which chunks of inode numbers are free, and we cache them in an rb-tree. Because we are searching the commit root, we have to carefully handle the cross-transaction case. The rb-tree is a hybrid extent+bitmap tree, so if we have too many small chunks of inode numbers, we'll use bitmaps. Initially we allow 16K ram of extents, and a bitmap will be used if we exceed this threshold. The extents threshold is adjusted in runtime. Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
This commit is contained in:
parent
34d52cb6c5
commit
581bb05094
@ -1102,6 +1102,15 @@ struct btrfs_root {
|
|||||||
spinlock_t accounting_lock;
|
spinlock_t accounting_lock;
|
||||||
struct btrfs_block_rsv *block_rsv;
|
struct btrfs_block_rsv *block_rsv;
|
||||||
|
|
||||||
|
/* free ino cache stuff */
|
||||||
|
struct mutex fs_commit_mutex;
|
||||||
|
struct btrfs_free_space_ctl *free_ino_ctl;
|
||||||
|
enum btrfs_caching_type cached;
|
||||||
|
spinlock_t cache_lock;
|
||||||
|
wait_queue_head_t cache_wait;
|
||||||
|
struct btrfs_free_space_ctl *free_ino_pinned;
|
||||||
|
u64 cache_progress;
|
||||||
|
|
||||||
struct mutex log_mutex;
|
struct mutex log_mutex;
|
||||||
wait_queue_head_t log_writer_wait;
|
wait_queue_head_t log_writer_wait;
|
||||||
wait_queue_head_t log_commit_wait[2];
|
wait_queue_head_t log_commit_wait[2];
|
||||||
@ -2408,12 +2417,6 @@ int btrfs_del_orphan_item(struct btrfs_trans_handle *trans,
|
|||||||
struct btrfs_root *root, u64 offset);
|
struct btrfs_root *root, u64 offset);
|
||||||
int btrfs_find_orphan_item(struct btrfs_root *root, u64 offset);
|
int btrfs_find_orphan_item(struct btrfs_root *root, u64 offset);
|
||||||
|
|
||||||
/* inode-map.c */
|
|
||||||
int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
|
|
||||||
struct btrfs_root *fs_root,
|
|
||||||
u64 dirid, u64 *objectid);
|
|
||||||
int btrfs_find_highest_inode(struct btrfs_root *fs_root, u64 *objectid);
|
|
||||||
|
|
||||||
/* inode-item.c */
|
/* inode-item.c */
|
||||||
int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
|
int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root,
|
struct btrfs_root *root,
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "locking.h"
|
#include "locking.h"
|
||||||
#include "tree-log.h"
|
#include "tree-log.h"
|
||||||
#include "free-space-cache.h"
|
#include "free-space-cache.h"
|
||||||
|
#include "inode-map.h"
|
||||||
|
|
||||||
static struct extent_io_ops btree_extent_io_ops;
|
static struct extent_io_ops btree_extent_io_ops;
|
||||||
static void end_workqueue_fn(struct btrfs_work *work);
|
static void end_workqueue_fn(struct btrfs_work *work);
|
||||||
@ -1327,6 +1328,19 @@ again:
|
|||||||
if (IS_ERR(root))
|
if (IS_ERR(root))
|
||||||
return root;
|
return root;
|
||||||
|
|
||||||
|
root->free_ino_ctl = kzalloc(sizeof(*root->free_ino_ctl), GFP_NOFS);
|
||||||
|
if (!root->free_ino_ctl)
|
||||||
|
goto fail;
|
||||||
|
root->free_ino_pinned = kzalloc(sizeof(*root->free_ino_pinned),
|
||||||
|
GFP_NOFS);
|
||||||
|
if (!root->free_ino_pinned)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
btrfs_init_free_ino_ctl(root);
|
||||||
|
mutex_init(&root->fs_commit_mutex);
|
||||||
|
spin_lock_init(&root->cache_lock);
|
||||||
|
init_waitqueue_head(&root->cache_wait);
|
||||||
|
|
||||||
set_anon_super(&root->anon_super, NULL);
|
set_anon_super(&root->anon_super, NULL);
|
||||||
|
|
||||||
if (btrfs_root_refs(&root->root_item) == 0) {
|
if (btrfs_root_refs(&root->root_item) == 0) {
|
||||||
@ -2483,6 +2497,8 @@ int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
|
|||||||
if (btrfs_root_refs(&root->root_item) == 0)
|
if (btrfs_root_refs(&root->root_item) == 0)
|
||||||
synchronize_srcu(&fs_info->subvol_srcu);
|
synchronize_srcu(&fs_info->subvol_srcu);
|
||||||
|
|
||||||
|
__btrfs_remove_free_space_cache(root->free_ino_pinned);
|
||||||
|
__btrfs_remove_free_space_cache(root->free_ino_ctl);
|
||||||
free_fs_root(root);
|
free_fs_root(root);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2496,6 +2512,8 @@ static void free_fs_root(struct btrfs_root *root)
|
|||||||
}
|
}
|
||||||
free_extent_buffer(root->node);
|
free_extent_buffer(root->node);
|
||||||
free_extent_buffer(root->commit_root);
|
free_extent_buffer(root->commit_root);
|
||||||
|
kfree(root->free_ino_ctl);
|
||||||
|
kfree(root->free_ino_pinned);
|
||||||
kfree(root->name);
|
kfree(root->name);
|
||||||
kfree(root);
|
kfree(root);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
#include "extent_io.h"
|
#include "extent_io.h"
|
||||||
|
#include "inode-map.h"
|
||||||
|
|
||||||
#define BITS_PER_BITMAP (PAGE_CACHE_SIZE * 8)
|
#define BITS_PER_BITMAP (PAGE_CACHE_SIZE * 8)
|
||||||
#define MAX_CACHE_BYTES_PER_GIG (32 * 1024)
|
#define MAX_CACHE_BYTES_PER_GIG (32 * 1024)
|
||||||
@ -105,7 +106,7 @@ int create_free_space_inode(struct btrfs_root *root,
|
|||||||
u64 objectid;
|
u64 objectid;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = btrfs_find_free_objectid(trans, root, 0, &objectid);
|
ret = btrfs_find_free_objectid(root, &objectid);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1496,10 +1497,9 @@ bool try_merge_free_space(struct btrfs_free_space_ctl *ctl,
|
|||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
int btrfs_add_free_space(struct btrfs_block_group_cache *block_group,
|
int __btrfs_add_free_space(struct btrfs_free_space_ctl *ctl,
|
||||||
u64 offset, u64 bytes)
|
u64 offset, u64 bytes)
|
||||||
{
|
{
|
||||||
struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
|
|
||||||
struct btrfs_free_space *info;
|
struct btrfs_free_space *info;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -1751,11 +1751,29 @@ out:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __btrfs_remove_free_space_cache(struct btrfs_free_space_ctl *ctl)
|
||||||
|
{
|
||||||
|
struct btrfs_free_space *info;
|
||||||
|
struct rb_node *node;
|
||||||
|
|
||||||
|
spin_lock(&ctl->tree_lock);
|
||||||
|
while ((node = rb_last(&ctl->free_space_offset)) != NULL) {
|
||||||
|
info = rb_entry(node, struct btrfs_free_space, offset_index);
|
||||||
|
unlink_free_space(ctl, info);
|
||||||
|
kfree(info->bitmap);
|
||||||
|
kmem_cache_free(btrfs_free_space_cachep, info);
|
||||||
|
if (need_resched()) {
|
||||||
|
spin_unlock(&ctl->tree_lock);
|
||||||
|
cond_resched();
|
||||||
|
spin_lock(&ctl->tree_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&ctl->tree_lock);
|
||||||
|
}
|
||||||
|
|
||||||
void btrfs_remove_free_space_cache(struct btrfs_block_group_cache *block_group)
|
void btrfs_remove_free_space_cache(struct btrfs_block_group_cache *block_group)
|
||||||
{
|
{
|
||||||
struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
|
struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
|
||||||
struct btrfs_free_space *info;
|
|
||||||
struct rb_node *node;
|
|
||||||
struct btrfs_free_cluster *cluster;
|
struct btrfs_free_cluster *cluster;
|
||||||
struct list_head *head;
|
struct list_head *head;
|
||||||
|
|
||||||
@ -1773,21 +1791,9 @@ void btrfs_remove_free_space_cache(struct btrfs_block_group_cache *block_group)
|
|||||||
spin_lock(&ctl->tree_lock);
|
spin_lock(&ctl->tree_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((node = rb_last(&ctl->free_space_offset)) != NULL) {
|
|
||||||
info = rb_entry(node, struct btrfs_free_space, offset_index);
|
|
||||||
unlink_free_space(ctl, info);
|
|
||||||
if (info->bitmap)
|
|
||||||
kfree(info->bitmap);
|
|
||||||
kmem_cache_free(btrfs_free_space_cachep, info);
|
|
||||||
if (need_resched()) {
|
|
||||||
spin_unlock(&ctl->tree_lock);
|
spin_unlock(&ctl->tree_lock);
|
||||||
cond_resched();
|
|
||||||
spin_lock(&ctl->tree_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&ctl->tree_lock);
|
__btrfs_remove_free_space_cache(ctl);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
|
u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
|
||||||
@ -2352,3 +2358,53 @@ int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group,
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the left-most item in the cache tree, and then return the
|
||||||
|
* smallest inode number in the item.
|
||||||
|
*
|
||||||
|
* Note: the returned inode number may not be the smallest one in
|
||||||
|
* the tree, if the left-most item is a bitmap.
|
||||||
|
*/
|
||||||
|
u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root)
|
||||||
|
{
|
||||||
|
struct btrfs_free_space_ctl *ctl = fs_root->free_ino_ctl;
|
||||||
|
struct btrfs_free_space *entry = NULL;
|
||||||
|
u64 ino = 0;
|
||||||
|
|
||||||
|
spin_lock(&ctl->tree_lock);
|
||||||
|
|
||||||
|
if (RB_EMPTY_ROOT(&ctl->free_space_offset))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
entry = rb_entry(rb_first(&ctl->free_space_offset),
|
||||||
|
struct btrfs_free_space, offset_index);
|
||||||
|
|
||||||
|
if (!entry->bitmap) {
|
||||||
|
ino = entry->offset;
|
||||||
|
|
||||||
|
unlink_free_space(ctl, entry);
|
||||||
|
entry->offset++;
|
||||||
|
entry->bytes--;
|
||||||
|
if (!entry->bytes)
|
||||||
|
kmem_cache_free(btrfs_free_space_cachep, entry);
|
||||||
|
else
|
||||||
|
link_free_space(ctl, entry);
|
||||||
|
} else {
|
||||||
|
u64 offset = 0;
|
||||||
|
u64 count = 1;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = search_bitmap(ctl, entry, &offset, &count);
|
||||||
|
BUG_ON(ret);
|
||||||
|
|
||||||
|
ino = offset;
|
||||||
|
bitmap_clear_bits(ctl, entry, offset, 1);
|
||||||
|
if (entry->bytes == 0)
|
||||||
|
free_bitmap(ctl, entry);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
spin_unlock(&ctl->tree_lock);
|
||||||
|
|
||||||
|
return ino;
|
||||||
|
}
|
||||||
|
@ -64,15 +64,25 @@ int btrfs_write_out_cache(struct btrfs_root *root,
|
|||||||
struct btrfs_trans_handle *trans,
|
struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_block_group_cache *block_group,
|
struct btrfs_block_group_cache *block_group,
|
||||||
struct btrfs_path *path);
|
struct btrfs_path *path);
|
||||||
|
|
||||||
void btrfs_init_free_space_ctl(struct btrfs_block_group_cache *block_group);
|
void btrfs_init_free_space_ctl(struct btrfs_block_group_cache *block_group);
|
||||||
int btrfs_add_free_space(struct btrfs_block_group_cache *block_group,
|
int __btrfs_add_free_space(struct btrfs_free_space_ctl *ctl,
|
||||||
u64 bytenr, u64 size);
|
u64 bytenr, u64 size);
|
||||||
|
static inline int
|
||||||
|
btrfs_add_free_space(struct btrfs_block_group_cache *block_group,
|
||||||
|
u64 bytenr, u64 size)
|
||||||
|
{
|
||||||
|
return __btrfs_add_free_space(block_group->free_space_ctl,
|
||||||
|
bytenr, size);
|
||||||
|
}
|
||||||
int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
|
int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
|
||||||
u64 bytenr, u64 size);
|
u64 bytenr, u64 size);
|
||||||
|
void __btrfs_remove_free_space_cache(struct btrfs_free_space_ctl *ctl);
|
||||||
void btrfs_remove_free_space_cache(struct btrfs_block_group_cache
|
void btrfs_remove_free_space_cache(struct btrfs_block_group_cache
|
||||||
*block_group);
|
*block_group);
|
||||||
u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
|
u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
|
||||||
u64 offset, u64 bytes, u64 empty_size);
|
u64 offset, u64 bytes, u64 empty_size);
|
||||||
|
u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root);
|
||||||
void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group,
|
void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group,
|
||||||
u64 bytes);
|
u64 bytes);
|
||||||
int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
|
int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
|
||||||
|
@ -16,11 +16,343 @@
|
|||||||
* Boston, MA 021110-1307, USA.
|
* Boston, MA 021110-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/kthread.h>
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
|
||||||
#include "ctree.h"
|
#include "ctree.h"
|
||||||
#include "disk-io.h"
|
#include "disk-io.h"
|
||||||
|
#include "free-space-cache.h"
|
||||||
|
#include "inode-map.h"
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
|
||||||
int btrfs_find_highest_inode(struct btrfs_root *root, u64 *objectid)
|
static int caching_kthread(void *data)
|
||||||
|
{
|
||||||
|
struct btrfs_root *root = data;
|
||||||
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||||
|
struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
|
||||||
|
struct btrfs_key key;
|
||||||
|
struct btrfs_path *path;
|
||||||
|
struct extent_buffer *leaf;
|
||||||
|
u64 last = (u64)-1;
|
||||||
|
int slot;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
path = btrfs_alloc_path();
|
||||||
|
if (!path)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Since the commit root is read-only, we can safely skip locking. */
|
||||||
|
path->skip_locking = 1;
|
||||||
|
path->search_commit_root = 1;
|
||||||
|
path->reada = 2;
|
||||||
|
|
||||||
|
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
|
||||||
|
key.offset = 0;
|
||||||
|
key.type = BTRFS_INODE_ITEM_KEY;
|
||||||
|
again:
|
||||||
|
/* need to make sure the commit_root doesn't disappear */
|
||||||
|
mutex_lock(&root->fs_commit_mutex);
|
||||||
|
|
||||||
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
smp_mb();
|
||||||
|
if (fs_info->closing > 1)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
leaf = path->nodes[0];
|
||||||
|
slot = path->slots[0];
|
||||||
|
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
|
||||||
|
ret = btrfs_next_leaf(root, path);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
else if (ret > 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (need_resched() ||
|
||||||
|
btrfs_transaction_in_commit(fs_info)) {
|
||||||
|
leaf = path->nodes[0];
|
||||||
|
|
||||||
|
if (btrfs_header_nritems(leaf) == 0) {
|
||||||
|
WARN_ON(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the key so we can advances forward
|
||||||
|
* in the next search.
|
||||||
|
*/
|
||||||
|
btrfs_item_key_to_cpu(leaf, &key, 0);
|
||||||
|
btrfs_release_path(root, path);
|
||||||
|
root->cache_progress = last;
|
||||||
|
mutex_unlock(&root->fs_commit_mutex);
|
||||||
|
schedule_timeout(1);
|
||||||
|
goto again;
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
btrfs_item_key_to_cpu(leaf, &key, slot);
|
||||||
|
|
||||||
|
if (key.type != BTRFS_INODE_ITEM_KEY)
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
if (key.objectid >= BTRFS_LAST_FREE_OBJECTID)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (last != (u64)-1 && last + 1 != key.objectid) {
|
||||||
|
__btrfs_add_free_space(ctl, last + 1,
|
||||||
|
key.objectid - last - 1);
|
||||||
|
wake_up(&root->cache_wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
last = key.objectid;
|
||||||
|
next:
|
||||||
|
path->slots[0]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last < BTRFS_LAST_FREE_OBJECTID - 1) {
|
||||||
|
__btrfs_add_free_space(ctl, last + 1,
|
||||||
|
BTRFS_LAST_FREE_OBJECTID - last - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&root->cache_lock);
|
||||||
|
root->cached = BTRFS_CACHE_FINISHED;
|
||||||
|
spin_unlock(&root->cache_lock);
|
||||||
|
|
||||||
|
root->cache_progress = (u64)-1;
|
||||||
|
btrfs_unpin_free_ino(root);
|
||||||
|
out:
|
||||||
|
wake_up(&root->cache_wait);
|
||||||
|
mutex_unlock(&root->fs_commit_mutex);
|
||||||
|
|
||||||
|
btrfs_free_path(path);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_caching(struct btrfs_root *root)
|
||||||
|
{
|
||||||
|
struct task_struct *tsk;
|
||||||
|
|
||||||
|
spin_lock(&root->cache_lock);
|
||||||
|
if (root->cached != BTRFS_CACHE_NO) {
|
||||||
|
spin_unlock(&root->cache_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root->cached = BTRFS_CACHE_STARTED;
|
||||||
|
spin_unlock(&root->cache_lock);
|
||||||
|
|
||||||
|
tsk = kthread_run(caching_kthread, root, "btrfs-ino-cache-%llu\n",
|
||||||
|
root->root_key.objectid);
|
||||||
|
BUG_ON(IS_ERR(tsk));
|
||||||
|
}
|
||||||
|
|
||||||
|
int btrfs_find_free_ino(struct btrfs_root *root, u64 *objectid)
|
||||||
|
{
|
||||||
|
again:
|
||||||
|
*objectid = btrfs_find_ino_for_alloc(root);
|
||||||
|
|
||||||
|
if (*objectid != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
start_caching(root);
|
||||||
|
|
||||||
|
wait_event(root->cache_wait,
|
||||||
|
root->cached == BTRFS_CACHE_FINISHED ||
|
||||||
|
root->free_ino_ctl->free_space > 0);
|
||||||
|
|
||||||
|
if (root->cached == BTRFS_CACHE_FINISHED &&
|
||||||
|
root->free_ino_ctl->free_space == 0)
|
||||||
|
return -ENOSPC;
|
||||||
|
else
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
void btrfs_return_ino(struct btrfs_root *root, u64 objectid)
|
||||||
|
{
|
||||||
|
struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
|
||||||
|
struct btrfs_free_space_ctl *pinned = root->free_ino_pinned;
|
||||||
|
again:
|
||||||
|
if (root->cached == BTRFS_CACHE_FINISHED) {
|
||||||
|
__btrfs_add_free_space(ctl, objectid, 1);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* If we are in the process of caching free ino chunks,
|
||||||
|
* to avoid adding the same inode number to the free_ino
|
||||||
|
* tree twice due to cross transaction, we'll leave it
|
||||||
|
* in the pinned tree until a transaction is committed
|
||||||
|
* or the caching work is done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mutex_lock(&root->fs_commit_mutex);
|
||||||
|
spin_lock(&root->cache_lock);
|
||||||
|
if (root->cached == BTRFS_CACHE_FINISHED) {
|
||||||
|
spin_unlock(&root->cache_lock);
|
||||||
|
mutex_unlock(&root->fs_commit_mutex);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
spin_unlock(&root->cache_lock);
|
||||||
|
|
||||||
|
start_caching(root);
|
||||||
|
|
||||||
|
if (objectid <= root->cache_progress)
|
||||||
|
__btrfs_add_free_space(ctl, objectid, 1);
|
||||||
|
else
|
||||||
|
__btrfs_add_free_space(pinned, objectid, 1);
|
||||||
|
|
||||||
|
mutex_unlock(&root->fs_commit_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When a transaction is committed, we'll move those inode numbers which
|
||||||
|
* are smaller than root->cache_progress from pinned tree to free_ino tree,
|
||||||
|
* and others will just be dropped, because the commit root we were
|
||||||
|
* searching has changed.
|
||||||
|
*
|
||||||
|
* Must be called with root->fs_commit_mutex held
|
||||||
|
*/
|
||||||
|
void btrfs_unpin_free_ino(struct btrfs_root *root)
|
||||||
|
{
|
||||||
|
struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
|
||||||
|
struct rb_root *rbroot = &root->free_ino_pinned->free_space_offset;
|
||||||
|
struct btrfs_free_space *info;
|
||||||
|
struct rb_node *n;
|
||||||
|
u64 count;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
n = rb_first(rbroot);
|
||||||
|
if (!n)
|
||||||
|
break;
|
||||||
|
|
||||||
|
info = rb_entry(n, struct btrfs_free_space, offset_index);
|
||||||
|
BUG_ON(info->bitmap);
|
||||||
|
|
||||||
|
if (info->offset > root->cache_progress)
|
||||||
|
goto free;
|
||||||
|
else if (info->offset + info->bytes > root->cache_progress)
|
||||||
|
count = root->cache_progress - info->offset + 1;
|
||||||
|
else
|
||||||
|
count = info->bytes;
|
||||||
|
|
||||||
|
__btrfs_add_free_space(ctl, info->offset, count);
|
||||||
|
free:
|
||||||
|
rb_erase(&info->offset_index, rbroot);
|
||||||
|
kfree(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INIT_THRESHOLD (((1024 * 32) / 2) / sizeof(struct btrfs_free_space))
|
||||||
|
#define INODES_PER_BITMAP (PAGE_CACHE_SIZE * 8)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The goal is to keep the memory used by the free_ino tree won't
|
||||||
|
* exceed the memory if we use bitmaps only.
|
||||||
|
*/
|
||||||
|
static void recalculate_thresholds(struct btrfs_free_space_ctl *ctl)
|
||||||
|
{
|
||||||
|
struct btrfs_free_space *info;
|
||||||
|
struct rb_node *n;
|
||||||
|
int max_ino;
|
||||||
|
int max_bitmaps;
|
||||||
|
|
||||||
|
n = rb_last(&ctl->free_space_offset);
|
||||||
|
if (!n) {
|
||||||
|
ctl->extents_thresh = INIT_THRESHOLD;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info = rb_entry(n, struct btrfs_free_space, offset_index);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the maximum inode number in the filesystem. Note we
|
||||||
|
* ignore the fact that this can be a bitmap, because we are
|
||||||
|
* not doing precise calculation.
|
||||||
|
*/
|
||||||
|
max_ino = info->bytes - 1;
|
||||||
|
|
||||||
|
max_bitmaps = ALIGN(max_ino, INODES_PER_BITMAP) / INODES_PER_BITMAP;
|
||||||
|
if (max_bitmaps <= ctl->total_bitmaps) {
|
||||||
|
ctl->extents_thresh = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctl->extents_thresh = (max_bitmaps - ctl->total_bitmaps) *
|
||||||
|
PAGE_CACHE_SIZE / sizeof(*info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't fall back to bitmap, if we are below the extents threshold
|
||||||
|
* or this chunk of inode numbers is a big one.
|
||||||
|
*/
|
||||||
|
static bool use_bitmap(struct btrfs_free_space_ctl *ctl,
|
||||||
|
struct btrfs_free_space *info)
|
||||||
|
{
|
||||||
|
if (ctl->free_extents < ctl->extents_thresh ||
|
||||||
|
info->bytes > INODES_PER_BITMAP / 10)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct btrfs_free_space_op free_ino_op = {
|
||||||
|
.recalc_thresholds = recalculate_thresholds,
|
||||||
|
.use_bitmap = use_bitmap,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pinned_recalc_thresholds(struct btrfs_free_space_ctl *ctl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pinned_use_bitmap(struct btrfs_free_space_ctl *ctl,
|
||||||
|
struct btrfs_free_space *info)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We always use extents for two reasons:
|
||||||
|
*
|
||||||
|
* - The pinned tree is only used during the process of caching
|
||||||
|
* work.
|
||||||
|
* - Make code simpler. See btrfs_unpin_free_ino().
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct btrfs_free_space_op pinned_free_ino_op = {
|
||||||
|
.recalc_thresholds = pinned_recalc_thresholds,
|
||||||
|
.use_bitmap = pinned_use_bitmap,
|
||||||
|
};
|
||||||
|
|
||||||
|
void btrfs_init_free_ino_ctl(struct btrfs_root *root)
|
||||||
|
{
|
||||||
|
struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
|
||||||
|
struct btrfs_free_space_ctl *pinned = root->free_ino_pinned;
|
||||||
|
|
||||||
|
spin_lock_init(&ctl->tree_lock);
|
||||||
|
ctl->unit = 1;
|
||||||
|
ctl->start = 0;
|
||||||
|
ctl->private = NULL;
|
||||||
|
ctl->op = &free_ino_op;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initially we allow to use 16K of ram to cache chunks of
|
||||||
|
* inode numbers before we resort to bitmaps. This is somewhat
|
||||||
|
* arbitrary, but it will be adjusted in runtime.
|
||||||
|
*/
|
||||||
|
ctl->extents_thresh = INIT_THRESHOLD;
|
||||||
|
|
||||||
|
spin_lock_init(&pinned->tree_lock);
|
||||||
|
pinned->unit = 1;
|
||||||
|
pinned->start = 0;
|
||||||
|
pinned->private = NULL;
|
||||||
|
pinned->extents_thresh = 0;
|
||||||
|
pinned->op = &pinned_free_ino_op;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btrfs_find_highest_objectid(struct btrfs_root *root, u64 *objectid)
|
||||||
{
|
{
|
||||||
struct btrfs_path *path;
|
struct btrfs_path *path;
|
||||||
int ret;
|
int ret;
|
||||||
@ -55,15 +387,14 @@ error:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
|
int btrfs_find_free_objectid(struct btrfs_root *root, u64 *objectid)
|
||||||
struct btrfs_root *root,
|
|
||||||
u64 dirid, u64 *objectid)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
mutex_lock(&root->objectid_mutex);
|
mutex_lock(&root->objectid_mutex);
|
||||||
|
|
||||||
if (unlikely(root->highest_objectid < BTRFS_FIRST_FREE_OBJECTID)) {
|
if (unlikely(root->highest_objectid < BTRFS_FIRST_FREE_OBJECTID)) {
|
||||||
ret = btrfs_find_highest_inode(root, &root->highest_objectid);
|
ret = btrfs_find_highest_objectid(root,
|
||||||
|
&root->highest_objectid);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
11
fs/btrfs/inode-map.h
Normal file
11
fs/btrfs/inode-map.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef __BTRFS_INODE_MAP
|
||||||
|
#define __BTRFS_INODE_MAP
|
||||||
|
|
||||||
|
void btrfs_init_free_ino_ctl(struct btrfs_root *root);
|
||||||
|
void btrfs_unpin_free_ino(struct btrfs_root *root);
|
||||||
|
void btrfs_return_ino(struct btrfs_root *root, u64 objectid);
|
||||||
|
int btrfs_find_free_ino(struct btrfs_root *root, u64 *objectid);
|
||||||
|
|
||||||
|
int btrfs_find_free_objectid(struct btrfs_root *root, u64 *objectid);
|
||||||
|
|
||||||
|
#endif
|
@ -51,6 +51,7 @@
|
|||||||
#include "compression.h"
|
#include "compression.h"
|
||||||
#include "locking.h"
|
#include "locking.h"
|
||||||
#include "free-space-cache.h"
|
#include "free-space-cache.h"
|
||||||
|
#include "inode-map.h"
|
||||||
|
|
||||||
struct btrfs_iget_args {
|
struct btrfs_iget_args {
|
||||||
u64 ino;
|
u64 ino;
|
||||||
@ -3809,6 +3810,10 @@ void btrfs_evict_inode(struct inode *inode)
|
|||||||
BUG_ON(ret);
|
BUG_ON(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(root == root->fs_info->tree_root ||
|
||||||
|
root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID))
|
||||||
|
btrfs_return_ino(root, inode->i_ino);
|
||||||
|
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction(trans, root);
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
@ -4538,6 +4543,12 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we have to initialize this early, so we can reclaim the inode
|
||||||
|
* number if we fail afterwards in this function.
|
||||||
|
*/
|
||||||
|
inode->i_ino = objectid;
|
||||||
|
|
||||||
if (dir) {
|
if (dir) {
|
||||||
trace_btrfs_inode_request(dir);
|
trace_btrfs_inode_request(dir);
|
||||||
|
|
||||||
@ -4583,7 +4594,6 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
inode_init_owner(inode, dir, mode);
|
inode_init_owner(inode, dir, mode);
|
||||||
inode->i_ino = objectid;
|
|
||||||
inode_set_bytes(inode, 0);
|
inode_set_bytes(inode, 0);
|
||||||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
||||||
inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
||||||
@ -4712,10 +4722,6 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
|
|||||||
if (!new_valid_dev(rdev))
|
if (!new_valid_dev(rdev))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
err = btrfs_find_free_objectid(NULL, root, dir->i_ino, &objectid);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 2 for inode item and ref
|
* 2 for inode item and ref
|
||||||
* 2 for dir items
|
* 2 for dir items
|
||||||
@ -4727,6 +4733,10 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
|
|||||||
|
|
||||||
btrfs_set_trans_block_group(trans, dir);
|
btrfs_set_trans_block_group(trans, dir);
|
||||||
|
|
||||||
|
err = btrfs_find_free_ino(root, &objectid);
|
||||||
|
if (err)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
||||||
dentry->d_name.len, dir->i_ino, objectid,
|
dentry->d_name.len, dir->i_ino, objectid,
|
||||||
BTRFS_I(dir)->block_group, mode, &index);
|
BTRFS_I(dir)->block_group, mode, &index);
|
||||||
@ -4774,9 +4784,6 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
|||||||
u64 objectid;
|
u64 objectid;
|
||||||
u64 index = 0;
|
u64 index = 0;
|
||||||
|
|
||||||
err = btrfs_find_free_objectid(NULL, root, dir->i_ino, &objectid);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
/*
|
/*
|
||||||
* 2 for inode item and ref
|
* 2 for inode item and ref
|
||||||
* 2 for dir items
|
* 2 for dir items
|
||||||
@ -4788,6 +4795,10 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
|||||||
|
|
||||||
btrfs_set_trans_block_group(trans, dir);
|
btrfs_set_trans_block_group(trans, dir);
|
||||||
|
|
||||||
|
err = btrfs_find_free_ino(root, &objectid);
|
||||||
|
if (err)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
||||||
dentry->d_name.len, dir->i_ino, objectid,
|
dentry->d_name.len, dir->i_ino, objectid,
|
||||||
BTRFS_I(dir)->block_group, mode, &index);
|
BTRFS_I(dir)->block_group, mode, &index);
|
||||||
@ -4902,10 +4913,6 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|||||||
u64 index = 0;
|
u64 index = 0;
|
||||||
unsigned long nr = 1;
|
unsigned long nr = 1;
|
||||||
|
|
||||||
err = btrfs_find_free_objectid(NULL, root, dir->i_ino, &objectid);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 2 items for inode and ref
|
* 2 items for inode and ref
|
||||||
* 2 items for dir items
|
* 2 items for dir items
|
||||||
@ -4916,6 +4923,10 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|||||||
return PTR_ERR(trans);
|
return PTR_ERR(trans);
|
||||||
btrfs_set_trans_block_group(trans, dir);
|
btrfs_set_trans_block_group(trans, dir);
|
||||||
|
|
||||||
|
err = btrfs_find_free_ino(root, &objectid);
|
||||||
|
if (err)
|
||||||
|
goto out_fail;
|
||||||
|
|
||||||
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
||||||
dentry->d_name.len, dir->i_ino, objectid,
|
dentry->d_name.len, dir->i_ino, objectid,
|
||||||
BTRFS_I(dir)->block_group, S_IFDIR | mode,
|
BTRFS_I(dir)->block_group, S_IFDIR | mode,
|
||||||
@ -7257,9 +7268,6 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
|||||||
if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(root))
|
if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(root))
|
||||||
return -ENAMETOOLONG;
|
return -ENAMETOOLONG;
|
||||||
|
|
||||||
err = btrfs_find_free_objectid(NULL, root, dir->i_ino, &objectid);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
/*
|
/*
|
||||||
* 2 items for inode item and ref
|
* 2 items for inode item and ref
|
||||||
* 2 items for dir items
|
* 2 items for dir items
|
||||||
@ -7271,6 +7279,10 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
|||||||
|
|
||||||
btrfs_set_trans_block_group(trans, dir);
|
btrfs_set_trans_block_group(trans, dir);
|
||||||
|
|
||||||
|
err = btrfs_find_free_ino(root, &objectid);
|
||||||
|
if (err)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
||||||
dentry->d_name.len, dir->i_ino, objectid,
|
dentry->d_name.len, dir->i_ino, objectid,
|
||||||
BTRFS_I(dir)->block_group, S_IFLNK|S_IRWXUGO,
|
BTRFS_I(dir)->block_group, S_IFLNK|S_IRWXUGO,
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include "print-tree.h"
|
#include "print-tree.h"
|
||||||
#include "volumes.h"
|
#include "volumes.h"
|
||||||
#include "locking.h"
|
#include "locking.h"
|
||||||
|
#include "inode-map.h"
|
||||||
|
|
||||||
/* Mask out flags that are inappropriate for the given type of inode. */
|
/* Mask out flags that are inappropriate for the given type of inode. */
|
||||||
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
|
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
|
||||||
@ -323,8 +324,7 @@ static noinline int create_subvol(struct btrfs_root *root,
|
|||||||
u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
|
u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
|
||||||
u64 index = 0;
|
u64 index = 0;
|
||||||
|
|
||||||
ret = btrfs_find_free_objectid(NULL, root->fs_info->tree_root,
|
ret = btrfs_find_free_objectid(root->fs_info->tree_root, &objectid);
|
||||||
0, &objectid);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dput(parent);
|
dput(parent);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "btrfs_inode.h"
|
#include "btrfs_inode.h"
|
||||||
#include "async-thread.h"
|
#include "async-thread.h"
|
||||||
#include "free-space-cache.h"
|
#include "free-space-cache.h"
|
||||||
|
#include "inode-map.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* backref_node, mapping_node and tree_block start with this
|
* backref_node, mapping_node and tree_block start with this
|
||||||
@ -3897,7 +3898,7 @@ struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info,
|
|||||||
if (IS_ERR(trans))
|
if (IS_ERR(trans))
|
||||||
return ERR_CAST(trans);
|
return ERR_CAST(trans);
|
||||||
|
|
||||||
err = btrfs_find_free_objectid(trans, root, objectid, &objectid);
|
err = btrfs_find_free_objectid(root, &objectid);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
#include "locking.h"
|
#include "locking.h"
|
||||||
#include "tree-log.h"
|
#include "tree-log.h"
|
||||||
|
#include "inode-map.h"
|
||||||
|
|
||||||
#define BTRFS_ROOT_TRANS_TAG 0
|
#define BTRFS_ROOT_TRANS_TAG 0
|
||||||
|
|
||||||
@ -761,7 +762,11 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
|
|||||||
btrfs_orphan_commit_root(trans, root);
|
btrfs_orphan_commit_root(trans, root);
|
||||||
|
|
||||||
if (root->commit_root != root->node) {
|
if (root->commit_root != root->node) {
|
||||||
|
mutex_lock(&root->fs_commit_mutex);
|
||||||
switch_commit_root(root);
|
switch_commit_root(root);
|
||||||
|
btrfs_unpin_free_ino(root);
|
||||||
|
mutex_unlock(&root->fs_commit_mutex);
|
||||||
|
|
||||||
btrfs_set_root_node(&root->root_item,
|
btrfs_set_root_node(&root->root_item,
|
||||||
root->node);
|
root->node);
|
||||||
}
|
}
|
||||||
@ -930,7 +935,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = btrfs_find_free_objectid(trans, tree_root, 0, &objectid);
|
ret = btrfs_find_free_objectid(tree_root, &objectid);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pending->error = ret;
|
pending->error = ret;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
Loading…
Reference in New Issue
Block a user