aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ctree.h1
-rw-r--r--fs/btrfs/inode.c15
-rw-r--r--fs/btrfs/tree-log.c21
3 files changed, 37 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index a9466e346358..49d956b2cf30 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3866,6 +3866,7 @@ int btrfs_prealloc_file_range_trans(struct inode *inode,
3866 struct btrfs_trans_handle *trans, int mode, 3866 struct btrfs_trans_handle *trans, int mode,
3867 u64 start, u64 num_bytes, u64 min_size, 3867 u64 start, u64 num_bytes, u64 min_size,
3868 loff_t actual_len, u64 *alloc_hint); 3868 loff_t actual_len, u64 *alloc_hint);
3869int btrfs_inode_check_errors(struct inode *inode);
3869extern const struct dentry_operations btrfs_dentry_operations; 3870extern const struct dentry_operations btrfs_dentry_operations;
3870 3871
3871/* ioctl.c */ 3872/* ioctl.c */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index cb5978a4a277..a5374c2bb943 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9464,6 +9464,21 @@ out_inode:
9464 9464
9465} 9465}
9466 9466
9467/* Inspired by filemap_check_errors() */
9468int btrfs_inode_check_errors(struct inode *inode)
9469{
9470 int ret = 0;
9471
9472 if (test_bit(AS_ENOSPC, &inode->i_mapping->flags) &&
9473 test_and_clear_bit(AS_ENOSPC, &inode->i_mapping->flags))
9474 ret = -ENOSPC;
9475 if (test_bit(AS_EIO, &inode->i_mapping->flags) &&
9476 test_and_clear_bit(AS_EIO, &inode->i_mapping->flags))
9477 ret = -EIO;
9478
9479 return ret;
9480}
9481
9467static const struct inode_operations btrfs_dir_inode_operations = { 9482static const struct inode_operations btrfs_dir_inode_operations = {
9468 .getattr = btrfs_getattr, 9483 .getattr = btrfs_getattr,
9469 .lookup = btrfs_lookup, 9484 .lookup = btrfs_lookup,
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 3883d0febd82..9a02da16f2be 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -3635,6 +3635,12 @@ static int wait_ordered_extents(struct btrfs_trans_handle *trans,
3635 test_bit(BTRFS_ORDERED_IOERR, &ordered->flags))); 3635 test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)));
3636 3636
3637 if (test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)) { 3637 if (test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)) {
3638 /*
3639 * Clear the AS_EIO/AS_ENOSPC flags from the inode's
3640 * i_mapping flags, so that the next fsync won't get
3641 * an outdated io error too.
3642 */
3643 btrfs_inode_check_errors(inode);
3638 *ordered_io_error = true; 3644 *ordered_io_error = true;
3639 break; 3645 break;
3640 } 3646 }
@@ -4098,6 +4104,21 @@ log_extents:
4098 btrfs_release_path(path); 4104 btrfs_release_path(path);
4099 btrfs_release_path(dst_path); 4105 btrfs_release_path(dst_path);
4100 if (fast_search) { 4106 if (fast_search) {
4107 /*
4108 * Some ordered extents started by fsync might have completed
4109 * before we collected the ordered extents in logged_list, which
4110 * means they're gone, not in our logged_list nor in the inode's
4111 * ordered tree. We want the application/user space to know an
4112 * error happened while attempting to persist file data so that
4113 * it can take proper action. If such error happened, we leave
4114 * without writing to the log tree and the fsync must report the
4115 * file data write error and not commit the current transaction.
4116 */
4117 err = btrfs_inode_check_errors(inode);
4118 if (err) {
4119 ctx->io_err = err;
4120 goto out_unlock;
4121 }
4101 ret = btrfs_log_changed_extents(trans, root, inode, dst_path, 4122 ret = btrfs_log_changed_extents(trans, root, inode, dst_path,
4102 &logged_list, ctx); 4123 &logged_list, ctx);
4103 if (ret) { 4124 if (ret) {