Skip to content

Commit 82d5902

Browse files
author
Li Zefan
committed
Btrfs: Support reading/writing on disk free ino cache
This is similar to block group caching. We dedicate a special inode in fs tree to save free ino cache. At the very first time we create/delete a file after mount, the free ino cache will be loaded from disk into memory. When the fs tree is commited, the cache will be written back to disk. To keep compatibility, we check the root generation against the generation of the special inode when loading the cache, so the loading will fail if the btrfs filesystem was mounted in an older kernel before. Signed-off-by: Li Zefan <[email protected]>
1 parent 33345d0 commit 82d5902

File tree

9 files changed

+236
-19
lines changed

9 files changed

+236
-19
lines changed

fs/btrfs/ctree.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ struct btrfs_ordered_sum;
105105
/* For storing free space cache */
106106
#define BTRFS_FREE_SPACE_OBJECTID -11ULL
107107

108+
/*
109+
* The inode number assigned to the special inode for sotring
110+
* free ino cache
111+
*/
112+
#define BTRFS_FREE_INO_OBJECTID -12ULL
113+
108114
/* dummy objectid represents multiple objectids */
109115
#define BTRFS_MULTIPLE_OBJECTIDS -255ULL
110116

@@ -1110,6 +1116,7 @@ struct btrfs_root {
11101116
wait_queue_head_t cache_wait;
11111117
struct btrfs_free_space_ctl *free_ino_pinned;
11121118
u64 cache_progress;
1119+
struct inode *cache_inode;
11131120

11141121
struct mutex log_mutex;
11151122
wait_queue_head_t log_writer_wait;

fs/btrfs/disk-io.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,6 +2505,7 @@ int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
25052505

25062506
static void free_fs_root(struct btrfs_root *root)
25072507
{
2508+
iput(root->cache_inode);
25082509
WARN_ON(!RB_EMPTY_ROOT(&root->inode_tree));
25092510
if (root->anon_super.s_dev) {
25102511
down_write(&root->anon_super.s_umount);

fs/btrfs/extent-tree.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3145,7 +3145,8 @@ int btrfs_check_data_free_space(struct inode *inode, u64 bytes)
31453145
/* make sure bytes are sectorsize aligned */
31463146
bytes = (bytes + root->sectorsize - 1) & ~((u64)root->sectorsize - 1);
31473147

3148-
if (root == root->fs_info->tree_root) {
3148+
if (root == root->fs_info->tree_root ||
3149+
BTRFS_I(inode)->location.objectid == BTRFS_FREE_INO_OBJECTID) {
31493150
alloc_chunk = 0;
31503151
committed = 1;
31513152
}

fs/btrfs/free-space-cache.c

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ int btrfs_truncate_free_space_cache(struct btrfs_root *root,
209209
return ret;
210210
}
211211

212-
return btrfs_update_inode(trans, root, inode);
212+
ret = btrfs_update_inode(trans, root, inode);
213+
return ret;
213214
}
214215

215216
static int readahead_cache(struct inode *inode)
@@ -525,6 +526,7 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
525526
spin_lock(&block_group->lock);
526527
block_group->disk_cache_state = BTRFS_DC_CLEAR;
527528
spin_unlock(&block_group->lock);
529+
ret = 0;
528530

529531
printk(KERN_ERR "btrfs: failed to load free space cache "
530532
"for block group %llu\n", block_group->key.objectid);
@@ -893,6 +895,7 @@ int btrfs_write_out_cache(struct btrfs_root *root,
893895
spin_lock(&block_group->lock);
894896
block_group->disk_cache_state = BTRFS_DC_ERROR;
895897
spin_unlock(&block_group->lock);
898+
ret = 0;
896899

897900
printk(KERN_ERR "btrfs: failed to write free space cace "
898901
"for block group %llu\n", block_group->key.objectid);
@@ -2458,3 +2461,95 @@ u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root)
24582461

24592462
return ino;
24602463
}
2464+
2465+
struct inode *lookup_free_ino_inode(struct btrfs_root *root,
2466+
struct btrfs_path *path)
2467+
{
2468+
struct inode *inode = NULL;
2469+
2470+
spin_lock(&root->cache_lock);
2471+
if (root->cache_inode)
2472+
inode = igrab(root->cache_inode);
2473+
spin_unlock(&root->cache_lock);
2474+
if (inode)
2475+
return inode;
2476+
2477+
inode = __lookup_free_space_inode(root, path, 0);
2478+
if (IS_ERR(inode))
2479+
return inode;
2480+
2481+
spin_lock(&root->cache_lock);
2482+
if (!root->fs_info->closing)
2483+
root->cache_inode = igrab(inode);
2484+
spin_unlock(&root->cache_lock);
2485+
2486+
return inode;
2487+
}
2488+
2489+
int create_free_ino_inode(struct btrfs_root *root,
2490+
struct btrfs_trans_handle *trans,
2491+
struct btrfs_path *path)
2492+
{
2493+
return __create_free_space_inode(root, trans, path,
2494+
BTRFS_FREE_INO_OBJECTID, 0);
2495+
}
2496+
2497+
int load_free_ino_cache(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
2498+
{
2499+
struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
2500+
struct btrfs_path *path;
2501+
struct inode *inode;
2502+
int ret = 0;
2503+
u64 root_gen = btrfs_root_generation(&root->root_item);
2504+
2505+
/*
2506+
* If we're unmounting then just return, since this does a search on the
2507+
* normal root and not the commit root and we could deadlock.
2508+
*/
2509+
smp_mb();
2510+
if (fs_info->closing)
2511+
return 0;
2512+
2513+
path = btrfs_alloc_path();
2514+
if (!path)
2515+
return 0;
2516+
2517+
inode = lookup_free_ino_inode(root, path);
2518+
if (IS_ERR(inode))
2519+
goto out;
2520+
2521+
if (root_gen != BTRFS_I(inode)->generation)
2522+
goto out_put;
2523+
2524+
ret = __load_free_space_cache(root, inode, ctl, path, 0);
2525+
2526+
if (ret < 0)
2527+
printk(KERN_ERR "btrfs: failed to load free ino cache for "
2528+
"root %llu\n", root->root_key.objectid);
2529+
out_put:
2530+
iput(inode);
2531+
out:
2532+
btrfs_free_path(path);
2533+
return ret;
2534+
}
2535+
2536+
int btrfs_write_out_ino_cache(struct btrfs_root *root,
2537+
struct btrfs_trans_handle *trans,
2538+
struct btrfs_path *path)
2539+
{
2540+
struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
2541+
struct inode *inode;
2542+
int ret;
2543+
2544+
inode = lookup_free_ino_inode(root, path);
2545+
if (IS_ERR(inode))
2546+
return 0;
2547+
2548+
ret = __btrfs_write_out_cache(root, inode, ctl, NULL, trans, path, 0);
2549+
if (ret < 0)
2550+
printk(KERN_ERR "btrfs: failed to write free ino cache "
2551+
"for root %llu\n", root->root_key.objectid);
2552+
2553+
iput(inode);
2554+
return ret;
2555+
}

fs/btrfs/free-space-cache.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@ int btrfs_write_out_cache(struct btrfs_root *root,
6565
struct btrfs_block_group_cache *block_group,
6666
struct btrfs_path *path);
6767

68+
struct inode *lookup_free_ino_inode(struct btrfs_root *root,
69+
struct btrfs_path *path);
70+
int create_free_ino_inode(struct btrfs_root *root,
71+
struct btrfs_trans_handle *trans,
72+
struct btrfs_path *path);
73+
int load_free_ino_cache(struct btrfs_fs_info *fs_info,
74+
struct btrfs_root *root);
75+
int btrfs_write_out_ino_cache(struct btrfs_root *root,
76+
struct btrfs_trans_handle *trans,
77+
struct btrfs_path *path);
78+
6879
void btrfs_init_free_space_ctl(struct btrfs_block_group_cache *block_group);
6980
int __btrfs_add_free_space(struct btrfs_free_space_ctl *ctl,
7081
u64 bytenr, u64 size);

fs/btrfs/inode-map.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ static int caching_kthread(void *data)
137137
static void start_caching(struct btrfs_root *root)
138138
{
139139
struct task_struct *tsk;
140+
int ret;
140141

141142
spin_lock(&root->cache_lock);
142143
if (root->cached != BTRFS_CACHE_NO) {
@@ -147,6 +148,14 @@ static void start_caching(struct btrfs_root *root)
147148
root->cached = BTRFS_CACHE_STARTED;
148149
spin_unlock(&root->cache_lock);
149150

151+
ret = load_free_ino_cache(root->fs_info, root);
152+
if (ret == 1) {
153+
spin_lock(&root->cache_lock);
154+
root->cached = BTRFS_CACHE_FINISHED;
155+
spin_unlock(&root->cache_lock);
156+
return;
157+
}
158+
150159
tsk = kthread_run(caching_kthread, root, "btrfs-ino-cache-%llu\n",
151160
root->root_key.objectid);
152161
BUG_ON(IS_ERR(tsk));
@@ -352,6 +361,84 @@ void btrfs_init_free_ino_ctl(struct btrfs_root *root)
352361
pinned->op = &pinned_free_ino_op;
353362
}
354363

364+
int btrfs_save_ino_cache(struct btrfs_root *root,
365+
struct btrfs_trans_handle *trans)
366+
{
367+
struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
368+
struct btrfs_path *path;
369+
struct inode *inode;
370+
u64 alloc_hint = 0;
371+
int ret;
372+
int prealloc;
373+
bool retry = false;
374+
375+
path = btrfs_alloc_path();
376+
if (!path)
377+
return -ENOMEM;
378+
again:
379+
inode = lookup_free_ino_inode(root, path);
380+
if (IS_ERR(inode) && PTR_ERR(inode) != -ENOENT) {
381+
ret = PTR_ERR(inode);
382+
goto out;
383+
}
384+
385+
if (IS_ERR(inode)) {
386+
BUG_ON(retry);
387+
retry = true;
388+
389+
ret = create_free_ino_inode(root, trans, path);
390+
if (ret)
391+
goto out;
392+
goto again;
393+
}
394+
395+
BTRFS_I(inode)->generation = 0;
396+
ret = btrfs_update_inode(trans, root, inode);
397+
WARN_ON(ret);
398+
399+
if (i_size_read(inode) > 0) {
400+
ret = btrfs_truncate_free_space_cache(root, trans, path, inode);
401+
if (ret)
402+
goto out_put;
403+
}
404+
405+
spin_lock(&root->cache_lock);
406+
if (root->cached != BTRFS_CACHE_FINISHED) {
407+
ret = -1;
408+
spin_unlock(&root->cache_lock);
409+
goto out_put;
410+
}
411+
spin_unlock(&root->cache_lock);
412+
413+
spin_lock(&ctl->tree_lock);
414+
prealloc = sizeof(struct btrfs_free_space) * ctl->free_extents;
415+
prealloc = ALIGN(prealloc, PAGE_CACHE_SIZE);
416+
prealloc += ctl->total_bitmaps * PAGE_CACHE_SIZE;
417+
spin_unlock(&ctl->tree_lock);
418+
419+
/* Just to make sure we have enough space */
420+
prealloc += 8 * PAGE_CACHE_SIZE;
421+
422+
ret = btrfs_check_data_free_space(inode, prealloc);
423+
if (ret)
424+
goto out_put;
425+
426+
ret = btrfs_prealloc_file_range_trans(inode, trans, 0, 0, prealloc,
427+
prealloc, prealloc, &alloc_hint);
428+
if (ret)
429+
goto out_put;
430+
btrfs_free_reserved_data_space(inode, prealloc);
431+
432+
out_put:
433+
iput(inode);
434+
out:
435+
if (ret == 0)
436+
ret = btrfs_write_out_ino_cache(root, trans, path);
437+
438+
btrfs_free_path(path);
439+
return ret;
440+
}
441+
355442
static int btrfs_find_highest_objectid(struct btrfs_root *root, u64 *objectid)
356443
{
357444
struct btrfs_path *path;

fs/btrfs/inode-map.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ void btrfs_init_free_ino_ctl(struct btrfs_root *root);
55
void btrfs_unpin_free_ino(struct btrfs_root *root);
66
void btrfs_return_ino(struct btrfs_root *root, u64 objectid);
77
int btrfs_find_free_ino(struct btrfs_root *root, u64 *objectid);
8+
int btrfs_save_ino_cache(struct btrfs_root *root,
9+
struct btrfs_trans_handle *trans);
810

911
int btrfs_find_free_objectid(struct btrfs_root *root, u64 *objectid);
1012

0 commit comments

Comments
 (0)