Skip to content

Commit 9909b28

Browse files
Christoph Hellwiggregkh
authored andcommitted
xfs: fix log CRC mismatches between i386 and other architectures
[ Upstream commit e747883 ] When mounting file systems with a log that was dirtied on i386 on other architectures or vice versa, log recovery is unhappy: [ 11.068052] XFS (vdb): Torn write (CRC failure) detected at log block 0x2. Truncating head block from 0xc. This is because the CRCs generated by i386 and other architectures always diff. The reason for that is that sizeof(struct xlog_rec_header) returns different values for i386 vs the rest (324 vs 328), because the struct is not sizeof(uint64_t) aligned, and i386 has odd struct size alignment rules. This issue goes back to commit 13cdc853c519 ("Add log versioning, and new super block field for the log stripe") in the xfs-import tree, which adds log v2 support and the h_size field that causes the unaligned size. At that time it only mattered for the crude debug only log header checksum, but with commit 0e446be ("xfs: add CRC checks to the log") it became a real issue for v5 file system, because now there is a proper CRC, and regular builds actually expect it match. Fix this by allowing checksums with and without the padding. Fixes: 0e446be ("xfs: add CRC checks to the log") Cc: <[email protected]> # v3.8 Signed-off-by: Christoph Hellwig <[email protected]> Signed-off-by: Carlos Maiolino <[email protected]> Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent ab0f805 commit 9909b28

File tree

5 files changed

+54
-9
lines changed

5 files changed

+54
-9
lines changed

fs/xfs/libxfs/xfs_log_format.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,40 @@ typedef struct xlog_rec_header {
174174
__be32 h_prev_block; /* block number to previous LR : 4 */
175175
__be32 h_num_logops; /* number of log operations in this LR : 4 */
176176
__be32 h_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE];
177-
/* new fields */
177+
178+
/* fields added by the Linux port: */
178179
__be32 h_fmt; /* format of log record : 4 */
179180
uuid_t h_fs_uuid; /* uuid of FS : 16 */
181+
182+
/* fields added for log v2: */
180183
__be32 h_size; /* iclog size : 4 */
184+
185+
/*
186+
* When h_size added for log v2 support, it caused structure to have
187+
* a different size on i386 vs all other architectures because the
188+
* sum of the size ofthe member is not aligned by that of the largest
189+
* __be64-sized member, and i386 has really odd struct alignment rules.
190+
*
191+
* Due to the way the log headers are placed out on-disk that alone is
192+
* not a problem becaue the xlog_rec_header always sits alone in a
193+
* BBSIZEs area, and the rest of that area is padded with zeroes.
194+
* But xlog_cksum used to calculate the checksum based on the structure
195+
* size, and thus gives different checksums for i386 vs the rest.
196+
* We now do two checksum validation passes for both sizes to allow
197+
* moving v5 file systems with unclean logs between i386 and other
198+
* (little-endian) architectures.
199+
*/
200+
__u32 h_pad0;
181201
} xlog_rec_header_t;
182202

203+
#ifdef __i386__
204+
#define XLOG_REC_SIZE offsetofend(struct xlog_rec_header, h_size)
205+
#define XLOG_REC_SIZE_OTHER sizeof(struct xlog_rec_header)
206+
#else
207+
#define XLOG_REC_SIZE sizeof(struct xlog_rec_header)
208+
#define XLOG_REC_SIZE_OTHER offsetofend(struct xlog_rec_header, h_size)
209+
#endif /* __i386__ */
210+
183211
typedef struct xlog_rec_ext_header {
184212
__be32 xh_cycle; /* write cycle of log : 4 */
185213
__be32 xh_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE]; /* : 256 */

fs/xfs/libxfs/xfs_ondisk.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ xfs_check_ondisk_structs(void)
149149
XFS_CHECK_STRUCT_SIZE(struct xfs_rud_log_format, 16);
150150
XFS_CHECK_STRUCT_SIZE(struct xfs_map_extent, 32);
151151
XFS_CHECK_STRUCT_SIZE(struct xfs_phys_extent, 16);
152+
XFS_CHECK_STRUCT_SIZE(struct xlog_rec_header, 328);
153+
XFS_CHECK_STRUCT_SIZE(struct xlog_rec_ext_header, 260);
152154

153155
XFS_CHECK_OFFSET(struct xfs_bui_log_format, bui_extents, 16);
154156
XFS_CHECK_OFFSET(struct xfs_cui_log_format, cui_extents, 16);

fs/xfs/xfs_log.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,13 +1567,13 @@ xlog_cksum(
15671567
struct xlog *log,
15681568
struct xlog_rec_header *rhead,
15691569
char *dp,
1570-
int size)
1570+
unsigned int hdrsize,
1571+
unsigned int size)
15711572
{
15721573
uint32_t crc;
15731574

15741575
/* first generate the crc for the record header ... */
1575-
crc = xfs_start_cksum_update((char *)rhead,
1576-
sizeof(struct xlog_rec_header),
1576+
crc = xfs_start_cksum_update((char *)rhead, hdrsize,
15771577
offsetof(struct xlog_rec_header, h_crc));
15781578

15791579
/* ... then for additional cycle data for v2 logs ... */
@@ -1837,7 +1837,7 @@ xlog_sync(
18371837

18381838
/* calculcate the checksum */
18391839
iclog->ic_header.h_crc = xlog_cksum(log, &iclog->ic_header,
1840-
iclog->ic_datap, size);
1840+
iclog->ic_datap, XLOG_REC_SIZE, size);
18411841
/*
18421842
* Intentionally corrupt the log record CRC based on the error injection
18431843
* frequency, if defined. This facilitates testing log recovery in the

fs/xfs/xfs_log_priv.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,8 @@ xlog_recover_finish(
498498
extern void
499499
xlog_recover_cancel(struct xlog *);
500500

501-
extern __le32 xlog_cksum(struct xlog *log, struct xlog_rec_header *rhead,
502-
char *dp, int size);
501+
__le32 xlog_cksum(struct xlog *log, struct xlog_rec_header *rhead,
502+
char *dp, unsigned int hdrsize, unsigned int size);
503503

504504
extern struct kmem_cache *xfs_log_ticket_cache;
505505
struct xlog_ticket *xlog_ticket_alloc(struct xlog *log, int unit_bytes,

fs/xfs/xfs_log_recover.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2890,9 +2890,24 @@ xlog_recover_process(
28902890
int pass,
28912891
struct list_head *buffer_list)
28922892
{
2893-
__le32 expected_crc = rhead->h_crc, crc;
2893+
__le32 expected_crc = rhead->h_crc, crc, other_crc;
28942894

2895-
crc = xlog_cksum(log, rhead, dp, be32_to_cpu(rhead->h_len));
2895+
crc = xlog_cksum(log, rhead, dp, XLOG_REC_SIZE,
2896+
be32_to_cpu(rhead->h_len));
2897+
2898+
/*
2899+
* Look at the end of the struct xlog_rec_header definition in
2900+
* xfs_log_format.h for the glory details.
2901+
*/
2902+
if (expected_crc && crc != expected_crc) {
2903+
other_crc = xlog_cksum(log, rhead, dp, XLOG_REC_SIZE_OTHER,
2904+
be32_to_cpu(rhead->h_len));
2905+
if (other_crc == expected_crc) {
2906+
xfs_notice_once(log->l_mp,
2907+
"Fixing up incorrect CRC due to padding.");
2908+
crc = other_crc;
2909+
}
2910+
}
28962911

28972912
/*
28982913
* Nothing else to do if this is a CRC verification pass. Just return

0 commit comments

Comments
 (0)