diff --git a/lib/raid/raid.c b/lib/raid/raid.c index 1e28c7365..69b6ee349 100644 --- a/lib/raid/raid.c +++ b/lib/raid/raid.c @@ -161,6 +161,7 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)), uint32_t s; uint64_t flags = 0; uint64_t rebuilds = 0; + char *raid_health = NULL; if (!seg->area_count) { log_error(INTERNAL_ERROR "_raid_add_target_line called " @@ -190,6 +191,20 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)), if (mirror_in_sync()) flags = DM_NOSYNC; + /* + * If the RAID LV is not 'PARTIAL' and the status indicates + * that the array has failed devices, it means that the + * failed devices have returned and can be reintegrated. + * + * We reload the (potentially identical) table to force the + * kernel to re-read the RAID superblocks - possibly restoring + * transiently failed devices. + */ + if (!(seg->lv->status & PARTIAL_LV) && + lv_raid_dev_health(seg->lv, &raid_health) && + strchr(raid_health, 'D')) + dm_tree_node_force_identical_table_reload(node); + if (!dm_tree_node_add_raid_target(node, len, _raid_name(seg), seg->region_size, seg->stripe_size, rebuilds, flags)) diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h index 0460afab3..184615f0b 100644 --- a/libdm/libdevmapper.h +++ b/libdm/libdevmapper.h @@ -491,13 +491,14 @@ struct dm_tree_node *dm_tree_next_child(void **handle, uint32_t inverted); /* - * Get properties of a node. + * Get and set properties of a node. */ const char *dm_tree_node_get_name(const struct dm_tree_node *node); const char *dm_tree_node_get_uuid(const struct dm_tree_node *node); const struct dm_info *dm_tree_node_get_info(const struct dm_tree_node *node); void *dm_tree_node_get_context(const struct dm_tree_node *node); int dm_tree_node_size_changed(const struct dm_tree_node *dnode); +int dm_tree_node_force_identical_table_reload(struct dm_tree_node *dnode); /* * Returns the number of children of the given node (excluding the root node). diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c index 5e938d7e3..e702a2584 100644 --- a/libdm/libdm-deptree.c +++ b/libdm/libdm-deptree.c @@ -232,6 +232,16 @@ struct load_properties { /* Send messages for this node in preload */ unsigned send_messages; + + /* + * If a mapping table is replacing an existing identical table, + * the load is suppressed by default - avoiding the construction + * of an unnecessary in-kernel table. Sometimes we want to avoid + * this optimization and load the identical table anyway. This + * field is used to indicate that we desire identical tables to be + * loaded and not suppressed. + */ + unsigned force_identical_table_reload; }; /* Two of these used to join two nodes with uses and used_by. */ @@ -587,6 +597,13 @@ int dm_tree_node_size_changed(const struct dm_tree_node *dnode) return dnode->props.size_changed; } +int dm_tree_node_force_identical_table_reload(struct dm_tree_node *dnode) +{ + dnode->props.force_identical_table_reload = 1; + return 1; +} + + int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted) { if (inverted) { @@ -2409,7 +2426,8 @@ static int _load_node(struct dm_tree_node *dnode) seg, &seg_start)) goto_out; - if (!dm_task_suppress_identical_reload(dmt)) + if (!dnode->props.force_identical_table_reload && + !dm_task_suppress_identical_reload(dmt)) log_error("Failed to suppress reload of identical tables."); if ((r = dm_task_run(dmt))) {