// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2023 Red Hat */ #include "repair.h" #include <linux/min_heap.h> #include <linux/minmax.h> #include "logger.h" #include "memory-alloc.h" #include "permassert.h" #include "block-map.h" #include "completion.h" #include "constants.h" #include "encodings.h" #include "int-map.h" #include "io-submitter.h" #include "recovery-journal.h" #include "slab-depot.h" #include "types.h" #include "vdo.h" #include "wait-queue.h" /* * An explicitly numbered block mapping. Numbering the mappings allows them to be sorted by logical * block number during repair while still preserving the relative order of journal entries with * the same logical block number. */ struct numbered_block_mapping { … } __packed; /* * The absolute position of an entry in the recovery journal, including the sector number and the * entry number within the sector. */ struct recovery_point { … }; DEFINE_MIN_HEAP( … } ; struct repair_completion { … }; /* * This is a min_heap callback function that orders numbered_block_mappings using the * 'block_map_slot' field as the primary key and the mapping 'number' field as the secondary key. * Using the mapping number preserves the journal order of entries for the same slot, allowing us * to sort by slot while still ensuring we replay all entries with the same slot in the exact order * as they appeared in the journal. */ static bool mapping_is_less_than(const void *item1, const void *item2, void __always_unused *args) { … } static void swap_mappings(void *item1, void *item2, void __always_unused *args) { … } static const struct min_heap_callbacks repair_min_heap = …; static struct numbered_block_mapping *sort_next_heap_element(struct repair_completion *repair) { … } /** * as_repair_completion() - Convert a generic completion to a repair_completion. * @completion: The completion to convert. * * Return: The repair_completion. */ static inline struct repair_completion * __must_check as_repair_completion(struct vdo_completion *completion) { … } static void prepare_repair_completion(struct repair_completion *repair, vdo_action_fn callback, enum vdo_zone_type zone_type) { … } static void launch_repair_completion(struct repair_completion *repair, vdo_action_fn callback, enum vdo_zone_type zone_type) { … } static void uninitialize_vios(struct repair_completion *repair) { … } static void free_repair_completion(struct repair_completion *repair) { … } static void finish_repair(struct vdo_completion *completion) { … } /** * abort_repair() - Handle a repair error. * @completion: The repair completion. */ static void abort_repair(struct vdo_completion *completion) { … } /** * abort_on_error() - Abort a repair if there is an error. * @result: The result to check. * @repair: The repair completion. * * Return: true if the result was an error. */ static bool __must_check abort_on_error(int result, struct repair_completion *repair) { … } /** * drain_slab_depot() - Flush out all dirty refcounts blocks now that they have been rebuilt or * recovered. * @completion: The repair completion. */ static void drain_slab_depot(struct vdo_completion *completion) { … } /** * flush_block_map_updates() - Flush the block map now that all the reference counts are rebuilt. * @completion: The repair completion. * * This callback is registered in finish_if_done(). */ static void flush_block_map_updates(struct vdo_completion *completion) { … } static bool fetch_page(struct repair_completion *repair, struct vdo_completion *completion); /** * handle_page_load_error() - Handle an error loading a page. * @completion: The vdo_page_completion. */ static void handle_page_load_error(struct vdo_completion *completion) { … } /** * unmap_entry() - Unmap an invalid entry and indicate that its page must be written out. * @page: The page containing the entries * @completion: The page_completion for writing the page * @slot: The slot to unmap */ static void unmap_entry(struct block_map_page *page, struct vdo_completion *completion, slot_number_t slot) { … } /** * remove_out_of_bounds_entries() - Unmap entries which outside the logical space. * @page: The page containing the entries * @completion: The page_completion for writing the page * @start: The first slot to check */ static void remove_out_of_bounds_entries(struct block_map_page *page, struct vdo_completion *completion, slot_number_t start) { … } /** * process_slot() - Update the reference counts for a single entry. * @page: The page containing the entries * @completion: The page_completion for writing the page * @slot: The slot to check * * Return: true if the entry was a valid mapping */ static bool process_slot(struct block_map_page *page, struct vdo_completion *completion, slot_number_t slot) { … } /** * rebuild_reference_counts_from_page() - Rebuild reference counts from a block map page. * @repair: The repair completion. * @completion: The page completion holding the page. */ static void rebuild_reference_counts_from_page(struct repair_completion *repair, struct vdo_completion *completion) { … } /** * page_loaded() - Process a page which has just been loaded. * @completion: The vdo_page_completion for the fetched page. * * This callback is registered by fetch_page(). */ static void page_loaded(struct vdo_completion *completion) { … } static physical_block_number_t get_pbn_to_fetch(struct repair_completion *repair, struct block_map *block_map) { … } /** * fetch_page() - Fetch a page from the block map. * @repair: The repair_completion. * @completion: The page completion to use. * * Return true if the rebuild is complete */ static bool fetch_page(struct repair_completion *repair, struct vdo_completion *completion) { … } /** * rebuild_from_leaves() - Rebuild reference counts from the leaf block map pages. * @completion: The repair completion. * * Rebuilds reference counts from the leaf block map pages now that reference counts have been * rebuilt from the interior tree pages (which have been loaded in the process). This callback is * registered in rebuild_reference_counts(). */ static void rebuild_from_leaves(struct vdo_completion *completion) { … } /** * process_entry() - Process a single entry from the block map tree. * @pbn: A pbn which holds a block map tree page. * @completion: The parent completion of the traversal. * * Implements vdo_entry_callback_fn. * * Return: VDO_SUCCESS or an error. */ static int process_entry(physical_block_number_t pbn, struct vdo_completion *completion) { … } static void rebuild_reference_counts(struct vdo_completion *completion) { … } static void increment_recovery_point(struct recovery_point *point) { … } /** * advance_points() - Advance the current recovery and journal points. * @repair: The repair_completion whose points are to be advanced. * @entries_per_block: The number of entries in a recovery journal block. */ static void advance_points(struct repair_completion *repair, journal_entry_count_t entries_per_block) { … } /** * before_recovery_point() - Check whether the first point precedes the second point. * @first: The first recovery point. * @second: The second recovery point. * * Return: true if the first point precedes the second point. */ static bool __must_check before_recovery_point(const struct recovery_point *first, const struct recovery_point *second) { … } static struct packed_journal_sector * __must_check get_sector(struct recovery_journal *journal, char *journal_data, sequence_number_t sequence, u8 sector_number) { … } /** * get_entry() - Unpack the recovery journal entry associated with the given recovery point. * @repair: The repair completion. * @point: The recovery point. * * Return: The unpacked contents of the matching recovery journal entry. */ static struct recovery_journal_entry get_entry(const struct repair_completion *repair, const struct recovery_point *point) { … } /** * validate_recovery_journal_entry() - Validate a recovery journal entry. * @vdo: The vdo. * @entry: The entry to validate. * * Return: VDO_SUCCESS or an error. */ static int validate_recovery_journal_entry(const struct vdo *vdo, const struct recovery_journal_entry *entry) { … } /** * add_slab_journal_entries() - Replay recovery journal entries into the slab journals of the * allocator currently being recovered. * @completion: The allocator completion. * * Waits for slab journal tailblock space when necessary. This method is its own callback. */ static void add_slab_journal_entries(struct vdo_completion *completion) { … } /** * vdo_replay_into_slab_journals() - Replay recovery journal entries in the slab journals of slabs * owned by a given block_allocator. * @allocator: The allocator whose slab journals are to be recovered. * @context: The slab depot load context supplied by a recovery when it loads the depot. */ void vdo_replay_into_slab_journals(struct block_allocator *allocator, void *context) { … } static void load_slab_depot(struct vdo_completion *completion) { … } static void flush_block_map(struct vdo_completion *completion) { … } static bool finish_if_done(struct repair_completion *repair) { … } static void abort_block_map_recovery(struct repair_completion *repair, int result) { … } /** * find_entry_starting_next_page() - Find the first journal entry after a given entry which is not * on the same block map page. * @repair: The repair completion. * @current_entry: The entry to search from. * @needs_sort: Whether sorting is needed to proceed. * * Return: Pointer to the first later journal entry on a different block map page, or a pointer to * just before the journal entries if no subsequent entry is on a different block map page. */ static struct numbered_block_mapping * find_entry_starting_next_page(struct repair_completion *repair, struct numbered_block_mapping *current_entry, bool needs_sort) { … } /* * Apply a range of journal entries [starting_entry, ending_entry) journal * entries to a block map page. */ static void apply_journal_entries_to_page(struct block_map_page *page, struct numbered_block_mapping *starting_entry, struct numbered_block_mapping *ending_entry) { … } static void recover_ready_pages(struct repair_completion *repair, struct vdo_completion *completion); static void block_map_page_loaded(struct vdo_completion *completion) { … } static void handle_block_map_page_load_error(struct vdo_completion *completion) { … } static void fetch_block_map_page(struct repair_completion *repair, struct vdo_completion *completion) { … } static struct vdo_page_completion *get_next_page_completion(struct repair_completion *repair, struct vdo_page_completion *completion) { … } static void recover_ready_pages(struct repair_completion *repair, struct vdo_completion *completion) { … } static void recover_block_map(struct vdo_completion *completion) { … } /** * get_recovery_journal_block_header() - Get the block header for a block at a position in the * journal data and unpack it. * @journal: The recovery journal. * @data: The recovery journal data. * @sequence: The sequence number. * * Return: The unpacked header. */ static struct recovery_block_header __must_check get_recovery_journal_block_header(struct recovery_journal *journal, char *data, sequence_number_t sequence) { … } /** * is_valid_recovery_journal_block() - Determine whether the given header describes a valid block * for the given journal. * @journal: The journal to use. * @header: The unpacked block header to check. * @old_ok: Whether an old format header is valid. * * A block is not valid if it is unformatted, or if it is older than the last successful recovery * or reformat. * * Return: True if the header is valid. */ static bool __must_check is_valid_recovery_journal_block(const struct recovery_journal *journal, const struct recovery_block_header *header, bool old_ok) { … } /** * is_exact_recovery_journal_block() - Determine whether the given header describes the exact block * indicated. * @journal: The journal to use. * @header: The unpacked block header to check. * @sequence: The expected sequence number. * * Return: True if the block matches. */ static bool __must_check is_exact_recovery_journal_block(const struct recovery_journal *journal, const struct recovery_block_header *header, sequence_number_t sequence) { … } /** * find_recovery_journal_head_and_tail() - Find the tail and head of the journal. * @repair: The repair completion. * * Return: True if there were valid journal blocks. */ static bool find_recovery_journal_head_and_tail(struct repair_completion *repair) { … } /** * unpack_entry() - Unpack a recovery journal entry in either format. * @vdo: The vdo. * @packed: The entry to unpack. * @format: The expected format of the entry. * @entry: The unpacked entry. * * Return: true if the entry should be applied.3 */ static bool unpack_entry(struct vdo *vdo, char *packed, enum vdo_metadata_type format, struct recovery_journal_entry *entry) { … } /** * append_sector_entries() - Append an array of recovery journal entries from a journal block * sector to the array of numbered mappings in the repair completion, * numbering each entry in the order they are appended. * @repair: The repair completion. * @entries: The entries in the sector. * @format: The format of the sector. * @entry_count: The number of entries to append. */ static void append_sector_entries(struct repair_completion *repair, char *entries, enum vdo_metadata_type format, journal_entry_count_t entry_count) { … } static journal_entry_count_t entries_per_sector(enum vdo_metadata_type format, u8 sector_number) { … } static void extract_entries_from_block(struct repair_completion *repair, struct recovery_journal *journal, sequence_number_t sequence, enum vdo_metadata_type format, journal_entry_count_t entries) { … } static int parse_journal_for_rebuild(struct repair_completion *repair) { … } static int validate_heads(struct repair_completion *repair) { … } /** * extract_new_mappings() - Find all valid new mappings to be applied to the block map. * @repair: The repair completion. * * The mappings are extracted from the journal and stored in a sortable array so that all of the * mappings to be applied to a given block map page can be done in a single page fetch. */ static int extract_new_mappings(struct repair_completion *repair) { … } /** * compute_usages() - Compute the lbns in use and block map data blocks counts from the tail of * the journal. * @repair: The repair completion. */ static noinline int compute_usages(struct repair_completion *repair) { … } static int parse_journal_for_recovery(struct repair_completion *repair) { … } static int parse_journal(struct repair_completion *repair) { … } static void finish_journal_load(struct vdo_completion *completion) { … } static void handle_journal_load_error(struct vdo_completion *completion) { … } static void read_journal_endio(struct bio *bio) { … } /** * vdo_repair() - Load the recovery journal and then recover or rebuild a vdo. * @parent: The completion to notify when the operation is complete */ void vdo_repair(struct vdo_completion *parent) { … }