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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user