quorum: Add quorum_open() and quorum_close().
Example of command line: -drive if=virtio,driver=quorum,\ children.0.file.filename=1.raw,\ children.0.node-name=1.raw,\ children.0.driver=raw,\ children.1.file.filename=2.raw,\ children.1.node-name=2.raw,\ children.1.driver=raw,\ children.2.file.filename=3.raw,\ children.2.node-name=3.raw,\ children.2.driver=raw,\ vote-threshold=2 blkverify=on with vote-threshold=2 and two files can be passed to emulate blkverify. Signed-off-by: Benoit Canet <benoit@irqsave.net> Reviewed-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
98a7a38f81
commit
c88a1de51a
161
block/quorum.c
161
block/quorum.c
|
@ -20,6 +20,9 @@
|
||||||
|
|
||||||
#define HASH_LENGTH 32
|
#define HASH_LENGTH 32
|
||||||
|
|
||||||
|
#define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold"
|
||||||
|
#define QUORUM_OPT_BLKVERIFY "blkverify"
|
||||||
|
|
||||||
/* This union holds a vote hash value */
|
/* This union holds a vote hash value */
|
||||||
typedef union QuorumVoteValue {
|
typedef union QuorumVoteValue {
|
||||||
char h[HASH_LENGTH]; /* SHA-256 hash */
|
char h[HASH_LENGTH]; /* SHA-256 hash */
|
||||||
|
@ -672,12 +675,170 @@ static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int quorum_valid_threshold(int threshold, int num_children, Error **errp)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (threshold < 1) {
|
||||||
|
error_set(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||||
|
"vote-threshold", "value >= 1");
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threshold > num_children) {
|
||||||
|
error_setg(errp, "threshold may not exceed children count");
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QemuOptsList quorum_runtime_opts = {
|
||||||
|
.name = "quorum",
|
||||||
|
.head = QTAILQ_HEAD_INITIALIZER(quorum_runtime_opts.head),
|
||||||
|
.desc = {
|
||||||
|
{
|
||||||
|
.name = QUORUM_OPT_VOTE_THRESHOLD,
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "The number of vote needed for reaching quorum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = QUORUM_OPT_BLKVERIFY,
|
||||||
|
.type = QEMU_OPT_BOOL,
|
||||||
|
.help = "Trigger block verify mode if set",
|
||||||
|
},
|
||||||
|
{ /* end of list */ }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
QemuOpts *opts;
|
||||||
|
bool *opened;
|
||||||
|
QDict *sub = NULL;
|
||||||
|
QList *list = NULL;
|
||||||
|
const QListEntry *lentry;
|
||||||
|
const QDictEntry *dentry;
|
||||||
|
int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
qdict_flatten(options);
|
||||||
|
qdict_extract_subqdict(options, &sub, "children.");
|
||||||
|
qdict_array_split(sub, &list);
|
||||||
|
|
||||||
|
/* count how many different children are present and validate
|
||||||
|
* qdict_size(sub) address the open by reference case
|
||||||
|
*/
|
||||||
|
s->num_children = !qlist_size(list) ? qdict_size(sub) : qlist_size(list);
|
||||||
|
if (s->num_children < 2) {
|
||||||
|
error_setg(&local_err,
|
||||||
|
"Number of provided children must be greater than 1");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = qemu_opts_create(&quorum_runtime_opts, NULL, 0, &error_abort);
|
||||||
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0);
|
||||||
|
|
||||||
|
/* and validate it against s->num_children */
|
||||||
|
ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is the driver in blkverify mode */
|
||||||
|
if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
|
||||||
|
s->num_children == 2 && s->threshold == 2) {
|
||||||
|
s->is_blkverify = true;
|
||||||
|
} else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
|
||||||
|
fprintf(stderr, "blkverify mode is set by setting blkverify=on "
|
||||||
|
"and using two files with vote_threshold=2\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate the children BlockDriverState array */
|
||||||
|
s->bs = g_new0(BlockDriverState *, s->num_children);
|
||||||
|
opened = g_new0(bool, s->num_children);
|
||||||
|
|
||||||
|
/* Open by file name or options dict (command line or QMP) */
|
||||||
|
if (s->num_children == qlist_size(list)) {
|
||||||
|
for (i = 0, lentry = qlist_first(list); lentry;
|
||||||
|
lentry = qlist_next(lentry), i++) {
|
||||||
|
QDict *d = qobject_to_qdict(lentry->value);
|
||||||
|
QINCREF(d);
|
||||||
|
ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL, &local_err);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto close_exit;
|
||||||
|
}
|
||||||
|
opened[i] = true;
|
||||||
|
}
|
||||||
|
/* Open by QMP references */
|
||||||
|
} else {
|
||||||
|
for (i = 0, dentry = qdict_first(sub); dentry;
|
||||||
|
dentry = qdict_next(sub, dentry), i++) {
|
||||||
|
QString *string = qobject_to_qstring(dentry->value);
|
||||||
|
ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
|
||||||
|
flags, NULL, &local_err);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto close_exit;
|
||||||
|
}
|
||||||
|
opened[i] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(opened);
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
close_exit:
|
||||||
|
/* cleanup on error */
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
if (!opened[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bdrv_unref(s->bs[i]);
|
||||||
|
}
|
||||||
|
g_free(s->bs);
|
||||||
|
g_free(opened);
|
||||||
|
exit:
|
||||||
|
/* propagate error */
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
}
|
||||||
|
QDECREF(list);
|
||||||
|
QDECREF(sub);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
bdrv_unref(s->bs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(s->bs);
|
||||||
|
}
|
||||||
|
|
||||||
static BlockDriver bdrv_quorum = {
|
static BlockDriver bdrv_quorum = {
|
||||||
.format_name = "quorum",
|
.format_name = "quorum",
|
||||||
.protocol_name = "quorum",
|
.protocol_name = "quorum",
|
||||||
|
|
||||||
.instance_size = sizeof(BDRVQuorumState),
|
.instance_size = sizeof(BDRVQuorumState),
|
||||||
|
|
||||||
|
.bdrv_file_open = quorum_open,
|
||||||
|
.bdrv_close = quorum_close,
|
||||||
|
|
||||||
|
.authorizations = { true, true },
|
||||||
|
|
||||||
.bdrv_co_flush_to_disk = quorum_co_flush,
|
.bdrv_co_flush_to_disk = quorum_co_flush,
|
||||||
|
|
||||||
.bdrv_getlength = quorum_getlength,
|
.bdrv_getlength = quorum_getlength,
|
||||||
|
|
|
@ -640,6 +640,9 @@ static void monitor_protocol_event_init(void)
|
||||||
monitor_protocol_event_throttle(QEVENT_RTC_CHANGE, 1000);
|
monitor_protocol_event_throttle(QEVENT_RTC_CHANGE, 1000);
|
||||||
monitor_protocol_event_throttle(QEVENT_BALLOON_CHANGE, 1000);
|
monitor_protocol_event_throttle(QEVENT_BALLOON_CHANGE, 1000);
|
||||||
monitor_protocol_event_throttle(QEVENT_WATCHDOG, 1000);
|
monitor_protocol_event_throttle(QEVENT_WATCHDOG, 1000);
|
||||||
|
/* limit the rate of quorum events to avoid hammering the management */
|
||||||
|
monitor_protocol_event_throttle(QEVENT_QUORUM_REPORT_BAD, 1000);
|
||||||
|
monitor_protocol_event_throttle(QEVENT_QUORUM_FAILURE, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4431,6 +4431,24 @@
|
||||||
'data': { 'test': 'BlockdevRef',
|
'data': { 'test': 'BlockdevRef',
|
||||||
'raw': 'BlockdevRef' } }
|
'raw': 'BlockdevRef' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevOptionsQuorum
|
||||||
|
#
|
||||||
|
# Driver specific block device options for Quorum
|
||||||
|
#
|
||||||
|
# @blkverify: #optional true if the driver must print content mismatch
|
||||||
|
#
|
||||||
|
# @children: the children block device to use
|
||||||
|
#
|
||||||
|
# @vote_threshold: the vote limit under which a read will fail
|
||||||
|
#
|
||||||
|
# Since: 2.0
|
||||||
|
##
|
||||||
|
{ 'type': 'BlockdevOptionsQuorum',
|
||||||
|
'data': { '*blkverify': 'bool',
|
||||||
|
'children': [ 'BlockdevRef' ],
|
||||||
|
'vote-threshold': 'int' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevOptions
|
# @BlockdevOptions
|
||||||
#
|
#
|
||||||
|
@ -4470,7 +4488,8 @@
|
||||||
'vdi': 'BlockdevOptionsGenericFormat',
|
'vdi': 'BlockdevOptionsGenericFormat',
|
||||||
'vhdx': 'BlockdevOptionsGenericFormat',
|
'vhdx': 'BlockdevOptionsGenericFormat',
|
||||||
'vmdk': 'BlockdevOptionsGenericCOWFormat',
|
'vmdk': 'BlockdevOptionsGenericCOWFormat',
|
||||||
'vpc': 'BlockdevOptionsGenericFormat'
|
'vpc': 'BlockdevOptionsGenericFormat',
|
||||||
|
'quorum': 'BlockdevOptionsQuorum'
|
||||||
} }
|
} }
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
Loading…
Reference in New Issue