summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2019-02-18 12:38:46 -0500
committerDarrick J. Wong <darrick.wong@oracle.com>2019-02-21 10:55:07 -0500
commit60271ab79d40b99ce6cb28d8dc48aa5e9ffb6df3 (patch)
tree532b032a3c46bc1dbe5c6ba17e2e30edfb9b1996
parent16be1433737ee46f88da57d47f594c4fc1376538 (diff)
xfs: fix SEEK_DATA for speculative COW fork preallocation
We speculatively allocate extents in the COW fork to reduce fragmentation. But when we write data into such COW fork blocks that do now shadow an allocation in the data fork SEEK_DATA will not correctly report it, as it only looks at the data fork extents. The only reason why that hasn't been an issue so far is because we even use these speculative COW fork preallocations over holes in the data fork at all for buffered writes, and blocks in the COW fork that are written by direct writes are moved into the data fork immediately at I/O completion time. Add a new set of iomap_ops for SEEK_HOLE/SEEK_DATA which looks into both the COW and data fork, and reports all COW extents as unwritten to the iomap layer. While this isn't strictly true for COW fork extents that were already converted to real extents, the practical semantics that you can't read data from them until they are moved into the data fork are very similar, and this will force the iomap layer into probing the extents for actually present data. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/xfs_file.c4
-rw-r--r--fs/xfs/xfs_iomap.c86
-rw-r--r--fs/xfs/xfs_iomap.h1
3 files changed, 89 insertions, 2 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index e47425071e65..1d07dcfbbff3 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -1068,10 +1068,10 @@ xfs_file_llseek(
1068 default: 1068 default:
1069 return generic_file_llseek(file, offset, whence); 1069 return generic_file_llseek(file, offset, whence);
1070 case SEEK_HOLE: 1070 case SEEK_HOLE:
1071 offset = iomap_seek_hole(inode, offset, &xfs_iomap_ops); 1071 offset = iomap_seek_hole(inode, offset, &xfs_seek_iomap_ops);
1072 break; 1072 break;
1073 case SEEK_DATA: 1073 case SEEK_DATA:
1074 offset = iomap_seek_data(inode, offset, &xfs_iomap_ops); 1074 offset = iomap_seek_data(inode, offset, &xfs_seek_iomap_ops);
1075 break; 1075 break;
1076 } 1076 }
1077 1077
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 284c5e68f695..df6eda336f17 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -1069,6 +1069,92 @@ const struct iomap_ops xfs_iomap_ops = {
1069}; 1069};
1070 1070
1071static int 1071static int
1072xfs_seek_iomap_begin(
1073 struct inode *inode,
1074 loff_t offset,
1075 loff_t length,
1076 unsigned flags,
1077 struct iomap *iomap)
1078{
1079 struct xfs_inode *ip = XFS_I(inode);
1080 struct xfs_mount *mp = ip->i_mount;
1081 xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
1082 xfs_fileoff_t end_fsb = XFS_B_TO_FSB(mp, offset + length);
1083 xfs_fileoff_t cow_fsb = NULLFILEOFF, data_fsb = NULLFILEOFF;
1084 struct xfs_iext_cursor icur;
1085 struct xfs_bmbt_irec imap, cmap;
1086 int error = 0;
1087 unsigned lockmode;
1088
1089 if (XFS_FORCED_SHUTDOWN(mp))
1090 return -EIO;
1091
1092 lockmode = xfs_ilock_data_map_shared(ip);
1093 if (!(ip->i_df.if_flags & XFS_IFEXTENTS)) {
1094 error = xfs_iread_extents(NULL, ip, XFS_DATA_FORK);
1095 if (error)
1096 goto out_unlock;
1097 }
1098
1099 if (xfs_iext_lookup_extent(ip, &ip->i_df, offset_fsb, &icur, &imap)) {
1100 /*
1101 * If we found a data extent we are done.
1102 */
1103 if (imap.br_startoff <= offset_fsb)
1104 goto done;
1105 data_fsb = imap.br_startoff;
1106 } else {
1107 /*
1108 * Fake a hole until the end of the file.
1109 */
1110 data_fsb = min(XFS_B_TO_FSB(mp, offset + length),
1111 XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes));
1112 }
1113
1114 /*
1115 * If a COW fork extent covers the hole, report it - capped to the next
1116 * data fork extent:
1117 */
1118 if (xfs_inode_has_cow_data(ip) &&
1119 xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &cmap))
1120 cow_fsb = cmap.br_startoff;
1121 if (cow_fsb != NULLFILEOFF && cow_fsb <= offset_fsb) {
1122 if (data_fsb < cow_fsb + cmap.br_blockcount)
1123 end_fsb = min(end_fsb, data_fsb);
1124 xfs_trim_extent(&cmap, offset_fsb, end_fsb);
1125 error = xfs_bmbt_to_iomap(ip, iomap, &cmap, true);
1126 /*
1127 * This is a COW extent, so we must probe the page cache
1128 * because there could be dirty page cache being backed
1129 * by this extent.
1130 */
1131 iomap->type = IOMAP_UNWRITTEN;
1132 goto out_unlock;
1133 }
1134
1135 /*
1136 * Else report a hole, capped to the next found data or COW extent.
1137 */
1138 if (cow_fsb != NULLFILEOFF && cow_fsb < data_fsb)
1139 imap.br_blockcount = cow_fsb - offset_fsb;
1140 else
1141 imap.br_blockcount = data_fsb - offset_fsb;
1142 imap.br_startoff = offset_fsb;
1143 imap.br_startblock = HOLESTARTBLOCK;
1144 imap.br_state = XFS_EXT_NORM;
1145done:
1146 xfs_trim_extent(&imap, offset_fsb, end_fsb);
1147 error = xfs_bmbt_to_iomap(ip, iomap, &imap, false);
1148out_unlock:
1149 xfs_iunlock(ip, lockmode);
1150 return error;
1151}
1152
1153const struct iomap_ops xfs_seek_iomap_ops = {
1154 .iomap_begin = xfs_seek_iomap_begin,
1155};
1156
1157static int
1072xfs_xattr_iomap_begin( 1158xfs_xattr_iomap_begin(
1073 struct inode *inode, 1159 struct inode *inode,
1074 loff_t offset, 1160 loff_t offset,
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index 37b584c3069b..5c2f6aa6d78f 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -40,6 +40,7 @@ xfs_aligned_fsb_count(
40} 40}
41 41
42extern const struct iomap_ops xfs_iomap_ops; 42extern const struct iomap_ops xfs_iomap_ops;
43extern const struct iomap_ops xfs_seek_iomap_ops;
43extern const struct iomap_ops xfs_xattr_iomap_ops; 44extern const struct iomap_ops xfs_xattr_iomap_ops;
44 45
45#endif /* __XFS_IOMAP_H__*/ 46#endif /* __XFS_IOMAP_H__*/