Revision ba0c86a3
b/blockdev.c | ||
---|---|---|
779 | 779 |
|
780 | 780 |
|
781 | 781 |
/* New and old BlockDriverState structs for group snapshots */ |
782 |
typedef struct BlkTransactionStates { |
|
782 |
|
|
783 |
typedef struct BlkTransactionStates BlkTransactionStates; |
|
784 |
|
|
785 |
/* Only prepare() may fail. In a single transaction, only one of commit() or |
|
786 |
abort() will be called, clean() will always be called if it present. */ |
|
787 |
typedef struct BdrvActionOps { |
|
788 |
/* Size of state struct, in bytes. */ |
|
789 |
size_t instance_size; |
|
790 |
/* Prepare the work, must NOT be NULL. */ |
|
791 |
void (*prepare)(BlkTransactionStates *common, Error **errp); |
|
792 |
/* Commit the changes, must NOT be NULL. */ |
|
793 |
void (*commit)(BlkTransactionStates *common); |
|
794 |
/* Abort the changes on fail, can be NULL. */ |
|
795 |
void (*abort)(BlkTransactionStates *common); |
|
796 |
/* Clean up resource in the end, can be NULL. */ |
|
797 |
void (*clean)(BlkTransactionStates *common); |
|
798 |
} BdrvActionOps; |
|
799 |
|
|
800 |
/* |
|
801 |
* This structure must be arranged as first member in child type, assuming |
|
802 |
* that compiler will also arrange it to the same address with parent instance. |
|
803 |
* Later it will be used in free(). |
|
804 |
*/ |
|
805 |
struct BlkTransactionStates { |
|
806 |
BlockdevAction *action; |
|
807 |
const BdrvActionOps *ops; |
|
808 |
QSIMPLEQ_ENTRY(BlkTransactionStates) entry; |
|
809 |
}; |
|
810 |
|
|
811 |
/* external snapshot private data */ |
|
812 |
typedef struct ExternalSnapshotStates { |
|
813 |
BlkTransactionStates common; |
|
783 | 814 |
BlockDriverState *old_bs; |
784 | 815 |
BlockDriverState *new_bs; |
785 |
QSIMPLEQ_ENTRY(BlkTransactionStates) entry; |
|
786 |
} BlkTransactionStates; |
|
816 |
} ExternalSnapshotStates; |
|
787 | 817 |
|
788 |
static void external_snapshot_prepare(BlockdevAction *action, |
|
789 |
BlkTransactionStates *states, |
|
818 |
static void external_snapshot_prepare(BlkTransactionStates *common, |
|
790 | 819 |
Error **errp) |
791 | 820 |
{ |
792 | 821 |
BlockDriver *proto_drv; |
... | ... | |
797 | 826 |
const char *new_image_file; |
798 | 827 |
const char *format = "qcow2"; |
799 | 828 |
enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; |
829 |
ExternalSnapshotStates *states = |
|
830 |
DO_UPCAST(ExternalSnapshotStates, common, common); |
|
831 |
BlockdevAction *action = common->action; |
|
800 | 832 |
|
801 | 833 |
/* get parameters */ |
802 | 834 |
g_assert(action->kind == BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC); |
... | ... | |
871 | 903 |
} |
872 | 904 |
} |
873 | 905 |
|
874 |
static void external_snapshot_commit(BlkTransactionStates *states)
|
|
906 |
static void external_snapshot_commit(BlkTransactionStates *common)
|
|
875 | 907 |
{ |
908 |
ExternalSnapshotStates *states = |
|
909 |
DO_UPCAST(ExternalSnapshotStates, common, common); |
|
910 |
|
|
876 | 911 |
/* This removes our old bs from the bdrv_states, and adds the new bs */ |
877 | 912 |
bdrv_append(states->new_bs, states->old_bs); |
878 | 913 |
/* We don't need (or want) to use the transactional |
... | ... | |
882 | 917 |
NULL); |
883 | 918 |
} |
884 | 919 |
|
885 |
static void external_snapshot_abort(BlkTransactionStates *states)
|
|
920 |
static void external_snapshot_abort(BlkTransactionStates *common)
|
|
886 | 921 |
{ |
922 |
ExternalSnapshotStates *states = |
|
923 |
DO_UPCAST(ExternalSnapshotStates, common, common); |
|
887 | 924 |
if (states->new_bs) { |
888 | 925 |
bdrv_delete(states->new_bs); |
889 | 926 |
} |
890 | 927 |
} |
891 | 928 |
|
929 |
static const BdrvActionOps actions[] = { |
|
930 |
[BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = { |
|
931 |
.instance_size = sizeof(ExternalSnapshotStates), |
|
932 |
.prepare = external_snapshot_prepare, |
|
933 |
.commit = external_snapshot_commit, |
|
934 |
.abort = external_snapshot_abort, |
|
935 |
}, |
|
936 |
}; |
|
937 |
|
|
892 | 938 |
/* |
893 | 939 |
* 'Atomic' group snapshots. The snapshots are taken as a set, and if any fail |
894 | 940 |
* then we do not pivot any of the devices in the group, and abandon the |
... | ... | |
909 | 955 |
/* We don't do anything in this loop that commits us to the snapshot */ |
910 | 956 |
while (NULL != dev_entry) { |
911 | 957 |
BlockdevAction *dev_info = NULL; |
958 |
const BdrvActionOps *ops; |
|
912 | 959 |
|
913 | 960 |
dev_info = dev_entry->value; |
914 | 961 |
dev_entry = dev_entry->next; |
915 | 962 |
|
916 |
states = g_malloc0(sizeof(BlkTransactionStates)); |
|
963 |
assert(dev_info->kind < ARRAY_SIZE(actions)); |
|
964 |
|
|
965 |
ops = &actions[dev_info->kind]; |
|
966 |
states = g_malloc0(ops->instance_size); |
|
967 |
states->ops = ops; |
|
968 |
states->action = dev_info; |
|
917 | 969 |
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry); |
918 | 970 |
|
919 |
switch (dev_info->kind) { |
|
920 |
case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: |
|
921 |
external_snapshot_prepare(dev_info, states, errp); |
|
922 |
if (error_is_set(&local_err)) { |
|
923 |
error_propagate(errp, local_err); |
|
924 |
goto delete_and_fail; |
|
925 |
} |
|
926 |
break; |
|
927 |
default: |
|
928 |
abort(); |
|
971 |
states->ops->prepare(states, &local_err); |
|
972 |
if (error_is_set(&local_err)) { |
|
973 |
error_propagate(errp, local_err); |
|
974 |
goto delete_and_fail; |
|
929 | 975 |
} |
930 |
|
|
931 | 976 |
} |
932 | 977 |
|
933 |
|
|
934 |
/* Now we are going to do the actual pivot. Everything up to this point |
|
935 |
* is reversible, but we are committed at this point */ |
|
936 | 978 |
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) { |
937 |
external_snapshot_commit(states);
|
|
979 |
states->ops->commit(states);
|
|
938 | 980 |
} |
939 | 981 |
|
940 | 982 |
/* success */ |
... | ... | |
946 | 988 |
* the original bs for all images |
947 | 989 |
*/ |
948 | 990 |
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) { |
949 |
external_snapshot_abort(states); |
|
991 |
if (states->ops->abort) { |
|
992 |
states->ops->abort(states); |
|
993 |
} |
|
950 | 994 |
} |
951 | 995 |
exit: |
952 | 996 |
QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) { |
997 |
if (states->ops->clean) { |
|
998 |
states->ops->clean(states); |
|
999 |
} |
|
953 | 1000 |
g_free(states); |
954 | 1001 |
} |
955 | 1002 |
} |
Also available in: Unified diff