Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dm vdo: force read-only mode for a corrupt recovery journal

Ensure the recovery journal does not attempt recovery when blocks
with mismatched metadata versions are detected. This check is
performed after determining that the blocks are otherwise valid
so that it does not interfere with normal recovery.

Signed-off-by: Susan LeGendre-McGhee <slegendr@redhat.com>
Signed-off-by: Matthew Sakai <msakai@redhat.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>

authored by

Susan LeGendre-McGhee and committed by
Mikulas Patocka
448c4e4e f3ff6683

+24 -19
+22 -17
drivers/md/dm-vdo/repair.c
··· 1202 1202 * @journal: The journal to use. 1203 1203 * @header: The unpacked block header to check. 1204 1204 * @sequence: The expected sequence number. 1205 - * @type: The expected metadata type. 1206 1205 * 1207 1206 * Return: True if the block matches. 1208 1207 */ 1209 1208 static bool __must_check is_exact_recovery_journal_block(const struct recovery_journal *journal, 1210 1209 const struct recovery_block_header *header, 1211 - sequence_number_t sequence, 1212 - enum vdo_metadata_type type) 1210 + sequence_number_t sequence) 1213 1211 { 1214 - return ((header->metadata_type == type) && 1215 - (header->sequence_number == sequence) && 1212 + return ((header->sequence_number == sequence) && 1216 1213 (is_valid_recovery_journal_block(journal, header, true))); 1217 1214 } 1218 1215 ··· 1368 1371 get_recovery_journal_block_header(journal, repair->journal_data, 1369 1372 sequence); 1370 1373 1371 - if (!is_exact_recovery_journal_block(journal, &header, sequence, format)) { 1374 + if (!is_exact_recovery_journal_block(journal, &header, sequence) || 1375 + (header.metadata_type != format)) { 1372 1376 /* This block is invalid, so skip it. */ 1373 1377 return; 1374 1378 } ··· 1555 1557 sequence_number_t i, head; 1556 1558 bool found_entries = false; 1557 1559 struct recovery_journal *journal = repair->completion.vdo->recovery_journal; 1560 + struct recovery_block_header header; 1561 + enum vdo_metadata_type expected_format; 1558 1562 1559 1563 head = min(repair->block_map_head, repair->slab_journal_head); 1564 + header = get_recovery_journal_block_header(journal, repair->journal_data, head); 1565 + expected_format = header.metadata_type; 1560 1566 for (i = head; i <= repair->highest_tail; i++) { 1561 - struct recovery_block_header header; 1562 1567 journal_entry_count_t block_entries; 1563 1568 u8 j; 1564 1569 ··· 1573 1572 }; 1574 1573 1575 1574 header = get_recovery_journal_block_header(journal, repair->journal_data, i); 1576 - if (header.metadata_type == VDO_METADATA_RECOVERY_JOURNAL) { 1577 - /* This is an old format block, so we need to upgrade */ 1578 - vdo_log_error_strerror(VDO_UNSUPPORTED_VERSION, 1579 - "Recovery journal is in the old format. Downgrade and complete recovery, then upgrade with a clean volume"); 1580 - return VDO_UNSUPPORTED_VERSION; 1581 - } 1582 - 1583 - if (!is_exact_recovery_journal_block(journal, &header, i, 1584 - VDO_METADATA_RECOVERY_JOURNAL_2)) { 1575 + if (!is_exact_recovery_journal_block(journal, &header, i)) { 1585 1576 /* A bad block header was found so this must be the end of the journal. */ 1586 1577 break; 1578 + } else if (header.metadata_type != expected_format) { 1579 + /* There is a mix of old and new format blocks, so we need to rebuild. */ 1580 + vdo_log_error_strerror(VDO_CORRUPT_JOURNAL, 1581 + "Recovery journal is in an invalid format, a read-only rebuild is required."); 1582 + vdo_enter_read_only_mode(repair->completion.vdo, VDO_CORRUPT_JOURNAL); 1583 + return VDO_CORRUPT_JOURNAL; 1587 1584 } 1588 1585 1589 1586 block_entries = header.entry_count; ··· 1617 1618 break; 1618 1619 } 1619 1620 1620 - if (!found_entries) 1621 + if (!found_entries) { 1621 1622 return validate_heads(repair); 1623 + } else if (expected_format == VDO_METADATA_RECOVERY_JOURNAL) { 1624 + /* All journal blocks have the old format, so we need to upgrade. */ 1625 + vdo_log_error_strerror(VDO_UNSUPPORTED_VERSION, 1626 + "Recovery journal is in the old format. Downgrade and complete recovery, then upgrade with a clean volume"); 1627 + return VDO_UNSUPPORTED_VERSION; 1628 + } 1622 1629 1623 1630 /* Set the tail to the last valid tail block, if there is one. */ 1624 1631 if (repair->tail_recovery_point.sector_count == 0)
+1 -1
drivers/md/dm-vdo/status-codes.c
··· 28 28 { "VDO_LOCK_ERROR", "A lock is held incorrectly" }, 29 29 { "VDO_READ_ONLY", "The device is in read-only mode" }, 30 30 { "VDO_SHUTTING_DOWN", "The device is shutting down" }, 31 - { "VDO_CORRUPT_JOURNAL", "Recovery journal entries corrupted" }, 31 + { "VDO_CORRUPT_JOURNAL", "Recovery journal corrupted" }, 32 32 { "VDO_TOO_MANY_SLABS", "Exceeds maximum number of slabs supported" }, 33 33 { "VDO_INVALID_FRAGMENT", "Compressed block fragment is invalid" }, 34 34 { "VDO_RETRY_AFTER_REBUILD", "Retry operation after rebuilding finishes" },
+1 -1
drivers/md/dm-vdo/status-codes.h
··· 52 52 VDO_READ_ONLY, 53 53 /* the VDO is shutting down */ 54 54 VDO_SHUTTING_DOWN, 55 - /* the recovery journal has corrupt entries */ 55 + /* the recovery journal has corrupt entries or corrupt metadata */ 56 56 VDO_CORRUPT_JOURNAL, 57 57 /* exceeds maximum number of slabs supported */ 58 58 VDO_TOO_MANY_SLABS,