Commit fc1d4382 authored by Bruce Johnston's avatar Bruce Johnston Committed by Mikulas Patocka
Browse files

dm vdo: save the formatted metadata to disk



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: default avatarBruce Johnston <bjohnsto@redhat.com>
Reviewed-by: default avatarMatthew Sakai <msakai@redhat.com>
Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
parent defce4e0
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -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;

+102 −20
Original line number Diff line number Diff line
@@ -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);
}

/**
+9 −0
Original line number Diff line number Diff line
@@ -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,