Skip to content

Commit e747883

Browse files
Christoph Hellwigcmaiolino
authored andcommitted
xfs: fix log CRC mismatches between i386 and other architectures
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]>
1 parent 0b737f4 commit e747883

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

202+
#ifdef __i386__
203+
#define XLOG_REC_SIZE offsetofend(struct xlog_rec_header, h_size)
204+
#define XLOG_REC_SIZE_OTHER sizeof(struct xlog_rec_header)
205+
#else
206+
#define XLOG_REC_SIZE sizeof(struct xlog_rec_header)
207+
#define XLOG_REC_SIZE_OTHER offsetofend(struct xlog_rec_header, h_size)
208+
#endif /* __i386__ */
209+
182210
typedef struct xlog_rec_ext_header {
183211
__be32 xh_cycle; /* write cycle of log : 4 */
184212
__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
@@ -174,6 +174,8 @@ xfs_check_ondisk_structs(void)
174174
XFS_CHECK_STRUCT_SIZE(struct xfs_rud_log_format, 16);
175175
XFS_CHECK_STRUCT_SIZE(struct xfs_map_extent, 32);
176176
XFS_CHECK_STRUCT_SIZE(struct xfs_phys_extent, 16);
177+
XFS_CHECK_STRUCT_SIZE(struct xlog_rec_header, 328);
178+
XFS_CHECK_STRUCT_SIZE(struct xlog_rec_ext_header, 260);
177179

178180
XFS_CHECK_OFFSET(struct xfs_bui_log_format, bui_extents, 16);
179181
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
@@ -1568,13 +1568,13 @@ xlog_cksum(
15681568
struct xlog *log,
15691569
struct xlog_rec_header *rhead,
15701570
char *dp,
1571-
int size)
1571+
unsigned int hdrsize,
1572+
unsigned int size)
15721573
{
15731574
uint32_t crc;
15741575

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

15801580
/* ... then for additional cycle data for v2 logs ... */
@@ -1818,7 +1818,7 @@ xlog_sync(
18181818

18191819
/* calculcate the checksum */
18201820
iclog->ic_header.h_crc = xlog_cksum(log, &iclog->ic_header,
1821-
iclog->ic_datap, size);
1821+
iclog->ic_datap, XLOG_REC_SIZE, size);
18221822
/*
18231823
* Intentionally corrupt the log record CRC based on the error injection
18241824
* 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
@@ -499,8 +499,8 @@ xlog_recover_finish(
499499
extern void
500500
xlog_recover_cancel(struct xlog *);
501501

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

505505
extern struct kmem_cache *xfs_log_ticket_cache;
506506
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
@@ -2894,9 +2894,24 @@ xlog_recover_process(
28942894
int pass,
28952895
struct list_head *buffer_list)
28962896
{
2897-
__le32 expected_crc = rhead->h_crc, crc;
2897+
__le32 expected_crc = rhead->h_crc, crc, other_crc;
28982898

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

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

0 commit comments

Comments
 (0)