]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dm vdo: save the formatted metadata to disk
authorBruce Johnston <bjohnsto@redhat.com>
Tue, 24 Mar 2026 18:06:52 +0000 (14:06 -0400)
committerMikulas Patocka <mpatocka@redhat.com>
Thu, 26 Mar 2026 17:19:25 +0000 (18:19 +0100)
Add vdo_save_super_block() and vdo_save_geometry_block() to perform
asynchronous writes of the super block and geometry block respectively.
Add vdo_clear_layout() to zero the UDS index's first block, the block
map partition, and the recovery journal partition.

These operations are driven by new phases in the pre-load state machine
(PRE_LOAD_PHASE_FORMAT_*), ensuring that disk writes happen during
pre-resume rather than during dmsetup create.

Signed-off-by: Bruce Johnston <bjohnsto@redhat.com>
Reviewed-by: Matthew Sakai <msakai@redhat.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
drivers/md/dm-vdo/dm-vdo-target.c
drivers/md/dm-vdo/vdo.c
drivers/md/dm-vdo/vdo.h

index cee6e4edf76810442bab86ddf5c2e88cd3701096..0135a6f941fd5b621d8525c96fe512bbd1ed8f01 100644 (file)
@@ -61,6 +61,11 @@ enum admin_phases {
        LOAD_PHASE_DRAIN_JOURNAL,
        LOAD_PHASE_WAIT_FOR_READ_ONLY,
        PRE_LOAD_PHASE_START,
+       PRE_LOAD_PHASE_FORMAT_START,
+       PRE_LOAD_PHASE_FORMAT_SUPER,
+       PRE_LOAD_PHASE_FORMAT_GEOMETRY,
+       PRE_LOAD_PHASE_FORMAT_END,
+       PRE_LOAD_PHASE_LOAD_SUPER,
        PRE_LOAD_PHASE_LOAD_COMPONENTS,
        PRE_LOAD_PHASE_END,
        PREPARE_GROW_PHYSICAL_PHASE_START,
@@ -110,6 +115,11 @@ static const char * const ADMIN_PHASE_NAMES[] = {
        "LOAD_PHASE_DRAIN_JOURNAL",
        "LOAD_PHASE_WAIT_FOR_READ_ONLY",
        "PRE_LOAD_PHASE_START",
+       "PRE_LOAD_PHASE_FORMAT_START",
+       "PRE_LOAD_PHASE_FORMAT_SUPER",
+       "PRE_LOAD_PHASE_FORMAT_GEOMETRY",
+       "PRE_LOAD_PHASE_FORMAT_END",
+       "PRE_LOAD_PHASE_LOAD_SUPER",
        "PRE_LOAD_PHASE_LOAD_COMPONENTS",
        "PRE_LOAD_PHASE_END",
        "PREPARE_GROW_PHYSICAL_PHASE_START",
@@ -1487,7 +1497,33 @@ static void pre_load_callback(struct vdo_completion *completion)
                        vdo_continue_completion(completion, result);
                        return;
                }
+               if (vdo->needs_formatting)
+                       vdo->admin.phase = PRE_LOAD_PHASE_FORMAT_START;
+               else
+                       vdo->admin.phase = PRE_LOAD_PHASE_LOAD_SUPER;
 
+               vdo_continue_completion(completion, VDO_SUCCESS);
+               return;
+
+       case PRE_LOAD_PHASE_FORMAT_START:
+               vdo_continue_completion(completion, vdo_clear_layout(vdo));
+               return;
+
+       case PRE_LOAD_PHASE_FORMAT_SUPER:
+               vdo_save_super_block(vdo, completion);
+               return;
+
+       case PRE_LOAD_PHASE_FORMAT_GEOMETRY:
+               vdo_save_geometry_block(vdo, completion);
+               return;
+
+       case PRE_LOAD_PHASE_FORMAT_END:
+               /* cleanup layout before load adds to it */
+               vdo_uninitialize_layout(&vdo->states.layout);
+               vdo_continue_completion(completion, VDO_SUCCESS);
+               return;
+
+       case PRE_LOAD_PHASE_LOAD_SUPER:
                vdo_load_super_block(vdo, completion);
                return;
 
index b5e64af13437c203cf938028acb0576119ca5add..7bec2418c121f00d8dbcc3024dc242314881c14a 100644 (file)
@@ -491,6 +491,8 @@ static int __must_check vdo_format(struct vdo *vdo, char **error_ptr)
                return result;
        }
 
+       vdo->needs_formatting = true;
+
        return VDO_SUCCESS;
 }
 
@@ -951,24 +953,101 @@ static void record_vdo(struct vdo *vdo)
        vdo->states.layout = vdo->layout;
 }
 
+static int __must_check clear_partition(struct vdo *vdo, enum partition_id id)
+{
+       struct partition *partition;
+       int result;
+
+       result = vdo_get_partition(&vdo->states.layout, id, &partition);
+       if (result != VDO_SUCCESS)
+               return result;
+
+       return blkdev_issue_zeroout(vdo_get_backing_device(vdo),
+                                   partition->offset * VDO_SECTORS_PER_BLOCK,
+                                   partition->count * VDO_SECTORS_PER_BLOCK,
+                                   GFP_NOWAIT, 0);
+}
+
+int vdo_clear_layout(struct vdo *vdo)
+{
+       int result;
+
+       /* Zero out the uds index's first block. */
+       result = blkdev_issue_zeroout(vdo_get_backing_device(vdo),
+                                     VDO_SECTORS_PER_BLOCK,
+                                     VDO_SECTORS_PER_BLOCK,
+                                     GFP_NOWAIT, 0);
+       if (result != VDO_SUCCESS)
+               return result;
+
+       result = clear_partition(vdo, VDO_BLOCK_MAP_PARTITION);
+       if (result != VDO_SUCCESS)
+               return result;
+
+       return clear_partition(vdo, VDO_RECOVERY_JOURNAL_PARTITION);
+}
+
 /**
- * continue_super_block_parent() - Continue the parent of a super block save operation.
- * @completion: The super block vio.
+ * continue_parent() - Continue the parent of a save operation.
+ * @completion: The completion to continue.
  *
- * This callback is registered in vdo_save_components().
  */
-static void continue_super_block_parent(struct vdo_completion *completion)
+static void continue_parent(struct vdo_completion *completion)
 {
        vdo_continue_completion(vdo_forget(completion->parent), completion->result);
 }
 
+static void handle_write_endio(struct bio *bio)
+{
+       struct vio *vio = bio->bi_private;
+       struct vdo_completion *parent = vio->completion.parent;
+
+       continue_vio_after_io(vio, continue_parent,
+                             parent->callback_thread_id);
+}
+
 /**
- * handle_save_error() - Log a super block save error.
+ * handle_geometry_block_save_error() - Log a geometry block save error.
+ * @completion: The super block vio.
+ *
+ * This error handler is registered in vdo_save_geometry_block().
+ */
+static void handle_geometry_block_save_error(struct vdo_completion *completion)
+{
+       struct vdo_geometry_block *geometry_block =
+               container_of(as_vio(completion), struct vdo_geometry_block, vio);
+
+       vio_record_metadata_io_error(&geometry_block->vio);
+       vdo_log_error_strerror(completion->result, "geometry block save failed");
+       completion->callback(completion);
+}
+
+/**
+ * vdo_save_geometry_block() - Encode the vdo and save the geometry block asynchronously.
+ * @vdo: The vdo whose state is being saved.
+ * @parent: The completion to notify when the save is complete.
+ */
+void vdo_save_geometry_block(struct vdo *vdo, struct vdo_completion *parent)
+{
+       struct vdo_geometry_block *geometry_block = &vdo->geometry_block;
+
+       vdo_encode_volume_geometry(geometry_block->buffer, &vdo->geometry,
+                                  VDO_DEFAULT_GEOMETRY_BLOCK_VERSION);
+       geometry_block->vio.completion.parent = parent;
+       geometry_block->vio.completion.callback_thread_id = parent->callback_thread_id;
+       vdo_submit_metadata_vio(&geometry_block->vio,
+                               VDO_GEOMETRY_BLOCK_LOCATION,
+                               handle_write_endio, handle_geometry_block_save_error,
+                               REQ_OP_WRITE | REQ_PREFLUSH | REQ_FUA);
+}
+
+/**
+ * handle_super_block_save_error() - Log a super block save error.
  * @completion: The super block vio.
  *
  * This error handler is registered in vdo_save_components().
  */
-static void handle_save_error(struct vdo_completion *completion)
+static void handle_super_block_save_error(struct vdo_completion *completion)
 {
        struct vdo_super_block *super_block =
                container_of(as_vio(completion), struct vdo_super_block, vio);
@@ -987,17 +1066,27 @@ static void handle_save_error(struct vdo_completion *completion)
        completion->callback(completion);
 }
 
-static void super_block_write_endio(struct bio *bio)
+/**
+ * vdo_save_super_block() - Save the component states to the super block asynchronously.
+ * @vdo: The vdo whose state is being saved.
+ * @parent: The completion to notify when the save is complete.
+ */
+void vdo_save_super_block(struct vdo *vdo, struct vdo_completion *parent)
 {
-       struct vio *vio = bio->bi_private;
-       struct vdo_completion *parent = vio->completion.parent;
+       struct vdo_super_block *super_block = &vdo->super_block;
 
-       continue_vio_after_io(vio, continue_super_block_parent,
-                             parent->callback_thread_id);
+       vdo_encode_super_block(super_block->buffer, &vdo->states);
+       super_block->vio.completion.parent = parent;
+       super_block->vio.completion.callback_thread_id = parent->callback_thread_id;
+       vdo_submit_metadata_vio(&super_block->vio,
+                               vdo_get_data_region_start(vdo->geometry),
+                               handle_write_endio, handle_super_block_save_error,
+                               REQ_OP_WRITE | REQ_PREFLUSH | REQ_FUA);
 }
 
 /**
- * vdo_save_components() - Encode the vdo and save the super block asynchronously.
+ * vdo_save_components() - Copy the current state of the VDO to the states struct and save
+ *                         it to the super block asynchronously.
  * @vdo: The vdo whose state is being saved.
  * @parent: The completion to notify when the save is complete.
  */
@@ -1016,14 +1105,7 @@ void vdo_save_components(struct vdo *vdo, struct vdo_completion *parent)
        }
 
        record_vdo(vdo);
-
-       vdo_encode_super_block(super_block->buffer, &vdo->states);
-       super_block->vio.completion.parent = parent;
-       super_block->vio.completion.callback_thread_id = parent->callback_thread_id;
-       vdo_submit_metadata_vio(&super_block->vio,
-                               vdo_get_data_region_start(vdo->geometry),
-                               super_block_write_endio, handle_save_error,
-                               REQ_OP_WRITE | REQ_PREFLUSH | REQ_FUA);
+       vdo_save_super_block(vdo, parent);
 }
 
 /**
index 21f6ac999e9d9e2deb3f708d105cbcb1ce515fff..9a63f5d45ce3658e2ef66ac12666e3485b70bd3d 100644 (file)
@@ -246,6 +246,7 @@ struct vdo {
        const struct admin_state_code *suspend_type;
        bool allocations_allowed;
        bool dump_on_shutdown;
+       bool needs_formatting;
        atomic_t processing_message;
 
        /*
@@ -314,6 +315,10 @@ int __must_check vdo_make(unsigned int instance, struct device_config *config,
 
 void vdo_destroy(struct vdo *vdo);
 
+int __must_check vdo_format_components(struct vdo *vdo);
+
+void vdo_format_super_block(struct vdo *vdo, struct vdo_completion *parent);
+
 void vdo_load_super_block(struct vdo *vdo, struct vdo_completion *parent);
 
 struct block_device * __must_check vdo_get_backing_device(const struct vdo *vdo);
@@ -336,6 +341,10 @@ enum vdo_state __must_check vdo_get_state(const struct vdo *vdo);
 
 void vdo_set_state(struct vdo *vdo, enum vdo_state state);
 
+int vdo_clear_layout(struct vdo *vdo);
+void vdo_save_geometry_block(struct vdo *vdo, struct vdo_completion *parent);
+void vdo_save_super_block(struct vdo *vdo, struct vdo_completion *parent);
+
 void vdo_save_components(struct vdo *vdo, struct vdo_completion *parent);
 
 int vdo_register_read_only_listener(struct vdo *vdo, void *listener,