From: Benoît Canet <address@hidden>
Example of command line:
-drive if=virtio,file.driver=quorum,\
file.children.0.file.filename=1.raw,\
file.children.0.node-name=1.raw,\
file.children.0.driver=raw,\
file.children.1.file.filename=2.raw,\
file.children.1.node-name=2.raw,\
file.children.1.driver=raw,\
file.children.2.file.filename=3.raw,\
file.children.2.node-name=3.raw,\
file.children.2.driver=raw,\
file.vote_threshold=2
file.blkverify=on with file.vote_threshold=2 and two files can be passed to
emulated blkverify.
Signed-off-by: Benoit Canet <address@hidden>
---
block/quorum.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
qapi-schema.json | 21 ++++++-
2 files changed, 189 insertions(+), 1 deletion(-)
diff --git a/block/quorum.c b/block/quorum.c
index a4716b3..582bf2c 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -17,8 +17,12 @@
#include <gnutls/crypto.h>
#include "block/block_int.h"
#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/types.h"
+#include "qemu-common.h"
#define HASH_LENGTH 32
+#define KEY_PREFIX "children."
+#define KEY_FILENAME_SUFFIX ".file.filename"
/* This union holds a vote hash value */
typedef union QuorumVoteValue {
@@ -720,12 +724,177 @@ static bool
quorum_recurse_is_first_non_filter(BlockDriverState *bs,
return false;
}
+static int quorum_valid_threshold(int threshold,
+ int total,
+ Error **errp)
+{
+
+ if (threshold < 1) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ "vote-threshold", "value >= 1");
+ return -ERANGE;
+ }
+
+ if (threshold > total) {
+ error_setg(errp, "threshold may not exceed children count");
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static int quorum_open(BlockDriverState *bs,
+ QDict *options,
+ int flags,
+ Error **errp)
+{
+ BDRVQuorumState *s = bs->opaque;
+ Error *local_err = NULL;
+ bool *opened;
+ QDict *sub = NULL;
+ QList *list = NULL;
+ const QListEntry *lentry;
+ const QDictEntry *dentry;
+ const char *value;
+ char *next;
+ int i;
+ int ret = 0;
+ unsigned long long threshold = 0;
+
+ qdict_extract_subqdict(options, &sub, "children.");
+ qdict_array_split(sub, &list);
+
+ /* count how many different children are present and validate */
+ s->total = !qlist_size(list) ? qdict_size(sub) : qlist_size(list);
+ if (s->total < 2) {
+ error_setg(&local_err,
+ "Number of provided children must be greater than 1");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = qdict_get_try_int(options, "vote-threshold", -1);
+ /* from QMP */
+ if (ret != -1) {
+ qdict_del(options, "vote-threshold");
+ s->threshold = ret;
+ /* from command line */
+ } else {
+ /* retrieve the threshold option from the command line */
+ value = qdict_get_try_str(options, "vote_threshold");
+ if (!value) {
+ error_setg(&local_err,
+ "vote_threshold must be provided");
+ ret = -EINVAL;
+ goto exit;
+ }
+ qdict_del(options, "vote_threshold");
+
+ ret = parse_uint(value, &threshold, &next, 10);
+
+ /* no int found -> scan fail */
+ if (ret < 0) {
+ error_setg(&local_err,
+ "invalid vote_threshold specified");
+ ret = -EINVAL;
+ goto exit;
+ }
+ s->threshold = threshold;
+ }
+
+ /* and validate it againt s->total */
+ ret = quorum_valid_threshold(s->threshold, s->total, &local_err);
+ if (ret < 0) {
+ goto exit;
+ }
+
+ /* is the driver in blkverify mode */
+ value = qdict_get_try_str(options, "blkverify");
+ if (value && !strcmp(value, "on") &&
+ s->total == 2 && s->threshold == 2) {
+ s->is_blkverify = true;
+ } else if (value && strcmp(value, "off")) {
+ fprintf(stderr, "blkverify mode is set by setting blkverify=on "
+ "and using two files with vote_threshold=2");
+ }
+ qdict_del(options, "blkverify");
+
+ /* allocate the children BlockDriverState array */
+ s->bs = g_new0(BlockDriverState *, s->total);
+ opened = g_new0(bool, s->total);
+
+ /* Opening by file name or options */
+ for (i = 0, lentry = qlist_first(list);
+ s->total == qlist_size(list) && lentry;
+ lentry = qlist_next(lentry), i++) {
+ ret = bdrv_open(&s->bs[i], NULL, NULL, qobject_to_qdict(lentry->value),
+ flags, NULL, &local_err);
+ if (ret < 0) {
+ goto close_exit;
+ }
+ opened[i] = true;
+ }
+
+ /* Opening by reference */
+ for (i = 0, dentry = qdict_first(sub);
+ s->total == qdict_size(sub) && dentry;
+ dentry = qdict_next(sub, dentry), i++) {
+ ret = bdrv_open(&s->bs[i], NULL,
+ qstring_get_str(qobject_to_qstring(dentry->value)),
+ NULL, flags, NULL, &local_err);
+ if (ret < 0) {
+ goto close_exit;
+ }
+ opened[i] = true;
+ }