diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 74 |
1 files changed, 27 insertions, 47 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index e692eea87af6..8aaca5c6af94 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -4141,6 +4141,7 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, | |||
4141 | 4141 | ||
4142 | INIT_LIST_HEAD(&extents); | 4142 | INIT_LIST_HEAD(&extents); |
4143 | 4143 | ||
4144 | down_write(&BTRFS_I(inode)->dio_sem); | ||
4144 | write_lock(&tree->lock); | 4145 | write_lock(&tree->lock); |
4145 | test_gen = root->fs_info->last_trans_committed; | 4146 | test_gen = root->fs_info->last_trans_committed; |
4146 | 4147 | ||
@@ -4169,13 +4170,20 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, | |||
4169 | } | 4170 | } |
4170 | 4171 | ||
4171 | list_sort(NULL, &extents, extent_cmp); | 4172 | list_sort(NULL, &extents, extent_cmp); |
4173 | btrfs_get_logged_extents(inode, logged_list, start, end); | ||
4172 | /* | 4174 | /* |
4173 | * Collect any new ordered extents within the range. This is to | 4175 | * Some ordered extents started by fsync might have completed |
4174 | * prevent logging file extent items without waiting for the disk | 4176 | * before we could collect them into the list logged_list, which |
4175 | * location they point to being written. We do this only to deal | 4177 | * means they're gone, not in our logged_list nor in the inode's |
4176 | * with races against concurrent lockless direct IO writes. | 4178 | * ordered tree. We want the application/user space to know an |
4179 | * error happened while attempting to persist file data so that | ||
4180 | * it can take proper action. If such error happened, we leave | ||
4181 | * without writing to the log tree and the fsync must report the | ||
4182 | * file data write error and not commit the current transaction. | ||
4177 | */ | 4183 | */ |
4178 | btrfs_get_logged_extents(inode, logged_list, start, end); | 4184 | ret = btrfs_inode_check_errors(inode); |
4185 | if (ret) | ||
4186 | ctx->io_err = ret; | ||
4179 | process: | 4187 | process: |
4180 | while (!list_empty(&extents)) { | 4188 | while (!list_empty(&extents)) { |
4181 | em = list_entry(extents.next, struct extent_map, list); | 4189 | em = list_entry(extents.next, struct extent_map, list); |
@@ -4202,6 +4210,7 @@ process: | |||
4202 | } | 4210 | } |
4203 | WARN_ON(!list_empty(&extents)); | 4211 | WARN_ON(!list_empty(&extents)); |
4204 | write_unlock(&tree->lock); | 4212 | write_unlock(&tree->lock); |
4213 | up_write(&BTRFS_I(inode)->dio_sem); | ||
4205 | 4214 | ||
4206 | btrfs_release_path(path); | 4215 | btrfs_release_path(path); |
4207 | return ret; | 4216 | return ret; |
@@ -4623,23 +4632,6 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, | |||
4623 | mutex_lock(&BTRFS_I(inode)->log_mutex); | 4632 | mutex_lock(&BTRFS_I(inode)->log_mutex); |
4624 | 4633 | ||
4625 | /* | 4634 | /* |
4626 | * Collect ordered extents only if we are logging data. This is to | ||
4627 | * ensure a subsequent request to log this inode in LOG_INODE_ALL mode | ||
4628 | * will process the ordered extents if they still exists at the time, | ||
4629 | * because when we collect them we test and set for the flag | ||
4630 | * BTRFS_ORDERED_LOGGED to prevent multiple log requests to process the | ||
4631 | * same ordered extents. The consequence for the LOG_INODE_ALL log mode | ||
4632 | * not processing the ordered extents is that we end up logging the | ||
4633 | * corresponding file extent items, based on the extent maps in the | ||
4634 | * inode's extent_map_tree's modified_list, without logging the | ||
4635 | * respective checksums (since the may still be only attached to the | ||
4636 | * ordered extents and have not been inserted in the csum tree by | ||
4637 | * btrfs_finish_ordered_io() yet). | ||
4638 | */ | ||
4639 | if (inode_only == LOG_INODE_ALL) | ||
4640 | btrfs_get_logged_extents(inode, &logged_list, start, end); | ||
4641 | |||
4642 | /* | ||
4643 | * a brute force approach to making sure we get the most uptodate | 4635 | * a brute force approach to making sure we get the most uptodate |
4644 | * copies of everything. | 4636 | * copies of everything. |
4645 | */ | 4637 | */ |
@@ -4846,21 +4838,6 @@ log_extents: | |||
4846 | goto out_unlock; | 4838 | goto out_unlock; |
4847 | } | 4839 | } |
4848 | if (fast_search) { | 4840 | if (fast_search) { |
4849 | /* | ||
4850 | * Some ordered extents started by fsync might have completed | ||
4851 | * before we collected the ordered extents in logged_list, which | ||
4852 | * means they're gone, not in our logged_list nor in the inode's | ||
4853 | * ordered tree. We want the application/user space to know an | ||
4854 | * error happened while attempting to persist file data so that | ||
4855 | * it can take proper action. If such error happened, we leave | ||
4856 | * without writing to the log tree and the fsync must report the | ||
4857 | * file data write error and not commit the current transaction. | ||
4858 | */ | ||
4859 | err = btrfs_inode_check_errors(inode); | ||
4860 | if (err) { | ||
4861 | ctx->io_err = err; | ||
4862 | goto out_unlock; | ||
4863 | } | ||
4864 | ret = btrfs_log_changed_extents(trans, root, inode, dst_path, | 4841 | ret = btrfs_log_changed_extents(trans, root, inode, dst_path, |
4865 | &logged_list, ctx, start, end); | 4842 | &logged_list, ctx, start, end); |
4866 | if (ret) { | 4843 | if (ret) { |
@@ -5158,7 +5135,7 @@ process_leaf: | |||
5158 | } | 5135 | } |
5159 | 5136 | ||
5160 | ctx->log_new_dentries = false; | 5137 | ctx->log_new_dentries = false; |
5161 | if (type == BTRFS_FT_DIR) | 5138 | if (type == BTRFS_FT_DIR || type == BTRFS_FT_SYMLINK) |
5162 | log_mode = LOG_INODE_ALL; | 5139 | log_mode = LOG_INODE_ALL; |
5163 | btrfs_release_path(path); | 5140 | btrfs_release_path(path); |
5164 | ret = btrfs_log_inode(trans, root, di_inode, | 5141 | ret = btrfs_log_inode(trans, root, di_inode, |
@@ -5278,11 +5255,16 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans, | |||
5278 | if (IS_ERR(dir_inode)) | 5255 | if (IS_ERR(dir_inode)) |
5279 | continue; | 5256 | continue; |
5280 | 5257 | ||
5258 | if (ctx) | ||
5259 | ctx->log_new_dentries = false; | ||
5281 | ret = btrfs_log_inode(trans, root, dir_inode, | 5260 | ret = btrfs_log_inode(trans, root, dir_inode, |
5282 | LOG_INODE_ALL, 0, LLONG_MAX, ctx); | 5261 | LOG_INODE_ALL, 0, LLONG_MAX, ctx); |
5283 | if (!ret && | 5262 | if (!ret && |
5284 | btrfs_must_commit_transaction(trans, dir_inode)) | 5263 | btrfs_must_commit_transaction(trans, dir_inode)) |
5285 | ret = 1; | 5264 | ret = 1; |
5265 | if (!ret && ctx && ctx->log_new_dentries) | ||
5266 | ret = log_new_dir_dentries(trans, root, | ||
5267 | dir_inode, ctx); | ||
5286 | iput(dir_inode); | 5268 | iput(dir_inode); |
5287 | if (ret) | 5269 | if (ret) |
5288 | goto out; | 5270 | goto out; |
@@ -5519,7 +5501,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree) | |||
5519 | 5501 | ||
5520 | ret = walk_log_tree(trans, log_root_tree, &wc); | 5502 | ret = walk_log_tree(trans, log_root_tree, &wc); |
5521 | if (ret) { | 5503 | if (ret) { |
5522 | btrfs_std_error(fs_info, ret, "Failed to pin buffers while " | 5504 | btrfs_handle_fs_error(fs_info, ret, "Failed to pin buffers while " |
5523 | "recovering log root tree."); | 5505 | "recovering log root tree."); |
5524 | goto error; | 5506 | goto error; |
5525 | } | 5507 | } |
@@ -5533,7 +5515,7 @@ again: | |||
5533 | ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0); | 5515 | ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0); |
5534 | 5516 | ||
5535 | if (ret < 0) { | 5517 | if (ret < 0) { |
5536 | btrfs_std_error(fs_info, ret, | 5518 | btrfs_handle_fs_error(fs_info, ret, |
5537 | "Couldn't find tree log root."); | 5519 | "Couldn't find tree log root."); |
5538 | goto error; | 5520 | goto error; |
5539 | } | 5521 | } |
@@ -5551,7 +5533,7 @@ again: | |||
5551 | log = btrfs_read_fs_root(log_root_tree, &found_key); | 5533 | log = btrfs_read_fs_root(log_root_tree, &found_key); |
5552 | if (IS_ERR(log)) { | 5534 | if (IS_ERR(log)) { |
5553 | ret = PTR_ERR(log); | 5535 | ret = PTR_ERR(log); |
5554 | btrfs_std_error(fs_info, ret, | 5536 | btrfs_handle_fs_error(fs_info, ret, |
5555 | "Couldn't read tree log root."); | 5537 | "Couldn't read tree log root."); |
5556 | goto error; | 5538 | goto error; |
5557 | } | 5539 | } |
@@ -5566,7 +5548,7 @@ again: | |||
5566 | free_extent_buffer(log->node); | 5548 | free_extent_buffer(log->node); |
5567 | free_extent_buffer(log->commit_root); | 5549 | free_extent_buffer(log->commit_root); |
5568 | kfree(log); | 5550 | kfree(log); |
5569 | btrfs_std_error(fs_info, ret, "Couldn't read target root " | 5551 | btrfs_handle_fs_error(fs_info, ret, "Couldn't read target root " |
5570 | "for tree log recovery."); | 5552 | "for tree log recovery."); |
5571 | goto error; | 5553 | goto error; |
5572 | } | 5554 | } |
@@ -5652,11 +5634,9 @@ void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans, | |||
5652 | * into the file. When the file is logged we check it and | 5634 | * into the file. When the file is logged we check it and |
5653 | * don't log the parents if the file is fully on disk. | 5635 | * don't log the parents if the file is fully on disk. |
5654 | */ | 5636 | */ |
5655 | if (S_ISREG(inode->i_mode)) { | 5637 | mutex_lock(&BTRFS_I(inode)->log_mutex); |
5656 | mutex_lock(&BTRFS_I(inode)->log_mutex); | 5638 | BTRFS_I(inode)->last_unlink_trans = trans->transid; |
5657 | BTRFS_I(inode)->last_unlink_trans = trans->transid; | 5639 | mutex_unlock(&BTRFS_I(inode)->log_mutex); |
5658 | mutex_unlock(&BTRFS_I(inode)->log_mutex); | ||
5659 | } | ||
5660 | 5640 | ||
5661 | /* | 5641 | /* |
5662 | * if this directory was already logged any new | 5642 | * if this directory was already logged any new |