Skip to content

Commit 5625ea0

Browse files
committed
xfs: use iomap_valid method to detect stale cached iomaps
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2155605 Tested: With xfstests and bz reproducer Conflicts: - Context Now that iomap supports a mechanism to validate cached iomaps for buffered write operations, hook it up to the XFS buffered write ops so that we can avoid data corruptions that result from stale cached iomaps. See: https://lore.kernel.org/linux-xfs/20220817093627.GZ3600936@dread.disaster.area/ or the ->iomap_valid() introduction commit for exact details of the corruption vector. The validity cookie we store in the iomap is based on the type of iomap we return. It is expected that the iomap->flags we set in xfs_bmbt_to_iomap() is not perturbed by the iomap core and are returned to us in the iomap passed via the .iomap_valid() callback. This ensures that the validity cookie is always checking the correct inode fork sequence numbers to detect potential changes that affect the extent cached by the iomap. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <djwong@kernel.org> (cherry picked from commit 304a68b) Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
1 parent 5fe3d30 commit 5625ea0

File tree

5 files changed

+92
-27
lines changed

5 files changed

+92
-27
lines changed

fs/xfs/libxfs/xfs_bmap.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4551,7 +4551,8 @@ xfs_bmapi_convert_delalloc(
45514551
* the extent. Just return the real extent at this offset.
45524552
*/
45534553
if (!isnullstartblock(bma.got.br_startblock)) {
4554-
xfs_bmbt_to_iomap(ip, iomap, &bma.got, flags);
4554+
xfs_bmbt_to_iomap(ip, iomap, &bma.got, flags,
4555+
xfs_iomap_inode_sequence(ip, flags));
45554556
*seq = READ_ONCE(ifp->if_seq);
45564557
goto out_trans_cancel;
45574558
}
@@ -4598,7 +4599,8 @@ xfs_bmapi_convert_delalloc(
45984599
XFS_STATS_INC(mp, xs_xstrat_quick);
45994600

46004601
ASSERT(!isnullstartblock(bma.got.br_startblock));
4601-
xfs_bmbt_to_iomap(ip, iomap, &bma.got, flags);
4602+
xfs_bmbt_to_iomap(ip, iomap, &bma.got, flags,
4603+
xfs_iomap_inode_sequence(ip, flags));
46024604
*seq = READ_ONCE(ifp->if_seq);
46034605

46044606
if (whichfork == XFS_COW_FORK)

fs/xfs/xfs_aops.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ xfs_map_blocks(
358358
isnullstartblock(imap.br_startblock))
359359
goto allocate_blocks;
360360

361-
xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0);
361+
xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0, XFS_WPC(wpc)->data_seq);
362362
trace_xfs_map_blocks_found(ip, offset, count, whichfork, &imap);
363363
return 0;
364364
allocate_blocks:

fs/xfs/xfs_iomap.c

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,44 @@ xfs_alert_fsblock_zero(
4949
return -EFSCORRUPTED;
5050
}
5151

52+
u64
53+
xfs_iomap_inode_sequence(
54+
struct xfs_inode *ip,
55+
u16 iomap_flags)
56+
{
57+
u64 cookie = 0;
58+
59+
if (iomap_flags & IOMAP_F_XATTR)
60+
return READ_ONCE(ip->i_afp->if_seq);
61+
if ((iomap_flags & IOMAP_F_SHARED) && ip->i_cowfp)
62+
cookie = (u64)READ_ONCE(ip->i_cowfp->if_seq) << 32;
63+
return cookie | READ_ONCE(ip->i_df.if_seq);
64+
}
65+
66+
/*
67+
* Check that the iomap passed to us is still valid for the given offset and
68+
* length.
69+
*/
70+
static bool
71+
xfs_iomap_valid(
72+
struct inode *inode,
73+
const struct iomap *iomap)
74+
{
75+
return iomap->validity_cookie ==
76+
xfs_iomap_inode_sequence(XFS_I(inode), iomap->flags);
77+
}
78+
79+
const struct iomap_page_ops xfs_iomap_page_ops = {
80+
.iomap_valid = xfs_iomap_valid,
81+
};
82+
5283
int
5384
xfs_bmbt_to_iomap(
5485
struct xfs_inode *ip,
5586
struct iomap *iomap,
5687
struct xfs_bmbt_irec *imap,
57-
u16 flags)
88+
u16 flags,
89+
u64 sequence_cookie)
5890
{
5991
struct xfs_mount *mp = ip->i_mount;
6092
struct xfs_buftarg *target = xfs_inode_buftarg(ip);
@@ -85,6 +117,9 @@ xfs_bmbt_to_iomap(
85117
if (xfs_ipincount(ip) &&
86118
(ip->i_itemp->ili_fsync_fields & ~XFS_ILOG_TIMESTAMP))
87119
iomap->flags |= IOMAP_F_DIRTY;
120+
121+
iomap->validity_cookie = sequence_cookie;
122+
iomap->page_ops = &xfs_iomap_page_ops;
88123
return 0;
89124
}
90125

@@ -188,7 +223,8 @@ xfs_iomap_write_direct(
188223
struct xfs_inode *ip,
189224
xfs_fileoff_t offset_fsb,
190225
xfs_fileoff_t count_fsb,
191-
struct xfs_bmbt_irec *imap)
226+
struct xfs_bmbt_irec *imap,
227+
u64 *seq)
192228
{
193229
struct xfs_mount *mp = ip->i_mount;
194230
struct xfs_trans *tp;
@@ -276,6 +312,7 @@ xfs_iomap_write_direct(
276312
error = xfs_alert_fsblock_zero(ip, imap);
277313

278314
out_unlock:
315+
*seq = xfs_iomap_inode_sequence(ip, 0);
279316
xfs_iunlock(ip, XFS_ILOCK_EXCL);
280317
return error;
281318

@@ -731,6 +768,7 @@ xfs_direct_write_iomap_begin(
731768
bool shared = false;
732769
u16 iomap_flags = 0;
733770
unsigned lockmode;
771+
u64 seq;
734772

735773
ASSERT(flags & (IOMAP_WRITE | IOMAP_ZERO));
736774

@@ -798,9 +836,10 @@ xfs_direct_write_iomap_begin(
798836
goto out_unlock;
799837
}
800838

839+
seq = xfs_iomap_inode_sequence(ip, iomap_flags);
801840
xfs_iunlock(ip, lockmode);
802841
trace_xfs_iomap_found(ip, offset, length, XFS_DATA_FORK, &imap);
803-
return xfs_bmbt_to_iomap(ip, iomap, &imap, iomap_flags);
842+
return xfs_bmbt_to_iomap(ip, iomap, &imap, iomap_flags, seq);
804843

805844
allocate_blocks:
806845
error = -EAGAIN;
@@ -826,23 +865,26 @@ xfs_direct_write_iomap_begin(
826865
xfs_iunlock(ip, lockmode);
827866

828867
error = xfs_iomap_write_direct(ip, offset_fsb, end_fsb - offset_fsb,
829-
&imap);
868+
&imap, &seq);
830869
if (error)
831870
return error;
832871

833872
trace_xfs_iomap_alloc(ip, offset, length, XFS_DATA_FORK, &imap);
834-
return xfs_bmbt_to_iomap(ip, iomap, &imap, iomap_flags | IOMAP_F_NEW);
873+
return xfs_bmbt_to_iomap(ip, iomap, &imap,
874+
iomap_flags | IOMAP_F_NEW, seq);
835875

836876
out_found_cow:
837-
xfs_iunlock(ip, lockmode);
838877
length = XFS_FSB_TO_B(mp, cmap.br_startoff + cmap.br_blockcount);
839878
trace_xfs_iomap_found(ip, offset, length - offset, XFS_COW_FORK, &cmap);
840879
if (imap.br_startblock != HOLESTARTBLOCK) {
841-
error = xfs_bmbt_to_iomap(ip, srcmap, &imap, 0);
880+
seq = xfs_iomap_inode_sequence(ip, 0);
881+
error = xfs_bmbt_to_iomap(ip, srcmap, &imap, 0, seq);
842882
if (error)
843-
return error;
883+
goto out_unlock;
844884
}
845-
return xfs_bmbt_to_iomap(ip, iomap, &cmap, IOMAP_F_SHARED);
885+
seq = xfs_iomap_inode_sequence(ip, IOMAP_F_SHARED);
886+
xfs_iunlock(ip, lockmode);
887+
return xfs_bmbt_to_iomap(ip, iomap, &cmap, IOMAP_F_SHARED, seq);
846888

847889
out_unlock:
848890
if (lockmode)
@@ -873,6 +915,7 @@ xfs_buffered_write_iomap_begin(
873915
bool eof = false, cow_eof = false, shared = false;
874916
int allocfork = XFS_DATA_FORK;
875917
int error = 0;
918+
u64 seq;
876919

877920
if (xfs_is_shutdown(mp))
878921
return -EIO;
@@ -1050,25 +1093,30 @@ xfs_buffered_write_iomap_begin(
10501093
* Flag newly allocated delalloc blocks with IOMAP_F_NEW so we punch
10511094
* them out if the write happens to fail.
10521095
*/
1096+
seq = xfs_iomap_inode_sequence(ip, IOMAP_F_NEW);
10531097
xfs_iunlock(ip, XFS_ILOCK_EXCL);
10541098
trace_xfs_iomap_alloc(ip, offset, count, allocfork, &imap);
1055-
return xfs_bmbt_to_iomap(ip, iomap, &imap, IOMAP_F_NEW);
1099+
return xfs_bmbt_to_iomap(ip, iomap, &imap, IOMAP_F_NEW, seq);
10561100

10571101
found_imap:
1102+
seq = xfs_iomap_inode_sequence(ip, 0);
10581103
xfs_iunlock(ip, XFS_ILOCK_EXCL);
1059-
return xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
1104+
return xfs_bmbt_to_iomap(ip, iomap, &imap, 0, seq);
10601105

10611106
found_cow:
1062-
xfs_iunlock(ip, XFS_ILOCK_EXCL);
1107+
seq = xfs_iomap_inode_sequence(ip, 0);
10631108
if (imap.br_startoff <= offset_fsb) {
1064-
error = xfs_bmbt_to_iomap(ip, srcmap, &imap, 0);
1109+
error = xfs_bmbt_to_iomap(ip, srcmap, &imap, 0, seq);
10651110
if (error)
1066-
return error;
1067-
return xfs_bmbt_to_iomap(ip, iomap, &cmap, IOMAP_F_SHARED);
1111+
goto out_unlock;
1112+
seq = xfs_iomap_inode_sequence(ip, IOMAP_F_SHARED);
1113+
xfs_iunlock(ip, XFS_ILOCK_EXCL);
1114+
return xfs_bmbt_to_iomap(ip, iomap, &cmap, IOMAP_F_SHARED, seq);
10681115
}
10691116

10701117
xfs_trim_extent(&cmap, offset_fsb, imap.br_startoff - offset_fsb);
1071-
return xfs_bmbt_to_iomap(ip, iomap, &cmap, 0);
1118+
xfs_iunlock(ip, XFS_ILOCK_EXCL);
1119+
return xfs_bmbt_to_iomap(ip, iomap, &cmap, 0, seq);
10721120

10731121
out_unlock:
10741122
xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -1148,6 +1196,7 @@ xfs_read_iomap_begin(
11481196
int nimaps = 1, error = 0;
11491197
bool shared = false;
11501198
unsigned lockmode;
1199+
u64 seq;
11511200

11521201
ASSERT(!(flags & (IOMAP_WRITE | IOMAP_ZERO)));
11531202

@@ -1161,12 +1210,14 @@ xfs_read_iomap_begin(
11611210
&nimaps, 0);
11621211
if (!error && (flags & IOMAP_REPORT))
11631212
error = xfs_reflink_trim_around_shared(ip, &imap, &shared);
1213+
seq = xfs_iomap_inode_sequence(ip, shared ? IOMAP_F_SHARED : 0);
11641214
xfs_iunlock(ip, lockmode);
11651215

11661216
if (error)
11671217
return error;
11681218
trace_xfs_iomap_found(ip, offset, length, XFS_DATA_FORK, &imap);
1169-
return xfs_bmbt_to_iomap(ip, iomap, &imap, shared ? IOMAP_F_SHARED : 0);
1219+
return xfs_bmbt_to_iomap(ip, iomap, &imap,
1220+
shared ? IOMAP_F_SHARED : 0, seq);
11701221
}
11711222

11721223
const struct iomap_ops xfs_read_iomap_ops = {
@@ -1191,6 +1242,7 @@ xfs_seek_iomap_begin(
11911242
struct xfs_bmbt_irec imap, cmap;
11921243
int error = 0;
11931244
unsigned lockmode;
1245+
u64 seq;
11941246

11951247
if (xfs_is_shutdown(mp))
11961248
return -EIO;
@@ -1225,7 +1277,9 @@ xfs_seek_iomap_begin(
12251277
if (data_fsb < cow_fsb + cmap.br_blockcount)
12261278
end_fsb = min(end_fsb, data_fsb);
12271279
xfs_trim_extent(&cmap, offset_fsb, end_fsb);
1228-
error = xfs_bmbt_to_iomap(ip, iomap, &cmap, IOMAP_F_SHARED);
1280+
seq = xfs_iomap_inode_sequence(ip, IOMAP_F_SHARED);
1281+
error = xfs_bmbt_to_iomap(ip, iomap, &cmap,
1282+
IOMAP_F_SHARED, seq);
12291283
/*
12301284
* This is a COW extent, so we must probe the page cache
12311285
* because there could be dirty page cache being backed
@@ -1246,8 +1300,9 @@ xfs_seek_iomap_begin(
12461300
imap.br_startblock = HOLESTARTBLOCK;
12471301
imap.br_state = XFS_EXT_NORM;
12481302
done:
1303+
seq = xfs_iomap_inode_sequence(ip, 0);
12491304
xfs_trim_extent(&imap, offset_fsb, end_fsb);
1250-
error = xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
1305+
error = xfs_bmbt_to_iomap(ip, iomap, &imap, 0, seq);
12511306
out_unlock:
12521307
xfs_iunlock(ip, lockmode);
12531308
return error;
@@ -1273,6 +1328,7 @@ xfs_xattr_iomap_begin(
12731328
struct xfs_bmbt_irec imap;
12741329
int nimaps = 1, error = 0;
12751330
unsigned lockmode;
1331+
int seq = 0;
12761332

12771333
if (xfs_is_shutdown(mp))
12781334
return -EIO;
@@ -1289,12 +1345,15 @@ xfs_xattr_iomap_begin(
12891345
error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
12901346
&nimaps, XFS_BMAPI_ATTRFORK);
12911347
out_unlock:
1348+
1349+
if (ip->i_afp)
1350+
seq = xfs_iomap_inode_sequence(ip, IOMAP_F_XATTR);
12921351
xfs_iunlock(ip, lockmode);
12931352

12941353
if (error)
12951354
return error;
12961355
ASSERT(nimaps);
1297-
return xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
1356+
return xfs_bmbt_to_iomap(ip, iomap, &imap, IOMAP_F_XATTR, seq);
12981357
}
12991358

13001359
const struct iomap_ops xfs_xattr_iomap_ops = {

fs/xfs/xfs_iomap.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ struct xfs_inode;
1212
struct xfs_bmbt_irec;
1313

1414
int xfs_iomap_write_direct(struct xfs_inode *ip, xfs_fileoff_t offset_fsb,
15-
xfs_fileoff_t count_fsb, struct xfs_bmbt_irec *imap);
15+
xfs_fileoff_t count_fsb, struct xfs_bmbt_irec *imap,
16+
u64 *sequence);
1617
int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t, bool);
1718
xfs_fileoff_t xfs_iomap_eof_align_last_fsb(struct xfs_inode *ip,
1819
xfs_fileoff_t end_fsb);
1920

21+
u64 xfs_iomap_inode_sequence(struct xfs_inode *ip, u16 iomap_flags);
2022
int xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *,
21-
struct xfs_bmbt_irec *, u16);
23+
struct xfs_bmbt_irec *, u16, u64 sequence_cookie);
2224

2325
static inline xfs_filblks_t
2426
xfs_aligned_fsb_count(

fs/xfs/xfs_pnfs.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ xfs_fs_map_blocks(
9191
int nimaps = 1;
9292
uint lock_flags;
9393
int error = 0;
94+
u64 seq;
9495

9596
if (xfs_is_shutdown(mp))
9697
return -EIO;
@@ -142,6 +143,7 @@ xfs_fs_map_blocks(
142143
lock_flags = xfs_ilock_data_map_shared(ip);
143144
error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
144145
&imap, &nimaps, bmapi_flags);
146+
seq = xfs_iomap_inode_sequence(ip, 0);
145147

146148
ASSERT(!nimaps || imap.br_startblock != DELAYSTARTBLOCK);
147149

@@ -155,7 +157,7 @@ xfs_fs_map_blocks(
155157
xfs_iunlock(ip, lock_flags);
156158

157159
error = xfs_iomap_write_direct(ip, offset_fsb,
158-
end_fsb - offset_fsb, &imap);
160+
end_fsb - offset_fsb, &imap, &seq);
159161
if (error)
160162
goto out_unlock;
161163

@@ -175,7 +177,7 @@ xfs_fs_map_blocks(
175177
}
176178
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
177179

178-
error = xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
180+
error = xfs_bmbt_to_iomap(ip, iomap, &imap, 0, seq);
179181
*device_generation = mp->m_generation;
180182
return error;
181183
out_unlock:

0 commit comments

Comments
 (0)