Btrfs: orphan and truncate fixes
v3 cover Btrfs: orphan and truncate fixes
From: Omar Sandoval <[email protected]>
Hi,
This is v3 of the fixes for the orphan item early ENOSPC issue we hit at Facebook. The big change is that I now also got rid of BTRFS_INODE_HAS_ORPHAN_ITEM (thanks, Nikolay) and shuffled the patches around so there is less churn.
Changes since v2:
- Add patch 5 to get rid of BTRFS_INODE_HAS_ORPHAN_ITEM
- Move patch 10 to patch 6
- Got rid of patch 5; the bug goes away in the process of removing code for patches 9 and 10
- Rename patch 10 batch to what it was called in v1
Changes since v1:
- Added two extra cleanups, patches 10 and 11
- Added a forgotten clear of the orphan bit in patch 8
- Reworded titles of patches 6 and 9
- Added people's reviewed-bys
Cover letter from v1:
At Facebook we hit an early ENOSPC issue which we tracked down to the reservations for orphan items of deleted-but-still-open files. The primary function of this series is to fix that bug, but I ended up uncovering a pile of other issues in the process, most notably that the orphan items we create for truncate are useless.
I've also posted an xfstest that reproduces this bug.
Thanks!
Omar Sandoval (11):
Btrfs: remove stale comment referencing vmtruncate()
Btrfs: fix error handling in btrfs_truncate_inode_items()
Btrfs: don't BUG_ON() in btrfs_truncate_inode_items()
Btrfs: stop creating orphan items for truncate
Btrfs: get rid of BTRFS_INODE_HAS_ORPHAN_ITEM
Btrfs: delete dead code in btrfs_orphan_commit_root()
Btrfs: don't return ino to ino cache if inode item removal fails
Btrfs: refactor btrfs_evict_inode() reserve refill dance
Btrfs: fix ENOSPC caused by orphan items reservations
Btrfs: get rid of unused orphan infrastructure
Btrfs: reserve space for O_TMPFILE orphan item deletion
fs/btrfs/btrfs_inode.h | 18 +- fs/btrfs/ctree.h | 8 - fs/btrfs/disk-io.c | 9 - fs/btrfs/extent-tree.c | 38 --- fs/btrfs/free-space-cache.c | 6 +- fs/btrfs/inode.c | 576 ++++++++++-------------------------- fs/btrfs/transaction.c | 1 - 7 files changed, 170 insertions(+), 486 deletions(-)
Signature · 1 line
2.17.0
PATCH v3 1/11 Btrfs: remove stale comment referencing vmtruncate()
From: Omar Sandoval <[email protected]>
Commit a41ad394a03b ("Btrfs: convert to the new truncate sequence") changed vmtruncate() to truncate_setsize() but didn't update the comment above it. truncate_setsize() never fails (the IS_SWAPFILE() check happens elsewhere), so remove the comment.
--- fs/btrfs/inode.c | 1 - 1 file changed, 1 deletion(-)
fs/btrfs/inode.c +0 -1
@@ -5106,7 +5106,6 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr)if (ret)return ret; -/* we don't support swapfiles, so vmtruncate shouldn't fail */ truncate_setsize (inode, newsize);/* Disable nonlocked read DIO to avoid the end less truncate */
Signature · 1 line
2.17.0
PATCH v3 2/11 Btrfs: fix error handling in btrfs_truncate_inode_items()
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
btrfs_truncate_inode_items() uses two variables for error handling, ret and err. These are not handled consistently, leading to a couple of bugs.
- Errors from btrfs_del_items() are handled but not propagated to the caller
- If btrfs_run_delayed_refs() fails and aborts the transaction, we continue running
Just use ret everywhere and simplify things a bit, fixing both of these issues.
--- fs/btrfs/inode.c | 55 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 27 deletions(-)
fs/btrfs/inode.c +28 -27
@@ -4442,7 +4442,6 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,int pending_del_slot =0 ;int extent_type =-1 ;int ret; -int err =0 ;u64 ino =btrfs_ino (BTRFS_I (inode));u64 bytes_deleted =0 ;bool be_nice = false;
@@ -4494,22 +4493,19 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,* up a huge file in a single leaf. Most of the time that * bytes_deleted is > 0, it will be huge by the time we get here */ -if (be_nice && bytes_deleted >SZ_32M ) { -if (btrfs_should_end_transaction (trans)) { - err = -EAGAIN ; - goto error; - } +if (be_nice && bytes_deleted >SZ_32M && +btrfs_should_end_transaction (trans)) { + ret = -EAGAIN ; + goto out; } - path->leave_spinning =1 ; ret =btrfs_search_slot (trans, root, &key, path,-1 ,1 ); -if (ret <0 ) { - err = ret; +if (ret <0 ) goto out; - }if (ret >0 ) { + ret =0 ;/* there are no items in the tree for us to truncate, we're * done */
@@ -4620,7 +4616,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,* We have to bail so the last_size is set to * just before this extent. */ - err =NEED_TRUNCATE_BLOCK ; + ret =NEED_TRUNCATE_BLOCK ;break ; }
@@ -4687,7 +4683,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, pending_del_nr);if (ret) {btrfs_abort_transaction (trans, ret); - goto error; +break ; } pending_del_nr =0 ; }
@@ -4698,8 +4694,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, trans->delayed_ref_updates =0 ; ret =btrfs_run_delayed_refs (trans, updates *2 ); -if (ret && !err) - err = ret; +if (ret) +break ; } }/*
@@ -4707,8 +4703,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,* and let the transaction restart */ if (should_end) { - err = -EAGAIN ; - goto error; + ret = -EAGAIN ; +break ; } goto search_again; }else {
@@ -4716,32 +4712,37 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, } } out: -if (pending_del_nr) { - ret =btrfs_del_items (trans, root, path, pending_del_slot, +if (ret >=0 && pending_del_nr) { +int err; + + err =btrfs_del_items (trans, root, path, pending_del_slot, pending_del_nr); -if (ret) -btrfs_abort_transaction (trans, ret); +if (err) { +btrfs_abort_transaction (trans, err); + ret = err; + } } -error:if (root->root_key.objectid !=BTRFS_TREE_LOG_OBJECTID ) {ASSERT (last_size >= new_size); -if (!err && last_size > new_size) +if (!ret && last_size > new_size) last_size = new_size;btrfs_ordered_update_i_size (inode, last_size,NULL ); }btrfs_free_path (path); -if (be_nice && bytes_deleted >SZ_32M ) { +if (be_nice && bytes_deleted >SZ_32M && (ret >=0 || ret == -EAGAIN )) {unsigned long updates = trans->delayed_ref_updates; +int err; +if (updates) { trans->delayed_ref_updates =0 ; - ret =btrfs_run_delayed_refs (trans, updates *2 ); -if (ret && !err) - err = ret; + err =btrfs_run_delayed_refs (trans, updates *2 ); +if (err) + ret = err; } } -return err; +return ret; }/*
Signature · 1 line
2.17.0
PATCH v3 3/11 Btrfs: don't BUG_ON() in btrfs_truncate_inode_items()
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
btrfs_free_extent() can fail because of ENOMEM. There's no reason to panic here, we can just abort the transaction.
--- fs/btrfs/inode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
fs/btrfs/inode.c +4 -1
@@ -4655,7 +4655,10 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, extent_num_bytes,0 ,btrfs_header_owner (leaf), ino, extent_offset); -BUG_ON (ret); +if (ret) { +btrfs_abort_transaction (trans, ret); +break ; + }if (btrfs_should_throttle_delayed_refs (trans, fs_info)) btrfs_async_run_delayed_refs(fs_info, trans->delayed_ref_updates * 2,
Signature · 1 line
2.17.0
PATCH v3 4/11 Btrfs: stop creating orphan items for truncate
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
Currently, we insert an orphan item during a truncate so that if there's a crash, we don't leak extents past the on-disk i_size. However, since commit 7f4f6e0a3f6d ("Btrfs: only update disk_i_size as we remove extents"), we keep disk_i_size in sync with the extent items as we truncate, so orphan cleanup will never have any extents to remove. Don't bother with the superfluous orphan item.
--- fs/btrfs/free-space-cache.c | 6 +- fs/btrfs/inode.c | 159 +++++++++++------------------------- 2 files changed, 51 insertions(+), 114 deletions(-)
fs/btrfs/free-space-cache.c +2 -4
@@ -253,10 +253,8 @@ int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans,truncate_pagecache (inode,0 );/* -* We don't need an orphan item because truncating the free space cache -* will never be split across transactions. -* We don't need to check for -EAGAIN because we're a free space -* cache inode +* We skip the throttling logic for free space cache inodes, so we don't +* need to check for -EAGAIN. */ ret =btrfs_truncate_inode_items (trans, root, inode,0 ,BTRFS_EXTENT_DATA_KEY );
fs/btrfs/inode.c +49 -110
243 lines not on this page · raw message
Signature · 1 line
2.17.0
PATCH v3 5/11 Btrfs: get rid of BTRFS_INODE_HAS_ORPHAN_ITEM
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
Now that we don't add orphan items for truncate, there can't be races on adding or deleting an orphan item, so this bit is unnecessary.
--- fs/btrfs/btrfs_inode.h | 13 ++++---- fs/btrfs/inode.c | 76 +++++++++++------------------------------- 2 files changed, 26 insertions(+), 63 deletions(-)
fs/btrfs/btrfs_inode.h +6 -7
@@ -23,13 +23,12 @@#define BTRFS_INODE_ORPHAN_META_RESERVED 1#define BTRFS_INODE_DUMMY 2#define BTRFS_INODE_IN_DEFRAG 3 -#define BTRFS_INODE_HAS_ORPHAN_ITEM 4 -#define BTRFS_INODE_HAS_ASYNC_EXTENT 5 -#define BTRFS_INODE_NEEDS_FULL_SYNC 6 -#define BTRFS_INODE_COPY_EVERYTHING 7 -#define BTRFS_INODE_IN_DELALLOC_LIST 8 -#define BTRFS_INODE_READDIO_NEED_LOCK 9 -#define BTRFS_INODE_HAS_PROPS 10 +#define BTRFS_INODE_HAS_ASYNC_EXTENT 4 +#define BTRFS_INODE_NEEDS_FULL_SYNC 5 +#define BTRFS_INODE_COPY_EVERYTHING 6 +#define BTRFS_INODE_IN_DELALLOC_LIST 7 +#define BTRFS_INODE_READDIO_NEED_LOCK 8 +#define BTRFS_INODE_HAS_PROPS 9/* in memory btrfs inode */ struct btrfs_inode {
fs/btrfs/inode.c +20 -56
139 lines not on this page · raw message
Signature · 1 line
2.17.0
PATCH v3 6/11 Btrfs: delete dead code in btrfs_orphan_commit_root()
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
btrfs_orphan_commit_root() tries to delete an orphan item for a subvolume in the tree root, but we don't actually insert that item in the first place. See commit 0a0d4415e338 ("Btrfs: delete dead code in btrfs_orphan_add()"). We can get rid of it.
--- fs/btrfs/inode.c | 12 ------------ 1 file changed, 12 deletions(-)
fs/btrfs/inode.c +0 -12
@@ -3302,7 +3302,6 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, {struct btrfs_fs_info *fs_info = root->fs_info;struct btrfs_block_rsv *block_rsv; -int ret;if (atomic_read (&root->orphan_inodes) || root->orphan_cleanup_state !=ORPHAN_CLEANUP_DONE )
@@ -3323,17 +3322,6 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, root->orphan_block_rsv =NULL ;spin_unlock (&root->orphan_lock); -if (test_bit (BTRFS_ROOT_ORPHAN_ITEM_INSERTED , &root->state) && -btrfs_root_refs (&root->root_item) >0 ) { - ret =btrfs_del_orphan_item (trans, fs_info->tree_root, - root->root_key.objectid); -if (ret) -btrfs_abort_transaction (trans, ret); -else -clear_bit (BTRFS_ROOT_ORPHAN_ITEM_INSERTED , - &root->state); - } -if (block_rsv) {WARN_ON (block_rsv->size >0 );btrfs_free_block_rsv (fs_info, block_rsv);
Signature · 1 line
2.17.0
PATCH v3 7/11 Btrfs: don't return ino to ino cache if inode item removal fails
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
In btrfs_evict_inode(), if btrfs_truncate_inode_items() fails, the inode item will still be in the tree but we still return the ino to the ino cache. That will blow up later when someone tries to allocate that ino, so don't return it to the cache.
--- fs/btrfs/inode.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-)
fs/btrfs/inode.c +13 -12
@@ -5330,13 +5330,18 @@ void btrfs_evict_inode(struct inode *inode) trans->block_rsv = rsv; ret =btrfs_truncate_inode_items (trans, root, inode,0 ,0 ); -if (ret != -ENOSPC && ret != -EAGAIN ) +if (ret) { + trans->block_rsv = &fs_info->trans_block_rsv; +btrfs_end_transaction (trans); +btrfs_btree_balance_dirty (fs_info); +if (ret != -ENOSPC && ret != -EAGAIN ) { +btrfs_orphan_del (NULL ,BTRFS_I (inode)); +btrfs_free_block_rsv (fs_info, rsv); + goto no_delete; + } + }else {break ; - - trans->block_rsv = &fs_info->trans_block_rsv; -btrfs_end_transaction (trans); - trans =NULL ; -btrfs_btree_balance_dirty (fs_info); + } }btrfs_free_block_rsv (fs_info, rsv);
@@ -5345,12 +5350,8 @@ void btrfs_evict_inode(struct inode *inode)* Errors here aren't a big deal, it just means we leave orphan items * in the tree. They will be cleaned up on the next mount. */ -if (ret ==0 ) { - trans->block_rsv = root->orphan_block_rsv; -btrfs_orphan_del (trans,BTRFS_I (inode)); - }else { -btrfs_orphan_del (NULL ,BTRFS_I (inode)); - } + trans->block_rsv = root->orphan_block_rsv; +btrfs_orphan_del (trans,BTRFS_I (inode)); trans->block_rsv = &fs_info->trans_block_rsv; if (!(root == fs_info->tree_root ||
Signature · 1 line
2.17.0
PATCH v3 8/11 Btrfs: refactor btrfs_evict_inode() reserve refill dance
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
The truncate loop in btrfs_evict_inode() does two things at once:
- It refills the temporary block reserve, potentially stealing from the global reserve or committing
- It calls btrfs_truncate_inode_items()
The tangle of continues hides the fact that these two steps are actually separate. Split the first step out into a separate function both for clarity and so that we can reuse it in a later patch.
--- fs/btrfs/inode.c | 113 ++++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 71 deletions(-)
fs/btrfs/inode.c +42 -71
@@ -5189,13 +5189,52 @@ static void evict_inode_truncate_pages(struct inode *inode)spin_unlock (&io_tree->lock); } +static struct btrfs_trans_handle *evict_refill_and_join (struct btrfs_root *root, +struct btrfs_block_rsv *rsv, +u64 min_size) +{ +struct btrfs_fs_info *fs_info = root->fs_info; +struct btrfs_block_rsv *global_rsv = &fs_info->global_block_rsv; +int failures =0 ; + +for (;;) { +struct btrfs_trans_handle *trans; +int ret; + + ret =btrfs_block_rsv_refill (root, rsv, min_size, +BTRFS_RESERVE_FLUSH_LIMIT ); + +if (ret && ++failures >2 ) { +btrfs_warn (fs_info, +"could not allocate space for a delete; will truncate on mount" ); +return ERR_PTR (-ENOSPC ); + } + + trans =btrfs_join_transaction (root); +if (IS_ERR (trans) || !ret) +return trans; + +/* +* Try to steal from the global reserve if there is space for +* it. +*/ +if (!btrfs_check_space_for_delayed_refs (trans, fs_info) && + !btrfs_block_rsv_migrate (global_rsv, rsv, min_size,0 )) +return trans; + +/* If not, commit and try again. */ + ret =btrfs_commit_transaction (trans); +if (ret) +return ERR_PTR (ret); + } +} +void btrfs_evict_inode (struct inode *inode) {struct btrfs_fs_info *fs_info =btrfs_sb (inode->i_sb);struct btrfs_trans_handle *trans;struct btrfs_root *root =BTRFS_I (inode)->root; -struct btrfs_block_rsv *rsv, *global_rsv; -int steal_from_global =0 ; +struct btrfs_block_rsv *rsv;u64 min_size;int ret;
@@ -5248,85 +5287,17 @@ void btrfs_evict_inode(struct inode *inode) } rsv->size = min_size; rsv->failfast =1 ; - global_rsv = &fs_info->global_block_rsv;btrfs_i_size_write (BTRFS_I (inode),0 ); -/* -* This is a bit simpler than btrfs_truncate since we've already -* reserved our space for our orphan item in the unlink, so we just -* need to reserve some slack space in case we add bytes and update -* inode item when doing the truncate. -*/ while (1 ) { - ret =btrfs_block_rsv_refill (root, rsv, min_size, -BTRFS_RESERVE_FLUSH_LIMIT ); - -/* -* Try and steal from the global reserve since we will -* likely not use this space anyway, we want to try as -* hard as possible to get this to work. -*/ -if (ret) - steal_from_global++; -else - steal_from_global =0 ; - ret =0 ; - -/* -* steal_from_global == 0: we reserved stuff, hooray! -* steal_from_global == 1: we didn't reserve stuff, boo! -* steal_from_global == 2: we've committed, still not a lot of -* room but maybe we'll have room in the global reserve this -* time. -* steal_from_global == 3: abandon all hope! -*/ -if (steal_from_global >2 ) { -btrfs_warn (fs_info, -"Could not get space for a delete, will truncate on mount %d" , - ret); -btrfs_orphan_del (NULL ,BTRFS_I (inode)); -btrfs_free_block_rsv (fs_info, rsv); - goto no_delete; - } - - trans =btrfs_join_transaction (root); + trans =evict_refill_and_join (root, rsv, min_size);if (IS_ERR (trans)) {btrfs_orphan_del (NULL ,BTRFS_I (inode));btrfs_free_block_rsv (fs_info, rsv); goto no_delete; } -/* -* We can't just steal from the global reserve, we need to make -* sure there is room to do it, if not we need to commit and try -* again. -*/ -if (steal_from_global) { -if (!btrfs_check_space_for_delayed_refs (trans, fs_info)) - ret =btrfs_block_rsv_migrate (global_rsv, rsv, - min_size,0 ); -else - ret = -ENOSPC ; - } - -/* -* Couldn't steal from the global reserve, we have too much -* pending stuff built up, commit the transaction and try it -* again. -*/ -if (ret) { - ret =btrfs_commit_transaction (trans); -if (ret) { -btrfs_orphan_del (NULL ,BTRFS_I (inode)); -btrfs_free_block_rsv (fs_info, rsv); - goto no_delete; - } -continue ; - }else { - steal_from_global =0 ; - } - trans->block_rsv = rsv; ret =btrfs_truncate_inode_items (trans, root, inode,0 ,0 );
Signature · 1 line
2.17.0
PATCH v3 9/11 Btrfs: fix ENOSPC caused by orphan items reservations
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
Currently, we keep space reserved for all inode orphan items until the inode is evicted (i.e., all references to it are dropped). We hit an issue where an application would keep a bunch of deleted files open (by design) and thus keep a large amount of space reserved, causing ENOSPC errors when other operations tried to reserve space. This long-standing reservation isn't absolutely necessary for a couple of reasons:
- We can almost always make the reservation we need or steal from the global reserve for the orphan item
- If we can't, it's not the end of the world if we drop the orphan item on the floor and let the next mount clean it up
So, get rid of persistent reservation and just reserve space in btrfs_evict_inode().
--- fs/btrfs/btrfs_inode.h | 17 +++-- fs/btrfs/inode.c | 158 ++++++++++------------------------------- 2 files changed, 46 insertions(+), 129 deletions(-)
fs/btrfs/btrfs_inode.h +8 -9
@@ -20,15 +20,14 @@* new data the application may have written before commit. */ #define BTRFS_INODE_ORDERED_DATA_CLOSE 0 -#define BTRFS_INODE_ORPHAN_META_RESERVED 1 -#define BTRFS_INODE_DUMMY 2 -#define BTRFS_INODE_IN_DEFRAG 3 -#define BTRFS_INODE_HAS_ASYNC_EXTENT 4 -#define BTRFS_INODE_NEEDS_FULL_SYNC 5 -#define BTRFS_INODE_COPY_EVERYTHING 6 -#define BTRFS_INODE_IN_DELALLOC_LIST 7 -#define BTRFS_INODE_READDIO_NEED_LOCK 8 -#define BTRFS_INODE_HAS_PROPS 9 +#define BTRFS_INODE_DUMMY 1 +#define BTRFS_INODE_IN_DEFRAG 2 +#define BTRFS_INODE_HAS_ASYNC_EXTENT 3 +#define BTRFS_INODE_NEEDS_FULL_SYNC 4 +#define BTRFS_INODE_COPY_EVERYTHING 5 +#define BTRFS_INODE_IN_DELALLOC_LIST 6 +#define BTRFS_INODE_READDIO_NEED_LOCK 7 +#define BTRFS_INODE_HAS_PROPS 8/* in memory btrfs inode */ struct btrfs_inode {
fs/btrfs/inode.c +38 -120
219 lines not on this page · raw message
Signature · 1 line
2.17.0
PATCH v3 10/11 Btrfs: get rid of unused orphan infrastructure
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
Now that we don't keep long-standing reservations for orphan items, root->orphan_block_rsv isn't used. We can git rid of it, along with root->orphan_lock, which was used to protect it, root->orphan_inodes, which was used as a refcount for it, and btrfs_orphan_commit_root(), which was the last user of all of these.
--- fs/btrfs/ctree.h | 8 -------- fs/btrfs/disk-io.c | 9 --------- fs/btrfs/extent-tree.c | 38 ------------------------------------- fs/btrfs/inode.c | 43 +----------------------------------------- fs/btrfs/transaction.c | 1 - 5 files changed, 1 insertion(+), 98 deletions(-)
fs/btrfs/ctree.h +0 -8
@@ -1219,9 +1219,6 @@ struct btrfs_root {spinlock_t log_extents_lock[2 ];struct list_head logged_list[2 ]; -spinlock_t orphan_lock; -atomic_t orphan_inodes; -struct btrfs_block_rsv *orphan_block_rsv;int orphan_cleanup_state;spinlock_t inode_lock;
@@ -2764,9 +2761,6 @@ void btrfs_delalloc_release_space(struct inode *inode,void btrfs_free_reserved_data_space_noquota (struct inode *inode,u64 start,u64 len);void btrfs_trans_release_chunk_metadata (struct btrfs_trans_handle *trans); -int btrfs_orphan_reserve_metadata (struct btrfs_trans_handle *trans, -struct btrfs_inode *inode); -void btrfs_orphan_release_metadata (struct btrfs_inode *inode); int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, struct btrfs_block_rsv *rsv, int nitems,
@@ -3238,8 +3232,6 @@ int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans,int btrfs_orphan_add (struct btrfs_trans_handle *trans,struct btrfs_inode *inode);int btrfs_orphan_cleanup (struct btrfs_root *root); -void btrfs_orphan_commit_root (struct btrfs_trans_handle *trans, -struct btrfs_root *root);int btrfs_cont_expand (struct inode *inode,loff_t oldsize,loff_t size);void btrfs_invalidate_inodes (struct btrfs_root *root);void btrfs_add_delayed_iput (struct inode *inode);
fs/btrfs/disk-io.c +0 -9
@@ -1185,7 +1185,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info, root->inode_tree =RB_ROOT ;INIT_RADIX_TREE (&root->delayed_nodes_tree,GFP_ATOMIC ); root->block_rsv =NULL ; - root->orphan_block_rsv =NULL ;INIT_LIST_HEAD (&root->dirty_list);INIT_LIST_HEAD (&root->root_list);
@@ -1195,7 +1194,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,INIT_LIST_HEAD (&root->ordered_root);INIT_LIST_HEAD (&root->logged_list[0 ]);INIT_LIST_HEAD (&root->logged_list[1 ]); -spin_lock_init (&root->orphan_lock);spin_lock_init (&root->inode_lock);spin_lock_init (&root->delalloc_lock);spin_lock_init (&root->ordered_extent_lock);
@@ -1216,7 +1214,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,atomic_set (&root->log_commit[1 ],0 );atomic_set (&root->log_writers,0 );atomic_set (&root->log_batch,0 ); -atomic_set (&root->orphan_inodes,0 );refcount_set (&root->refs,1 );atomic_set (&root->will_be_snapshotted,0 ); root->log_transid =0 ;
@@ -3674,8 +3671,6 @@ static void free_fs_root(struct btrfs_root *root) {iput (root->ino_cache_inode);WARN_ON (!RB_EMPTY_ROOT (&root->inode_tree)); -btrfs_free_block_rsv (root->fs_info, root->orphan_block_rsv); - root->orphan_block_rsv =NULL ;if (root->anon_dev)free_anon_bdev (root->anon_dev);if (root->subv_writers)
@@ -3766,7 +3761,6 @@ int btrfs_commit_super(struct btrfs_fs_info *fs_info)void close_ctree (struct btrfs_fs_info *fs_info) { -struct btrfs_root *root = fs_info->tree_root;int ret;set_bit (BTRFS_FS_CLOSING_START , &fs_info->flags);
@@ -3861,9 +3855,6 @@ void close_ctree(struct btrfs_fs_info *fs_info)btrfs_free_stripe_hash_table (fs_info);btrfs_free_ref_cache (fs_info); -__btrfs_free_block_rsv (root->orphan_block_rsv); - root->orphan_block_rsv =NULL ; -while (!list_empty (&fs_info->pinned_chunks)) {struct extent_map *em;
fs/btrfs/extent-tree.c +0 -38
@@ -5949,44 +5949,6 @@ void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans) trans->chunk_bytes_reserved =0 ; } -/* Can only return 0 or -ENOSPC */ -int btrfs_orphan_reserve_metadata (struct btrfs_trans_handle *trans, -struct btrfs_inode *inode) -{ -struct btrfs_fs_info *fs_info =btrfs_sb (inode->vfs_inode.i_sb); -struct btrfs_root *root = inode->root; -/* -* We always use trans->block_rsv here as we will have reserved space -* for our orphan when starting the transaction, using get_block_rsv() -* here will sometimes make us choose the wrong block rsv as we could be -* doing a reloc inode for a non refcounted root. -*/ -struct btrfs_block_rsv *src_rsv = trans->block_rsv; -struct btrfs_block_rsv *dst_rsv = root->orphan_block_rsv; - -/* -* We need to hold space in order to delete our orphan item once we've -* added it, so this takes the reservation so we can release it later -* when we are truly done with the orphan item. -*/ -u64 num_bytes =btrfs_calc_trans_metadata_size (fs_info,1 ); - -trace_btrfs_space_reservation (fs_info,"orphan" ,btrfs_ino (inode), - num_bytes,1 ); -return btrfs_block_rsv_migrate (src_rsv, dst_rsv, num_bytes,1 ); -} - -void btrfs_orphan_release_metadata (struct btrfs_inode *inode) -{ -struct btrfs_fs_info *fs_info =btrfs_sb (inode->vfs_inode.i_sb); -struct btrfs_root *root = inode->root; -u64 num_bytes =btrfs_calc_trans_metadata_size (fs_info,1 ); - -trace_btrfs_space_reservation (fs_info,"orphan" ,btrfs_ino (inode), - num_bytes,0 ); -btrfs_block_rsv_release (fs_info, root->orphan_block_rsv, num_bytes); -} -/* * btrfs_subvolume_reserve_metadata() - reserve space for subvolume operation * root: the root of the parent directory
fs/btrfs/inode.c +1 -42
55 lines not on this page · raw message
fs/btrfs/transaction.c +0 -1
@@ -1250,7 +1250,6 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans)btrfs_free_log (trans, root);btrfs_update_reloc_root (trans, root); -btrfs_orphan_commit_root (trans, root);btrfs_save_ino_cache (root, trans);
Signature · 1 line
2.17.0
PATCH v3 11/11 Btrfs: reserve space for O_TMPFILE orphan item deletion
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
btrfs_link() calls btrfs_orphan_del() if it's linking an O_TMPFILE but it doesn't reserve space to do so. Even before the removal of the orphan_block_rsv it wasn't using it.
--- fs/btrfs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/btrfs/inode.c +2 -1
@@ -6465,8 +6465,9 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,* 2 items for inode and inode ref * 2 items for dir items * 1 item for parent inode +* 1 item for orphan item deletion if O_TMPFILE */ - trans =btrfs_start_transaction (root,5 ); + trans =btrfs_start_transaction (root, inode->i_nlink ?5 :6 );if (IS_ERR (trans)) { err =PTR_ERR (trans); trans =NULL ;
Signature · 1 line
2.17.0
Re: [PATCH v3 05/11] Btrfs: get rid of BTRFS_INODE_HAS_ORPHAN_ITEM
In reply to Omar SandovalQuoted 10 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
Now that we don't add orphan items for truncate, there can't be races on adding or deleting an orphan item, so this bit is unnecessary.
Signed-off-by Omar Sandoval <[email protected]>--- fs/btrfs/btrfs_inode.h | 13 ++++---- fs/btrfs/inode.c | 76 +++++++++++------------------------------- 2 files changed, 26 insertions(+), 63 deletions(-)
Very nice,
Quoted 178 lines
fs/btrfs/btrfs_inode.h +6
-7@@ -23,13 +23,12 @@#define BTRFS_INODE_ORPHAN_META_RESERVED 1#define BTRFS_INODE_DUMMY 2#define BTRFS_INODE_IN_DEFRAG 3 -#define BTRFS_INODE_HAS_ORPHAN_ITEM 4 -#define BTRFS_INODE_HAS_ASYNC_EXTENT 5 -#define BTRFS_INODE_NEEDS_FULL_SYNC 6 -#define BTRFS_INODE_COPY_EVERYTHING 7 -#define BTRFS_INODE_IN_DELALLOC_LIST 8 -#define BTRFS_INODE_READDIO_NEED_LOCK 9 -#define BTRFS_INODE_HAS_PROPS 10 +#define BTRFS_INODE_HAS_ASYNC_EXTENT 4 +#define BTRFS_INODE_NEEDS_FULL_SYNC 5 +#define BTRFS_INODE_COPY_EVERYTHING 6 +#define BTRFS_INODE_IN_DELALLOC_LIST 7 +#define BTRFS_INODE_READDIO_NEED_LOCK 8 +#define BTRFS_INODE_HAS_PROPS 9/* in memory btrfs inode */ struct btrfs_inode {fs/btrfs/inode.c +20
-56@@ -3354,7 +3354,6 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans,struct btrfs_root *root = inode->root;struct btrfs_block_rsv *block_rsv =NULL ;int reserve =0 ; -bool insert = false;int ret;if (!root->orphan_block_rsv) {@@ -3364,10 +3363,6 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans,return -ENOMEM ; } -if (!test_and_set_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , - &inode->runtime_flags)) - insert = true; -if (!test_and_set_bit (BTRFS_INODE_ORPHAN_META_RESERVED , &inode->runtime_flags)) reserve =1 ;@@ -3381,8 +3376,7 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, block_rsv =NULL ; } -if (insert) -atomic_inc (&root->orphan_inodes); +atomic_inc (&root->orphan_inodes);spin_unlock (&root->orphan_lock);/* grab metadata reservation from transaction handle */ @@ -3398,36 +3392,28 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans,atomic_dec (&root->orphan_inodes);clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , &inode->runtime_flags); -if (insert) -clear_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , - &inode->runtime_flags);return ret; } }/* insert an orphan item to track this unlinked file */ -if (insert) { - ret =btrfs_insert_orphan_item (trans, root,btrfs_ino (inode)); -if (ret) { -if (reserve) { -clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , - &inode->runtime_flags); -btrfs_orphan_release_metadata (inode); - } -/* -* btrfs_orphan_commit_root may race with us and set -* ->orphan_block_rsv to zero, in order to avoid that, -* decrease ->orphan_inodes after everything is done. -*/ -atomic_dec (&root->orphan_inodes); -if (ret != -EEXIST ) { -clear_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , - &inode->runtime_flags); -btrfs_abort_transaction (trans, ret); -return ret; - } + ret =btrfs_insert_orphan_item (trans, root,btrfs_ino (inode)); +if (ret) { +if (reserve) { +clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , + &inode->runtime_flags); +btrfs_orphan_release_metadata (inode); + } +/* +* btrfs_orphan_commit_root may race with us and set +* ->orphan_block_rsv to zero, in order to avoid that, +* decrease ->orphan_inodes after everything is done. +*/ +atomic_dec (&root->orphan_inodes); +if (ret != -EEXIST ) { +btrfs_abort_transaction (trans, ret); +return ret; } - ret =0 ; }return 0 ;@@ -3441,14 +3427,9 @@ static int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct btrfs_inode *inode) {struct btrfs_root *root = inode->root; -int delete_item =0 ;int ret =0 ; -if (test_and_clear_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , - &inode->runtime_flags)) - delete_item =1 ; - -if (delete_item && trans) +if (trans) ret =btrfs_del_orphan_item (trans, root,btrfs_ino (inode)); if (test_and_clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED,@@ -3460,8 +3441,7 @@ static int btrfs_orphan_del(struct btrfs_trans_handle *trans,* to zero, in order to avoid that, decrease ->orphan_inodes after * everything is done. */ -if (delete_item) -atomic_dec (&root->orphan_inodes); +atomic_dec (&root->orphan_inodes);return ret; }@@ -3619,12 +3599,6 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)continue ; } -/* -* add this inode to the orphan list so btrfs_orphan_del does -* the proper thing when we hit it -*/ -set_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , - &BTRFS_I (inode)->runtime_flags);atomic_inc (&root->orphan_inodes); nr_unlink++;@@ -5264,11 +5238,8 @@ void btrfs_evict_inode(struct inode *inode)btrfs_free_io_failure_record (BTRFS_I (inode),0 , (u64)-1 ); -if (test_bit (BTRFS_FS_LOG_RECOVERING , &fs_info->flags)) { -BUG_ON (test_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , - &BTRFS_I (inode)->runtime_flags)); +if (test_bit (BTRFS_FS_LOG_RECOVERING , &fs_info->flags)) goto no_delete; - }if (inode->i_nlink >0 ) { BUG_ON(btrfs_root_refs(&root->root_item) != 0 &&@@ -9265,13 +9236,6 @@ void btrfs_destroy_inode(struct inode *inode)if (!root) goto free; -if (test_bit (BTRFS_INODE_HAS_ORPHAN_ITEM , - &BTRFS_I (inode)->runtime_flags)) { -btrfs_info (fs_info,"inode %llu still on the orphan list" , -btrfs_ino (BTRFS_I (inode))); -atomic_dec (&root->orphan_inodes); - } -while (1 ) { ordered =btrfs_lookup_first_ordered_extent (inode, (u64)-1 );if (!ordered)
Re: [PATCH v3 10/11] Btrfs: get rid of unused orphan infrastructure
In reply to Omar SandovalQuoted 9 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
Now that we don't keep long-standing reservations for orphan items,
root->orphan_block_rsvisn't used. We can git rid of it, along withroot->orphan_lock, which was used to protect it,root->orphan_inodes, which was used as a refcount for it, andbtrfs_orphan_commit_root(), which was the last user of all of these.Signed-off-by Omar Sandoval <[email protected]>
Quoted 219 lines
--- fs/btrfs/ctree.h | 8 -------- fs/btrfs/disk-io.c | 9 --------- fs/btrfs/extent-tree.c | 38 ------------------------------------- fs/btrfs/inode.c | 43 +----------------------------------------- fs/btrfs/transaction.c | 1 - 5 files changed, 1 insertion(+), 98 deletions(-)fs/btrfs/ctree.h +0
-8@@ -1219,9 +1219,6 @@ struct btrfs_root {spinlock_t log_extents_lock[2 ];struct list_head logged_list[2 ]; -spinlock_t orphan_lock; -atomic_t orphan_inodes; -struct btrfs_block_rsv *orphan_block_rsv;int orphan_cleanup_state;spinlock_t inode_lock;@@ -2764,9 +2761,6 @@ void btrfs_delalloc_release_space(struct inode *inode,void btrfs_free_reserved_data_space_noquota (struct inode *inode,u64 start,u64 len);void btrfs_trans_release_chunk_metadata (struct btrfs_trans_handle *trans); -int btrfs_orphan_reserve_metadata (struct btrfs_trans_handle *trans, -struct btrfs_inode *inode); -void btrfs_orphan_release_metadata (struct btrfs_inode *inode); int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, struct btrfs_block_rsv *rsv, int nitems,@@ -3238,8 +3232,6 @@ int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans,int btrfs_orphan_add (struct btrfs_trans_handle *trans,struct btrfs_inode *inode);int btrfs_orphan_cleanup (struct btrfs_root *root); -void btrfs_orphan_commit_root (struct btrfs_trans_handle *trans, -struct btrfs_root *root);int btrfs_cont_expand (struct inode *inode,loff_t oldsize,loff_t size);void btrfs_invalidate_inodes (struct btrfs_root *root);void btrfs_add_delayed_iput (struct inode *inode);fs/btrfs/disk-io.c +0
-9@@ -1185,7 +1185,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info, root->inode_tree =RB_ROOT ;INIT_RADIX_TREE (&root->delayed_nodes_tree,GFP_ATOMIC ); root->block_rsv =NULL ; - root->orphan_block_rsv =NULL ;INIT_LIST_HEAD (&root->dirty_list);INIT_LIST_HEAD (&root->root_list);@@ -1195,7 +1194,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,INIT_LIST_HEAD (&root->ordered_root);INIT_LIST_HEAD (&root->logged_list[0 ]);INIT_LIST_HEAD (&root->logged_list[1 ]); -spin_lock_init (&root->orphan_lock);spin_lock_init (&root->inode_lock);spin_lock_init (&root->delalloc_lock);spin_lock_init (&root->ordered_extent_lock);@@ -1216,7 +1214,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,atomic_set (&root->log_commit[1 ],0 );atomic_set (&root->log_writers,0 );atomic_set (&root->log_batch,0 ); -atomic_set (&root->orphan_inodes,0 );refcount_set (&root->refs,1 );atomic_set (&root->will_be_snapshotted,0 ); root->log_transid =0 ;@@ -3674,8 +3671,6 @@ static void free_fs_root(struct btrfs_root *root) {iput (root->ino_cache_inode);WARN_ON (!RB_EMPTY_ROOT (&root->inode_tree)); -btrfs_free_block_rsv (root->fs_info, root->orphan_block_rsv); - root->orphan_block_rsv =NULL ;if (root->anon_dev)free_anon_bdev (root->anon_dev);if (root->subv_writers)@@ -3766,7 +3761,6 @@ int btrfs_commit_super(struct btrfs_fs_info *fs_info)void close_ctree (struct btrfs_fs_info *fs_info) { -struct btrfs_root *root = fs_info->tree_root;int ret;set_bit (BTRFS_FS_CLOSING_START , &fs_info->flags);@@ -3861,9 +3855,6 @@ void close_ctree(struct btrfs_fs_info *fs_info)btrfs_free_stripe_hash_table (fs_info);btrfs_free_ref_cache (fs_info); -__btrfs_free_block_rsv (root->orphan_block_rsv); - root->orphan_block_rsv =NULL ; -while (!list_empty (&fs_info->pinned_chunks)) {struct extent_map *em;fs/btrfs/extent-tree.c +0
-38@@ -5949,44 +5949,6 @@ void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans) trans->chunk_bytes_reserved =0 ; } -/* Can only return 0 or -ENOSPC */ -int btrfs_orphan_reserve_metadata (struct btrfs_trans_handle *trans, -struct btrfs_inode *inode) -{ -struct btrfs_fs_info *fs_info =btrfs_sb (inode->vfs_inode.i_sb); -struct btrfs_root *root = inode->root; -/* -* We always use trans->block_rsv here as we will have reserved space -* for our orphan when starting the transaction, using get_block_rsv() -* here will sometimes make us choose the wrong block rsv as we could be -* doing a reloc inode for a non refcounted root. -*/ -struct btrfs_block_rsv *src_rsv = trans->block_rsv; -struct btrfs_block_rsv *dst_rsv = root->orphan_block_rsv; - -/* -* We need to hold space in order to delete our orphan item once we've -* added it, so this takes the reservation so we can release it later -* when we are truly done with the orphan item. -*/ -u64 num_bytes =btrfs_calc_trans_metadata_size (fs_info,1 ); - -trace_btrfs_space_reservation (fs_info,"orphan" ,btrfs_ino (inode), - num_bytes,1 ); -return btrfs_block_rsv_migrate (src_rsv, dst_rsv, num_bytes,1 ); -} - -void btrfs_orphan_release_metadata (struct btrfs_inode *inode) -{ -struct btrfs_fs_info *fs_info =btrfs_sb (inode->vfs_inode.i_sb); -struct btrfs_root *root = inode->root; -u64 num_bytes =btrfs_calc_trans_metadata_size (fs_info,1 ); - -trace_btrfs_space_reservation (fs_info,"orphan" ,btrfs_ino (inode), - num_bytes,0 ); -btrfs_block_rsv_release (fs_info, root->orphan_block_rsv, num_bytes); -} -/* * btrfs_subvolume_reserve_metadata() - reserve space for subvolume operation * root: the root of the parent directory fs/btrfs/inode.c +1
-42@@ -3292,42 +3292,6 @@ void btrfs_run_delayed_iputs(struct btrfs_fs_info *fs_info)spin_unlock (&fs_info->delayed_iput_lock); } -/* -* This is called in transaction commit time. If there are no orphan -* files in the subvolume, it removes orphan item and frees block_rsv -* structure. -*/ -void btrfs_orphan_commit_root (struct btrfs_trans_handle *trans, -struct btrfs_root *root) -{ -struct btrfs_fs_info *fs_info = root->fs_info; -struct btrfs_block_rsv *block_rsv; - -if (atomic_read (&root->orphan_inodes) || - root->orphan_cleanup_state !=ORPHAN_CLEANUP_DONE ) -return ; - -spin_lock (&root->orphan_lock); -if (atomic_read (&root->orphan_inodes)) { -spin_unlock (&root->orphan_lock); -return ; - } - -if (root->orphan_cleanup_state !=ORPHAN_CLEANUP_DONE ) { -spin_unlock (&root->orphan_lock); -return ; - } - - block_rsv = root->orphan_block_rsv; - root->orphan_block_rsv =NULL ; -spin_unlock (&root->orphan_lock); - -if (block_rsv) { -WARN_ON (block_rsv->size >0 ); -btrfs_free_block_rsv (fs_info, block_rsv); - } -} -/* * This creates an orphan entry for the given inode in case something goes wrong * in the middle of an unlink. @@ -3521,12 +3485,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) root->orphan_cleanup_state =ORPHAN_CLEANUP_DONE ; -if (root->orphan_block_rsv) -btrfs_block_rsv_release (fs_info, root->orphan_block_rsv, - (u64)-1 ); - -if (root->orphan_block_rsv || -test_bit (BTRFS_ROOT_ORPHAN_ITEM_INSERTED , &root->state)) { +if (test_bit (BTRFS_ROOT_ORPHAN_ITEM_INSERTED , &root->state)) { trans =btrfs_join_transaction (root);if (!IS_ERR (trans))btrfs_end_transaction (trans);fs/btrfs/transaction.c +0
-1@@ -1250,7 +1250,6 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans)btrfs_free_log (trans, root);btrfs_update_reloc_root (trans, root); -btrfs_orphan_commit_root (trans, root);btrfs_save_ino_cache (root, trans);
Re: [PATCH v3 09/11] Btrfs: fix ENOSPC caused by orphan items reservations
In reply to Omar SandovalQuoted 14 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
Currently, we keep space reserved for all inode orphan items until the inode is evicted (i.e., all references to it are dropped). We hit an issue where an application would keep a bunch of deleted files open (by design) and thus keep a large amount of space reserved, causing ENOSPC errors when other operations tried to reserve space. This long-standing reservation isn't absolutely necessary for a couple of reasons:
- We can almost always make the reservation we need or steal from the global reserve for the orphan item
- If we can't, it's not the end of the world if we drop the orphan item on the floor and let the next mount clean it up
Quoted 4 lines
So, get rid of persistent reservation and just reserve space in
btrfs_evict_inode().Signed-off-by Omar Sandoval <[email protected]>
Quoted 263 lines
--- fs/btrfs/btrfs_inode.h | 17 +++-- fs/btrfs/inode.c | 158 ++++++++++------------------------------- 2 files changed, 46 insertions(+), 129 deletions(-)fs/btrfs/btrfs_inode.h +8
-9@@ -20,15 +20,14 @@* new data the application may have written before commit. */ #define BTRFS_INODE_ORDERED_DATA_CLOSE 0 -#define BTRFS_INODE_ORPHAN_META_RESERVED 1 -#define BTRFS_INODE_DUMMY 2 -#define BTRFS_INODE_IN_DEFRAG 3 -#define BTRFS_INODE_HAS_ASYNC_EXTENT 4 -#define BTRFS_INODE_NEEDS_FULL_SYNC 5 -#define BTRFS_INODE_COPY_EVERYTHING 6 -#define BTRFS_INODE_IN_DELALLOC_LIST 7 -#define BTRFS_INODE_READDIO_NEED_LOCK 8 -#define BTRFS_INODE_HAS_PROPS 9 +#define BTRFS_INODE_DUMMY 1 +#define BTRFS_INODE_IN_DEFRAG 2 +#define BTRFS_INODE_HAS_ASYNC_EXTENT 3 +#define BTRFS_INODE_NEEDS_FULL_SYNC 4 +#define BTRFS_INODE_COPY_EVERYTHING 5 +#define BTRFS_INODE_IN_DELALLOC_LIST 6 +#define BTRFS_INODE_READDIO_NEED_LOCK 7 +#define BTRFS_INODE_HAS_PROPS 8/* in memory btrfs inode */ struct btrfs_inode {fs/btrfs/inode.c +38
-120@@ -3331,77 +3331,16 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans,/* * This creates an orphan entry for the given inode in case something goes wrong * in the middle of an unlink. -* -* NOTE: caller of this function should reserve 5 units of metadata for -* this function. */ int btrfs_orphan_add (struct btrfs_trans_handle *trans, -struct btrfs_inode *inode) +struct btrfs_inode *inode) { -struct btrfs_fs_info *fs_info =btrfs_sb (inode->vfs_inode.i_sb); -struct btrfs_root *root = inode->root; -struct btrfs_block_rsv *block_rsv =NULL ; -int reserve =0 ;int ret; -if (!root->orphan_block_rsv) { - block_rsv =btrfs_alloc_block_rsv (fs_info, -BTRFS_BLOCK_RSV_TEMP ); -if (!block_rsv) -return -ENOMEM ; - } - -if (!test_and_set_bit (BTRFS_INODE_ORPHAN_META_RESERVED , - &inode->runtime_flags)) - reserve =1 ; - -spin_lock (&root->orphan_lock); -/* If someone has created ->orphan_block_rsv, be happy to use it. */ -if (!root->orphan_block_rsv) { - root->orphan_block_rsv = block_rsv; - }else if (block_rsv) { -btrfs_free_block_rsv (fs_info, block_rsv); - block_rsv =NULL ; - } - -atomic_inc (&root->orphan_inodes); -spin_unlock (&root->orphan_lock); - -/* grab metadata reservation from transaction handle */ -if (reserve) { - ret =btrfs_orphan_reserve_metadata (trans, inode); -ASSERT (!ret); -if (ret) { -/* -* dec doesn't need spin_lock as ->orphan_block_rsv -* would be released only if ->orphan_inodes is -* zero. -*/ -atomic_dec (&root->orphan_inodes); -clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , - &inode->runtime_flags); -return ret; - } - } - -/* insert an orphan item to track this unlinked file */ - ret =btrfs_insert_orphan_item (trans, root,btrfs_ino (inode)); -if (ret) { -if (reserve) { -clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , - &inode->runtime_flags); -btrfs_orphan_release_metadata (inode); - } -/* -* btrfs_orphan_commit_root may race with us and set -* ->orphan_block_rsv to zero, in order to avoid that, -* decrease ->orphan_inodes after everything is done. -*/ -atomic_dec (&root->orphan_inodes); -if (ret != -EEXIST ) { -btrfs_abort_transaction (trans, ret); -return ret; - } + ret =btrfs_insert_orphan_item (trans, inode->root,btrfs_ino (inode)); +if (ret && ret != -EEXIST ) { +btrfs_abort_transaction (trans, ret); +return ret; }return 0 ;@@ -3414,24 +3353,7 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans,static int btrfs_orphan_del (struct btrfs_trans_handle *trans,struct btrfs_inode *inode) { -struct btrfs_root *root = inode->root; -int ret =0 ; - -if (trans) - ret =btrfs_del_orphan_item (trans, root,btrfs_ino (inode)); - -if (test_and_clear_bit (BTRFS_INODE_ORPHAN_META_RESERVED , - &inode->runtime_flags)) -btrfs_orphan_release_metadata (inode); - -/* -* btrfs_orphan_commit_root may race with us and set ->orphan_block_rsv -* to zero, in order to avoid that, decrease ->orphan_inodes after -* everything is done. -*/ -atomic_dec (&root->orphan_inodes); - -return ret; +return btrfs_del_orphan_item (trans, inode->root,btrfs_ino (inode)); }/* @@ -3587,8 +3509,6 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)continue ; } -atomic_inc (&root->orphan_inodes); - nr_unlink++;/* this will do delete_inode and everything for us */ @@ -5255,10 +5175,8 @@ void btrfs_evict_inode(struct inode *inode) btrfs_is_free_space_inode(BTRFS_I(inode)))) goto no_delete; -if (is_bad_inode (inode)) { -btrfs_orphan_del (NULL ,BTRFS_I (inode)); +if (is_bad_inode (inode)) goto no_delete; - }/* do we really want it for ->i_nlink > 0 and zero btrfs_root_refs? */ if (!special_file (inode->i_mode))btrfs_wait_ordered_range (inode,0 , (u64)-1 );@@ -5275,16 +5193,12 @@ void btrfs_evict_inode(struct inode *inode) } ret =btrfs_commit_inode_delayed_inode (BTRFS_I (inode)); -if (ret) { -btrfs_orphan_del (NULL ,BTRFS_I (inode)); +if (ret) goto no_delete; - } rsv =btrfs_alloc_block_rsv (fs_info,BTRFS_BLOCK_RSV_TEMP ); -if (!rsv) { -btrfs_orphan_del (NULL ,BTRFS_I (inode)); +if (!rsv) goto no_delete; - } rsv->size = min_size; rsv->failfast =1 ;@@ -5292,46 +5206,50 @@ void btrfs_evict_inode(struct inode *inode)while (1 ) { trans =evict_refill_and_join (root, rsv, min_size); -if (IS_ERR (trans)) { -btrfs_orphan_del (NULL ,BTRFS_I (inode)); -btrfs_free_block_rsv (fs_info, rsv); - goto no_delete; - } +if (IS_ERR (trans)) + goto free_rsv; trans->block_rsv = rsv; ret =btrfs_truncate_inode_items (trans, root, inode,0 ,0 ); -if (ret) { - trans->block_rsv = &fs_info->trans_block_rsv; -btrfs_end_transaction (trans); -btrfs_btree_balance_dirty (fs_info); -if (ret != -ENOSPC && ret != -EAGAIN ) { -btrfs_orphan_del (NULL ,BTRFS_I (inode)); -btrfs_free_block_rsv (fs_info, rsv); - goto no_delete; - } - }else { + trans->block_rsv = &fs_info->trans_block_rsv; +btrfs_end_transaction (trans); +btrfs_btree_balance_dirty (fs_info); +if (ret && ret != -ENOSPC && ret != -EAGAIN ) + goto free_rsv; +else if (!ret)break ; - } } -btrfs_free_block_rsv (fs_info, rsv); -/* -* Errors here aren't a big deal, it just means we leave orphan items -* in the tree. They will be cleaned up on the next mount. +* Errors here aren't a big deal, it just means we leave orphan items in +* the tree. They will be cleaned up on the next mount. If the inode +* number gets reused, cleanup deletes the orphan item without doing +* anything, and unlink reuses the existing orphan item. +* +* If it turns out that we are dropping too many of these, we might want +* to add a mechanism for retrying these after a commit. */ - trans->block_rsv = root->orphan_block_rsv; -btrfs_orphan_del (trans,BTRFS_I (inode)); + trans =evict_refill_and_join (root, rsv, min_size); +if (!IS_ERR (trans)) { + trans->block_rsv = rsv; +btrfs_orphan_del (trans,BTRFS_I (inode)); + trans->block_rsv = &fs_info->trans_block_rsv; +btrfs_end_transaction (trans); + } - trans->block_rsv = &fs_info->trans_block_rsv;if (!(root == fs_info->tree_root || root->root_key.objectid ==BTRFS_TREE_RELOC_OBJECTID ))btrfs_return_ino (root,btrfs_ino (BTRFS_I (inode))); -btrfs_end_transaction (trans); -btrfs_btree_balance_dirty (fs_info); +free_rsv: +btrfs_free_block_rsv (fs_info, rsv); no_delete: +/* +* If we didn't successfully delete, the orphan item will still be in +* the tree and we'll retry on the next mount. Again, we might also want +* to retry these periodically in the future. +*/ btrfs_remove_delayed_node (BTRFS_I (inode));clear_inode (inode); }
Re: [PATCH v3 05/11] Btrfs: get rid of BTRFS_INODE_HAS_ORPHAN_ITEM
In reply to Omar SandovalQuoted 9 lines · Omar Sandoval
fs/btrfs/btrfs_inode.h +0
-3@@ -23,13 +23,12 @@#define BTRFS_INODE_ORPHAN_META_RESERVED 1#define BTRFS_INODE_DUMMY 2#define BTRFS_INODE_IN_DEFRAG 3 -#define BTRFS_INODE_HAS_ORPHAN_ITEM 4 -#define BTRFS_INODE_HAS_ASYNC_EXTENT 5 -#define BTRFS_INODE_NEEDS_FULL_SYNC 6
fs/btrfs/btrfs_inode.h line 31
Quoted 10 lines
fs/btrfs/btrfs_inode.h +6
-4-#define BTRFS_INODE_COPY_EVERYTHING 7 -#define BTRFS_INODE_IN_DELALLOC_LIST 8 -#define BTRFS_INODE_READDIO_NEED_LOCK 9 -#define BTRFS_INODE_HAS_PROPS 10 +#define BTRFS_INODE_HAS_ASYNC_EXTENT 4 +#define BTRFS_INODE_NEEDS_FULL_SYNC 5 +#define BTRFS_INODE_COPY_EVERYTHING 6 +#define BTRFS_INODE_IN_DELALLOC_LIST 7 +#define BTRFS_INODE_READDIO_NEED_LOCK 8 +#define BTRFS_INODE_HAS_PROPS 9
Please keep such changes minimal and only relevant to the purpose of the patch, in this case just remove the BTRFS_INODE_HAS_ORPHAN_ITEM .
There will be a hole left in the sequence but this is not a problem and we're going to convert the defines to enums. The defines are prone to error if the nubmers get accidentally duplicated, like it happend not so long ago with the fsinfo::fs_flags.
Re: [PATCH v3 01/11] Btrfs: remove stale comment referencing vmtruncate()
In reply to Omar SandovalQuoted 6 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
Commit
a41ad394a03b("Btrfs: convert to the new truncate sequence") changedvmtruncate()totruncate_setsize()but didn't update the comment above it.truncate_setsize()never fails (theIS_SWAPFILE()check happens elsewhere), so remove the comment.
There's one more mention of vmtruncate at btrfs_page_mkwrite, can you please remove it and review that the comment is not stale in other respects? Thanks.
Re: [PATCH v3 04/11] Btrfs: stop creating orphan items for truncate
In reply to Omar SandovalQuoted 10 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
Currently, we insert an orphan item during a truncate so that if there's a crash, we don't leak extents past the on-disk
i_size. However, since commit7f4f6e0a3f6d("Btrfs: only updatedisk_i_sizeas we remove extents"), we keepdisk_i_sizein sync with the extent items as we truncate, so orphan cleanup will never have any extents to remove. Don't bother with the superfluous orphan item.Signed-off-by Omar Sandoval <[email protected]>
Thanks,
Josef
Re: [PATCH v3 06/11] Btrfs: delete dead code in btrfs_orphan_commit_root()
In reply to Omar SandovalQuoted 8 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
btrfs_orphan_commit_root()tries to delete an orphan item for a subvolume in the tree root, but we don't actually insert that item in the first place. See commit0a0d4415e338("Btrfs: delete dead code inbtrfs_orphan_add()"). We can get rid of it.Signed-off-by Omar Sandoval <[email protected]>
Thanks,
Josef
Re: [PATCH v3 07/11] Btrfs: don't return ino to ino cache if inode item removal fails
In reply to Omar SandovalQuoted 9 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
In
btrfs_evict_inode(), ifbtrfs_truncate_inode_items()fails, the inode item will still be in the tree but we still return the ino to the ino cache. That will blow up later when someone tries to allocate that ino, so don't return it to the cache.Fixes 581bb050941b ("Btrfs: Cache free inode numbers in memory")Signed-off-by Omar Sandoval <[email protected]>
Thanks,
Josef
Re: [PATCH v3 05/11] Btrfs: get rid of BTRFS_INODE_HAS_ORPHAN_ITEM
In reply to David SterbaI told him to do this, these flags aren't exposed anywhere are they? They are in-kernel specific stuff, please tell me we aren't exposing these via sysfs?
Josef
Quoted 28 lines · David Sterba
Quoted 9 lines · Omar Sandoval
fs/btrfs/btrfs_inode.h +0
-3@@ -23,13 +23,12 @@#define BTRFS_INODE_ORPHAN_META_RESERVED 1#define BTRFS_INODE_DUMMY 2#define BTRFS_INODE_IN_DEFRAG 3 -#define BTRFS_INODE_HAS_ORPHAN_ITEM 4 -#define BTRFS_INODE_HAS_ASYNC_EXTENT 5 -#define BTRFS_INODE_NEEDS_FULL_SYNC 6fs/btrfs/btrfs_inode.h line 31
Quoted 10 lines
fs/btrfs/btrfs_inode.h +6
-4-#define BTRFS_INODE_COPY_EVERYTHING 7 -#define BTRFS_INODE_IN_DELALLOC_LIST 8 -#define BTRFS_INODE_READDIO_NEED_LOCK 9 -#define BTRFS_INODE_HAS_PROPS 10 +#define BTRFS_INODE_HAS_ASYNC_EXTENT 4 +#define BTRFS_INODE_NEEDS_FULL_SYNC 5 +#define BTRFS_INODE_COPY_EVERYTHING 6 +#define BTRFS_INODE_IN_DELALLOC_LIST 7 +#define BTRFS_INODE_READDIO_NEED_LOCK 8 +#define BTRFS_INODE_HAS_PROPS 9Please keep such changes minimal and only relevant to the purpose of the patch, in this case just remove the
BTRFS_INODE_HAS_ORPHAN_ITEM.There will be a hole left in the sequence but this is not a problem and we're going to convert the defines to enums. The defines are prone to error if the nubmers get accidentally duplicated, like it happend not so long ago with the
fsinfo::fs_flags.
Re: [PATCH v3 05/11] Btrfs: get rid of BTRFS_INODE_HAS_ORPHAN_ITEM
Quoted 3 lines · Josef Bacik
I told him to do this, these flags aren't exposed anywhere are they? They are in-kernel specific stuff, please tell me we aren't exposing these via sysfs?
No worries, they're completely internal, just that shifting the number sequence does not need to be in this patch and should be made separately.
Re: [PATCH v3 05/11] Btrfs: get rid of BTRFS_INODE_HAS_ORPHAN_ITEM
Quoted 8 lines · David Sterba
Quoted 3 lines · Josef Bacik
I told him to do this, these flags aren't exposed anywhere are they? They are in-kernel specific stuff, please tell me we aren't exposing these via sysfs?
No worries, they're completely internal, just that shifting the number sequence does not need to be in this patch and should be made separately.
Sure, I can split out the renumbering into a separate patch.
Re: [PATCH v3 01/11] Btrfs: remove stale comment referencing vmtruncate()
In reply to David SterbaQuoted 11 lines · David Sterba
Quoted 6 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
Commit
a41ad394a03b("Btrfs: convert to the new truncate sequence") changedvmtruncate()totruncate_setsize()but didn't update the comment above it.truncate_setsize()never fails (theIS_SWAPFILE()check happens elsewhere), so remove the comment.There's one more mention of vmtruncate at
btrfs_page_mkwrite, can you please remove it and review that the comment is not stale in other respects? Thanks.
Yup, I'll take a look at that.