@@ -1091,6 +1091,29 @@ const struct iomap_ops xfs_zoned_direct_write_iomap_ops = {
10911091};
10921092#endif /* CONFIG_XFS_RT */
10931093
1094+ #ifdef DEBUG
1095+ static void
1096+ xfs_check_atomic_cow_conversion (
1097+ struct xfs_inode * ip ,
1098+ xfs_fileoff_t offset_fsb ,
1099+ xfs_filblks_t count_fsb ,
1100+ const struct xfs_bmbt_irec * cmap )
1101+ {
1102+ struct xfs_iext_cursor icur ;
1103+ struct xfs_bmbt_irec cmap2 = { };
1104+
1105+ if (xfs_iext_lookup_extent (ip , ip -> i_cowfp , offset_fsb , & icur , & cmap2 ))
1106+ xfs_trim_extent (& cmap2 , offset_fsb , count_fsb );
1107+
1108+ ASSERT (cmap2 .br_startoff == cmap -> br_startoff );
1109+ ASSERT (cmap2 .br_blockcount == cmap -> br_blockcount );
1110+ ASSERT (cmap2 .br_startblock == cmap -> br_startblock );
1111+ ASSERT (cmap2 .br_state == cmap -> br_state );
1112+ }
1113+ #else
1114+ # define xfs_check_atomic_cow_conversion (...) ((void)0)
1115+ #endif
1116+
10941117static int
10951118xfs_atomic_write_cow_iomap_begin (
10961119 struct inode * inode ,
@@ -1102,9 +1125,10 @@ xfs_atomic_write_cow_iomap_begin(
11021125{
11031126 struct xfs_inode * ip = XFS_I (inode );
11041127 struct xfs_mount * mp = ip -> i_mount ;
1105- const xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT (mp , offset );
1106- xfs_fileoff_t end_fsb = xfs_iomap_end_fsb (mp , offset , length );
1107- xfs_filblks_t count_fsb = end_fsb - offset_fsb ;
1128+ const xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT (mp , offset );
1129+ const xfs_fileoff_t end_fsb = XFS_B_TO_FSB (mp , offset + length );
1130+ const xfs_filblks_t count_fsb = end_fsb - offset_fsb ;
1131+ xfs_filblks_t hole_count_fsb ;
11081132 int nmaps = 1 ;
11091133 xfs_filblks_t resaligned ;
11101134 struct xfs_bmbt_irec cmap ;
@@ -1130,7 +1154,7 @@ xfs_atomic_write_cow_iomap_begin(
11301154 return - EAGAIN ;
11311155
11321156 trace_xfs_iomap_atomic_write_cow (ip , offset , length );
1133-
1157+ retry :
11341158 xfs_ilock (ip , XFS_ILOCK_EXCL );
11351159
11361160 if (!ip -> i_cowfp ) {
@@ -1141,14 +1165,22 @@ xfs_atomic_write_cow_iomap_begin(
11411165 if (!xfs_iext_lookup_extent (ip , ip -> i_cowfp , offset_fsb , & icur , & cmap ))
11421166 cmap .br_startoff = end_fsb ;
11431167 if (cmap .br_startoff <= offset_fsb ) {
1168+ if (isnullstartblock (cmap .br_startblock ))
1169+ goto convert_delay ;
1170+
1171+ /*
1172+ * cmap could extend outside the write range due to previous
1173+ * speculative preallocations. We must trim cmap to the write
1174+ * range because the cow fork treats written mappings to mean
1175+ * "write in progress".
1176+ */
11441177 xfs_trim_extent (& cmap , offset_fsb , count_fsb );
11451178 goto found ;
11461179 }
11471180
1148- end_fsb = cmap .br_startoff ;
1149- count_fsb = end_fsb - offset_fsb ;
1181+ hole_count_fsb = cmap .br_startoff - offset_fsb ;
11501182
1151- resaligned = xfs_aligned_fsb_count (offset_fsb , count_fsb ,
1183+ resaligned = xfs_aligned_fsb_count (offset_fsb , hole_count_fsb ,
11521184 xfs_get_cowextsz_hint (ip ));
11531185 xfs_iunlock (ip , XFS_ILOCK_EXCL );
11541186
@@ -1169,8 +1201,10 @@ xfs_atomic_write_cow_iomap_begin(
11691201 if (!xfs_iext_lookup_extent (ip , ip -> i_cowfp , offset_fsb , & icur , & cmap ))
11701202 cmap .br_startoff = end_fsb ;
11711203 if (cmap .br_startoff <= offset_fsb ) {
1172- xfs_trim_extent (& cmap , offset_fsb , count_fsb );
11731204 xfs_trans_cancel (tp );
1205+ if (isnullstartblock (cmap .br_startblock ))
1206+ goto convert_delay ;
1207+ xfs_trim_extent (& cmap , offset_fsb , count_fsb );
11741208 goto found ;
11751209 }
11761210
@@ -1182,7 +1216,7 @@ xfs_atomic_write_cow_iomap_begin(
11821216 * atomic writes to that same range will be aligned (and don't require
11831217 * this COW-based method).
11841218 */
1185- error = xfs_bmapi_write (tp , ip , offset_fsb , count_fsb ,
1219+ error = xfs_bmapi_write (tp , ip , offset_fsb , hole_count_fsb ,
11861220 XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC |
11871221 XFS_BMAPI_EXTSZALIGN , 0 , & cmap , & nmaps );
11881222 if (error ) {
@@ -1195,21 +1229,43 @@ xfs_atomic_write_cow_iomap_begin(
11951229 if (error )
11961230 goto out_unlock ;
11971231
1232+ /*
1233+ * cmap could map more blocks than the range we passed into bmapi_write
1234+ * because of EXTSZALIGN or adjacent pre-existing unwritten mappings
1235+ * that were merged. Trim cmap to the original write range so that we
1236+ * don't convert more than we were asked to do for this write.
1237+ */
1238+ xfs_trim_extent (& cmap , offset_fsb , count_fsb );
1239+
11981240found :
11991241 if (cmap .br_state != XFS_EXT_NORM ) {
1200- error = xfs_reflink_convert_cow_locked (ip , offset_fsb ,
1201- count_fsb );
1242+ error = xfs_reflink_convert_cow_locked (ip , cmap . br_startoff ,
1243+ cmap . br_blockcount );
12021244 if (error )
12031245 goto out_unlock ;
12041246 cmap .br_state = XFS_EXT_NORM ;
1247+ xfs_check_atomic_cow_conversion (ip , offset_fsb , count_fsb ,
1248+ & cmap );
12051249 }
12061250
1207- length = XFS_FSB_TO_B (mp , cmap .br_startoff + cmap .br_blockcount );
1208- trace_xfs_iomap_found (ip , offset , length - offset , XFS_COW_FORK , & cmap );
1251+ trace_xfs_iomap_found (ip , offset , length , XFS_COW_FORK , & cmap );
12091252 seq = xfs_iomap_inode_sequence (ip , IOMAP_F_SHARED );
12101253 xfs_iunlock (ip , XFS_ILOCK_EXCL );
12111254 return xfs_bmbt_to_iomap (ip , iomap , & cmap , flags , IOMAP_F_SHARED , seq );
12121255
1256+ convert_delay :
1257+ xfs_iunlock (ip , XFS_ILOCK_EXCL );
1258+ error = xfs_bmapi_convert_delalloc (ip , XFS_COW_FORK , offset , iomap ,
1259+ NULL );
1260+ if (error )
1261+ return error ;
1262+
1263+ /*
1264+ * Try the lookup again, because the delalloc conversion might have
1265+ * turned the COW mapping into unwritten, but we need it to be in
1266+ * written state.
1267+ */
1268+ goto retry ;
12131269out_unlock :
12141270 xfs_iunlock (ip , XFS_ILOCK_EXCL );
12151271 return error ;
0 commit comments