diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 67 |
1 files changed, 57 insertions, 10 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 43c6781af654..9f6372dd0eab 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -4772,6 +4772,42 @@ out_unlock: | |||
4772 | } | 4772 | } |
4773 | 4773 | ||
4774 | /* | 4774 | /* |
4775 | * Check if we must fallback to a transaction commit when logging an inode. | ||
4776 | * This must be called after logging the inode and is used only in the context | ||
4777 | * when fsyncing an inode requires the need to log some other inode - in which | ||
4778 | * case we can't lock the i_mutex of each other inode we need to log as that | ||
4779 | * can lead to deadlocks with concurrent fsync against other inodes (as we can | ||
4780 | * log inodes up or down in the hierarchy) or rename operations for example. So | ||
4781 | * we take the log_mutex of the inode after we have logged it and then check for | ||
4782 | * its last_unlink_trans value - this is safe because any task setting | ||
4783 | * last_unlink_trans must take the log_mutex and it must do this before it does | ||
4784 | * the actual unlink operation, so if we do this check before a concurrent task | ||
4785 | * sets last_unlink_trans it means we've logged a consistent version/state of | ||
4786 | * all the inode items, otherwise we are not sure and must do a transaction | ||
4787 | * commit (the concurrent task migth have only updated last_unlink_trans before | ||
4788 | * we logged the inode or it might have also done the unlink). | ||
4789 | */ | ||
4790 | static bool btrfs_must_commit_transaction(struct btrfs_trans_handle *trans, | ||
4791 | struct inode *inode) | ||
4792 | { | ||
4793 | struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; | ||
4794 | bool ret = false; | ||
4795 | |||
4796 | mutex_lock(&BTRFS_I(inode)->log_mutex); | ||
4797 | if (BTRFS_I(inode)->last_unlink_trans > fs_info->last_trans_committed) { | ||
4798 | /* | ||
4799 | * Make sure any commits to the log are forced to be full | ||
4800 | * commits. | ||
4801 | */ | ||
4802 | btrfs_set_log_full_commit(fs_info, trans); | ||
4803 | ret = true; | ||
4804 | } | ||
4805 | mutex_unlock(&BTRFS_I(inode)->log_mutex); | ||
4806 | |||
4807 | return ret; | ||
4808 | } | ||
4809 | |||
4810 | /* | ||
4775 | * follow the dentry parent pointers up the chain and see if any | 4811 | * follow the dentry parent pointers up the chain and see if any |
4776 | * of the directories in it require a full commit before they can | 4812 | * of the directories in it require a full commit before they can |
4777 | * be logged. Returns zero if nothing special needs to be done or 1 if | 4813 | * be logged. Returns zero if nothing special needs to be done or 1 if |
@@ -4784,7 +4820,6 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans, | |||
4784 | u64 last_committed) | 4820 | u64 last_committed) |
4785 | { | 4821 | { |
4786 | int ret = 0; | 4822 | int ret = 0; |
4787 | struct btrfs_root *root; | ||
4788 | struct dentry *old_parent = NULL; | 4823 | struct dentry *old_parent = NULL; |
4789 | struct inode *orig_inode = inode; | 4824 | struct inode *orig_inode = inode; |
4790 | 4825 | ||
@@ -4816,14 +4851,7 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans, | |||
4816 | BTRFS_I(inode)->logged_trans = trans->transid; | 4851 | BTRFS_I(inode)->logged_trans = trans->transid; |
4817 | smp_mb(); | 4852 | smp_mb(); |
4818 | 4853 | ||
4819 | if (BTRFS_I(inode)->last_unlink_trans > last_committed) { | 4854 | if (btrfs_must_commit_transaction(trans, inode)) { |
4820 | root = BTRFS_I(inode)->root; | ||
4821 | |||
4822 | /* | ||
4823 | * make sure any commits to the log are forced | ||
4824 | * to be full commits | ||
4825 | */ | ||
4826 | btrfs_set_log_full_commit(root->fs_info, trans); | ||
4827 | ret = 1; | 4855 | ret = 1; |
4828 | break; | 4856 | break; |
4829 | } | 4857 | } |
@@ -4982,6 +5010,9 @@ process_leaf: | |||
4982 | btrfs_release_path(path); | 5010 | btrfs_release_path(path); |
4983 | ret = btrfs_log_inode(trans, root, di_inode, | 5011 | ret = btrfs_log_inode(trans, root, di_inode, |
4984 | log_mode, 0, LLONG_MAX, ctx); | 5012 | log_mode, 0, LLONG_MAX, ctx); |
5013 | if (!ret && | ||
5014 | btrfs_must_commit_transaction(trans, di_inode)) | ||
5015 | ret = 1; | ||
4985 | iput(di_inode); | 5016 | iput(di_inode); |
4986 | if (ret) | 5017 | if (ret) |
4987 | goto next_dir_inode; | 5018 | goto next_dir_inode; |
@@ -5096,6 +5127,9 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans, | |||
5096 | 5127 | ||
5097 | ret = btrfs_log_inode(trans, root, dir_inode, | 5128 | ret = btrfs_log_inode(trans, root, dir_inode, |
5098 | LOG_INODE_ALL, 0, LLONG_MAX, ctx); | 5129 | LOG_INODE_ALL, 0, LLONG_MAX, ctx); |
5130 | if (!ret && | ||
5131 | btrfs_must_commit_transaction(trans, dir_inode)) | ||
5132 | ret = 1; | ||
5099 | iput(dir_inode); | 5133 | iput(dir_inode); |
5100 | if (ret) | 5134 | if (ret) |
5101 | goto out; | 5135 | goto out; |
@@ -5447,6 +5481,9 @@ error: | |||
5447 | * They revolve around files there were unlinked from the directory, and | 5481 | * They revolve around files there were unlinked from the directory, and |
5448 | * this function updates the parent directory so that a full commit is | 5482 | * this function updates the parent directory so that a full commit is |
5449 | * properly done if it is fsync'd later after the unlinks are done. | 5483 | * properly done if it is fsync'd later after the unlinks are done. |
5484 | * | ||
5485 | * Must be called before the unlink operations (updates to the subvolume tree, | ||
5486 | * inodes, etc) are done. | ||
5450 | */ | 5487 | */ |
5451 | void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans, | 5488 | void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans, |
5452 | struct inode *dir, struct inode *inode, | 5489 | struct inode *dir, struct inode *inode, |
@@ -5462,8 +5499,11 @@ void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans, | |||
5462 | * into the file. When the file is logged we check it and | 5499 | * into the file. When the file is logged we check it and |
5463 | * don't log the parents if the file is fully on disk. | 5500 | * don't log the parents if the file is fully on disk. |
5464 | */ | 5501 | */ |
5465 | if (S_ISREG(inode->i_mode)) | 5502 | if (S_ISREG(inode->i_mode)) { |
5503 | mutex_lock(&BTRFS_I(inode)->log_mutex); | ||
5466 | BTRFS_I(inode)->last_unlink_trans = trans->transid; | 5504 | BTRFS_I(inode)->last_unlink_trans = trans->transid; |
5505 | mutex_unlock(&BTRFS_I(inode)->log_mutex); | ||
5506 | } | ||
5467 | 5507 | ||
5468 | /* | 5508 | /* |
5469 | * if this directory was already logged any new | 5509 | * if this directory was already logged any new |
@@ -5494,7 +5534,9 @@ void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans, | |||
5494 | return; | 5534 | return; |
5495 | 5535 | ||
5496 | record: | 5536 | record: |
5537 | mutex_lock(&BTRFS_I(dir)->log_mutex); | ||
5497 | BTRFS_I(dir)->last_unlink_trans = trans->transid; | 5538 | BTRFS_I(dir)->last_unlink_trans = trans->transid; |
5539 | mutex_unlock(&BTRFS_I(dir)->log_mutex); | ||
5498 | } | 5540 | } |
5499 | 5541 | ||
5500 | /* | 5542 | /* |
@@ -5505,11 +5547,16 @@ record: | |||
5505 | * corresponding to the deleted snapshot's root, which could lead to replaying | 5547 | * corresponding to the deleted snapshot's root, which could lead to replaying |
5506 | * it after replaying the log tree of the parent directory (which would replay | 5548 | * it after replaying the log tree of the parent directory (which would replay |
5507 | * the snapshot delete operation). | 5549 | * the snapshot delete operation). |
5550 | * | ||
5551 | * Must be called before the actual snapshot destroy operation (updates to the | ||
5552 | * parent root and tree of tree roots trees, etc) are done. | ||
5508 | */ | 5553 | */ |
5509 | void btrfs_record_snapshot_destroy(struct btrfs_trans_handle *trans, | 5554 | void btrfs_record_snapshot_destroy(struct btrfs_trans_handle *trans, |
5510 | struct inode *dir) | 5555 | struct inode *dir) |
5511 | { | 5556 | { |
5557 | mutex_lock(&BTRFS_I(dir)->log_mutex); | ||
5512 | BTRFS_I(dir)->last_unlink_trans = trans->transid; | 5558 | BTRFS_I(dir)->last_unlink_trans = trans->transid; |
5559 | mutex_unlock(&BTRFS_I(dir)->log_mutex); | ||
5513 | } | 5560 | } |
5514 | 5561 | ||
5515 | /* | 5562 | /* |