Skip to content

Commit b1e0917

Browse files
johnpgarryDarrick J. Wong
authored andcommitted
xfs: commit CoW-based atomic writes atomically
When completing a CoW-based write, each extent range mapping update is covered by a separate transaction. For a CoW-based atomic write, all mappings must be changed at once, so change to use a single transaction. Note that there is a limit on the amount of log intent items which can be fit into a single transaction, but this is being ignored for now since the count of items for a typical atomic write would be much less than is typically supported. A typical atomic write would be expected to be 64KB or less, which means only 16 possible extents unmaps, which is quite small. Reviewed-by: Darrick J. Wong <[email protected]> [djwong: add tr_atomic_ioend] Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Darrick J. Wong <[email protected]> Signed-off-by: John Garry <[email protected]>
1 parent 11ab319 commit b1e0917

File tree

6 files changed

+82
-1
lines changed

6 files changed

+82
-1
lines changed

fs/xfs/libxfs/xfs_log_rlimit.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ xfs_log_calc_trans_resv_for_minlogblocks(
9191
*/
9292
if (xfs_want_minlogsize_fixes(&mp->m_sb)) {
9393
xfs_trans_resv_calc(mp, resv);
94+
resv->tr_atomic_ioend = M_RES(mp)->tr_atomic_ioend;
9495
return;
9596
}
9697

@@ -107,6 +108,9 @@ xfs_log_calc_trans_resv_for_minlogblocks(
107108

108109
xfs_trans_resv_calc(mp, resv);
109110

111+
/* Copy the dynamic transaction reservation types from the running fs */
112+
resv->tr_atomic_ioend = M_RES(mp)->tr_atomic_ioend;
113+
110114
if (xfs_has_reflink(mp)) {
111115
/*
112116
* In the early days of reflink, typical log operation counts

fs/xfs/libxfs/xfs_trans_resv.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,15 @@ xfs_calc_namespace_reservations(
12841284
resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
12851285
}
12861286

1287+
STATIC void
1288+
xfs_calc_default_atomic_ioend_reservation(
1289+
struct xfs_mount *mp,
1290+
struct xfs_trans_resv *resp)
1291+
{
1292+
/* Pick a default that will scale reasonably for the log size. */
1293+
resp->tr_atomic_ioend = resp->tr_itruncate;
1294+
}
1295+
12871296
void
12881297
xfs_trans_resv_calc(
12891298
struct xfs_mount *mp,
@@ -1378,4 +1387,10 @@ xfs_trans_resv_calc(
13781387
resp->tr_itruncate.tr_logcount += logcount_adj;
13791388
resp->tr_write.tr_logcount += logcount_adj;
13801389
resp->tr_qm_dqalloc.tr_logcount += logcount_adj;
1390+
1391+
/*
1392+
* Now that we've finished computing the static reservations, we can
1393+
* compute the dynamic reservation for atomic writes.
1394+
*/
1395+
xfs_calc_default_atomic_ioend_reservation(mp, resp);
13811396
}

fs/xfs/libxfs/xfs_trans_resv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct xfs_trans_resv {
4848
struct xfs_trans_res tr_qm_dqalloc; /* allocate quota on disk */
4949
struct xfs_trans_res tr_sb; /* modify superblock */
5050
struct xfs_trans_res tr_fsyncts; /* update timestamps on fsync */
51+
struct xfs_trans_res tr_atomic_ioend; /* untorn write completion */
5152
};
5253

5354
/* shorthand way of accessing reservation structure */

fs/xfs/xfs_file.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,10 @@ xfs_dio_write_end_io(
576576
nofs_flag = memalloc_nofs_save();
577577

578578
if (flags & IOMAP_DIO_COW) {
579-
error = xfs_reflink_end_cow(ip, offset, size);
579+
if (iocb->ki_flags & IOCB_ATOMIC)
580+
error = xfs_reflink_end_atomic_cow(ip, offset, size);
581+
else
582+
error = xfs_reflink_end_cow(ip, offset, size);
580583
if (error)
581584
goto out;
582585
}

fs/xfs/xfs_reflink.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,62 @@ xfs_reflink_end_cow(
984984
return error;
985985
}
986986

987+
/*
988+
* Fully remap all of the file's data fork at once, which is the critical part
989+
* in achieving atomic behaviour.
990+
* The regular CoW end path does not use function as to keep the block
991+
* reservation per transaction as low as possible.
992+
*/
993+
int
994+
xfs_reflink_end_atomic_cow(
995+
struct xfs_inode *ip,
996+
xfs_off_t offset,
997+
xfs_off_t count)
998+
{
999+
xfs_fileoff_t offset_fsb;
1000+
xfs_fileoff_t end_fsb;
1001+
int error = 0;
1002+
struct xfs_mount *mp = ip->i_mount;
1003+
struct xfs_trans *tp;
1004+
unsigned int resblks;
1005+
1006+
trace_xfs_reflink_end_cow(ip, offset, count);
1007+
1008+
offset_fsb = XFS_B_TO_FSBT(mp, offset);
1009+
end_fsb = XFS_B_TO_FSB(mp, offset + count);
1010+
1011+
/*
1012+
* Each remapping operation could cause a btree split, so in the worst
1013+
* case that's one for each block.
1014+
*/
1015+
resblks = (end_fsb - offset_fsb) *
1016+
XFS_NEXTENTADD_SPACE_RES(mp, 1, XFS_DATA_FORK);
1017+
1018+
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_atomic_ioend, resblks, 0,
1019+
XFS_TRANS_RESERVE, &tp);
1020+
if (error)
1021+
return error;
1022+
1023+
xfs_ilock(ip, XFS_ILOCK_EXCL);
1024+
xfs_trans_ijoin(tp, ip, 0);
1025+
1026+
while (end_fsb > offset_fsb && !error) {
1027+
error = xfs_reflink_end_cow_extent_locked(tp, ip, &offset_fsb,
1028+
end_fsb);
1029+
}
1030+
if (error) {
1031+
trace_xfs_reflink_end_cow_error(ip, error, _RET_IP_);
1032+
goto out_cancel;
1033+
}
1034+
error = xfs_trans_commit(tp);
1035+
xfs_iunlock(ip, XFS_ILOCK_EXCL);
1036+
return error;
1037+
out_cancel:
1038+
xfs_trans_cancel(tp);
1039+
xfs_iunlock(ip, XFS_ILOCK_EXCL);
1040+
return error;
1041+
}
1042+
9871043
/*
9881044
* Free all CoW staging blocks that are still referenced by the ondisk refcount
9891045
* metadata. The ondisk metadata does not track which inode created the

fs/xfs/xfs_reflink.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ extern int xfs_reflink_cancel_cow_range(struct xfs_inode *ip, xfs_off_t offset,
4545
xfs_off_t count, bool cancel_real);
4646
extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset,
4747
xfs_off_t count);
48+
int xfs_reflink_end_atomic_cow(struct xfs_inode *ip, xfs_off_t offset,
49+
xfs_off_t count);
4850
extern int xfs_reflink_recover_cow(struct xfs_mount *mp);
4951
extern loff_t xfs_reflink_remap_range(struct file *file_in, loff_t pos_in,
5052
struct file *file_out, loff_t pos_out, loff_t len,

0 commit comments

Comments
 (0)