block: make all steps in qmp_transaction() as callback
Make it easier to add other operations to qmp_transaction() by using callbacks, with external snapshots serving as an example implementation of the callbacks. Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
		
							parent
							
								
									96b86bf72d
								
							
						
					
					
						commit
						ba0c86a34e
					
				
							
								
								
									
										89
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								blockdev.c
									
									
									
									
									
								
							@ -779,14 +779,43 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* New and old BlockDriverState structs for group snapshots */
 | 
					/* New and old BlockDriverState structs for group snapshots */
 | 
				
			||||||
typedef struct BlkTransactionStates {
 | 
					
 | 
				
			||||||
 | 
					typedef struct BlkTransactionStates BlkTransactionStates;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Only prepare() may fail. In a single transaction, only one of commit() or
 | 
				
			||||||
 | 
					   abort() will be called, clean() will always be called if it present. */
 | 
				
			||||||
 | 
					typedef struct BdrvActionOps {
 | 
				
			||||||
 | 
					    /* Size of state struct, in bytes. */
 | 
				
			||||||
 | 
					    size_t instance_size;
 | 
				
			||||||
 | 
					    /* Prepare the work, must NOT be NULL. */
 | 
				
			||||||
 | 
					    void (*prepare)(BlkTransactionStates *common, Error **errp);
 | 
				
			||||||
 | 
					    /* Commit the changes, must NOT be NULL. */
 | 
				
			||||||
 | 
					    void (*commit)(BlkTransactionStates *common);
 | 
				
			||||||
 | 
					    /* Abort the changes on fail, can be NULL. */
 | 
				
			||||||
 | 
					    void (*abort)(BlkTransactionStates *common);
 | 
				
			||||||
 | 
					    /* Clean up resource in the end, can be NULL. */
 | 
				
			||||||
 | 
					    void (*clean)(BlkTransactionStates *common);
 | 
				
			||||||
 | 
					} BdrvActionOps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This structure must be arranged as first member in child type, assuming
 | 
				
			||||||
 | 
					 * that compiler will also arrange it to the same address with parent instance.
 | 
				
			||||||
 | 
					 * Later it will be used in free().
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct BlkTransactionStates {
 | 
				
			||||||
 | 
					    BlockdevAction *action;
 | 
				
			||||||
 | 
					    const BdrvActionOps *ops;
 | 
				
			||||||
 | 
					    QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* external snapshot private data */
 | 
				
			||||||
 | 
					typedef struct ExternalSnapshotStates {
 | 
				
			||||||
 | 
					    BlkTransactionStates common;
 | 
				
			||||||
    BlockDriverState *old_bs;
 | 
					    BlockDriverState *old_bs;
 | 
				
			||||||
    BlockDriverState *new_bs;
 | 
					    BlockDriverState *new_bs;
 | 
				
			||||||
    QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
 | 
					} ExternalSnapshotStates;
 | 
				
			||||||
} BlkTransactionStates;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void external_snapshot_prepare(BlockdevAction *action,
 | 
					static void external_snapshot_prepare(BlkTransactionStates *common,
 | 
				
			||||||
                                      BlkTransactionStates *states,
 | 
					 | 
				
			||||||
                                      Error **errp)
 | 
					                                      Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    BlockDriver *proto_drv;
 | 
					    BlockDriver *proto_drv;
 | 
				
			||||||
@ -797,6 +826,9 @@ static void external_snapshot_prepare(BlockdevAction *action,
 | 
				
			|||||||
    const char *new_image_file;
 | 
					    const char *new_image_file;
 | 
				
			||||||
    const char *format = "qcow2";
 | 
					    const char *format = "qcow2";
 | 
				
			||||||
    enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
 | 
					    enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
 | 
				
			||||||
 | 
					    ExternalSnapshotStates *states =
 | 
				
			||||||
 | 
					                             DO_UPCAST(ExternalSnapshotStates, common, common);
 | 
				
			||||||
 | 
					    BlockdevAction *action = common->action;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* get parameters */
 | 
					    /* get parameters */
 | 
				
			||||||
    g_assert(action->kind == BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
 | 
					    g_assert(action->kind == BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
 | 
				
			||||||
@ -871,8 +903,11 @@ static void external_snapshot_prepare(BlockdevAction *action,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void external_snapshot_commit(BlkTransactionStates *states)
 | 
					static void external_snapshot_commit(BlkTransactionStates *common)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    ExternalSnapshotStates *states =
 | 
				
			||||||
 | 
					                             DO_UPCAST(ExternalSnapshotStates, common, common);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* This removes our old bs from the bdrv_states, and adds the new bs */
 | 
					    /* This removes our old bs from the bdrv_states, and adds the new bs */
 | 
				
			||||||
    bdrv_append(states->new_bs, states->old_bs);
 | 
					    bdrv_append(states->new_bs, states->old_bs);
 | 
				
			||||||
    /* We don't need (or want) to use the transactional
 | 
					    /* We don't need (or want) to use the transactional
 | 
				
			||||||
@ -882,13 +917,24 @@ static void external_snapshot_commit(BlkTransactionStates *states)
 | 
				
			|||||||
                NULL);
 | 
					                NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void external_snapshot_abort(BlkTransactionStates *states)
 | 
					static void external_snapshot_abort(BlkTransactionStates *common)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    ExternalSnapshotStates *states =
 | 
				
			||||||
 | 
					                             DO_UPCAST(ExternalSnapshotStates, common, common);
 | 
				
			||||||
    if (states->new_bs) {
 | 
					    if (states->new_bs) {
 | 
				
			||||||
        bdrv_delete(states->new_bs);
 | 
					        bdrv_delete(states->new_bs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const BdrvActionOps actions[] = {
 | 
				
			||||||
 | 
					    [BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
 | 
				
			||||||
 | 
					        .instance_size = sizeof(ExternalSnapshotStates),
 | 
				
			||||||
 | 
					        .prepare  = external_snapshot_prepare,
 | 
				
			||||||
 | 
					        .commit   = external_snapshot_commit,
 | 
				
			||||||
 | 
					        .abort = external_snapshot_abort,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * 'Atomic' group snapshots.  The snapshots are taken as a set, and if any fail
 | 
					 * 'Atomic' group snapshots.  The snapshots are taken as a set, and if any fail
 | 
				
			||||||
 *  then we do not pivot any of the devices in the group, and abandon the
 | 
					 *  then we do not pivot any of the devices in the group, and abandon the
 | 
				
			||||||
@ -909,32 +955,28 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
 | 
				
			|||||||
    /* We don't do anything in this loop that commits us to the snapshot */
 | 
					    /* We don't do anything in this loop that commits us to the snapshot */
 | 
				
			||||||
    while (NULL != dev_entry) {
 | 
					    while (NULL != dev_entry) {
 | 
				
			||||||
        BlockdevAction *dev_info = NULL;
 | 
					        BlockdevAction *dev_info = NULL;
 | 
				
			||||||
 | 
					        const BdrvActionOps *ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dev_info = dev_entry->value;
 | 
					        dev_info = dev_entry->value;
 | 
				
			||||||
        dev_entry = dev_entry->next;
 | 
					        dev_entry = dev_entry->next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        states = g_malloc0(sizeof(BlkTransactionStates));
 | 
					        assert(dev_info->kind < ARRAY_SIZE(actions));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ops = &actions[dev_info->kind];
 | 
				
			||||||
 | 
					        states = g_malloc0(ops->instance_size);
 | 
				
			||||||
 | 
					        states->ops = ops;
 | 
				
			||||||
 | 
					        states->action = dev_info;
 | 
				
			||||||
        QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
 | 
					        QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        switch (dev_info->kind) {
 | 
					        states->ops->prepare(states, &local_err);
 | 
				
			||||||
        case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
 | 
					 | 
				
			||||||
            external_snapshot_prepare(dev_info, states, errp);
 | 
					 | 
				
			||||||
        if (error_is_set(&local_err)) {
 | 
					        if (error_is_set(&local_err)) {
 | 
				
			||||||
            error_propagate(errp, local_err);
 | 
					            error_propagate(errp, local_err);
 | 
				
			||||||
            goto delete_and_fail;
 | 
					            goto delete_and_fail;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
            abort();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Now we are going to do the actual pivot.  Everything up to this point
 | 
					 | 
				
			||||||
     * is reversible, but we are committed at this point */
 | 
					 | 
				
			||||||
    QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
 | 
					    QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
 | 
				
			||||||
        external_snapshot_commit(states);
 | 
					        states->ops->commit(states);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* success */
 | 
					    /* success */
 | 
				
			||||||
@ -946,10 +988,15 @@ delete_and_fail:
 | 
				
			|||||||
    * the original bs for all images
 | 
					    * the original bs for all images
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
    QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
 | 
					    QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
 | 
				
			||||||
        external_snapshot_abort(states);
 | 
					        if (states->ops->abort) {
 | 
				
			||||||
 | 
					            states->ops->abort(states);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
exit:
 | 
					exit:
 | 
				
			||||||
    QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
 | 
					    QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
 | 
				
			||||||
 | 
					        if (states->ops->clean) {
 | 
				
			||||||
 | 
					            states->ops->clean(states);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        g_free(states);
 | 
					        g_free(states);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user