提交 4c5dad04 编写于 作者: A Anthony Liguori

Merge remote-tracking branch 'stefanha/block' into staging

# By Wenchao Xia (5) and others
# Via Stefan Hajnoczi
* stefanha/block:
  coroutine: stop using AioContext in CoQueue
  coroutine: protect global pool with a mutex
  qemu-iotests: Try creating huge qcow2 image
  qcow2.py: Subcommand for changing header fields
  qemu-io: Fix 'map' output
  blockdev: Rename BlockdevAction -> TransactionAction
  block: make all steps in qmp_transaction() as callback
  block: package rollback code in qmp_transaction()
  block: package committing code in qmp_transaction()
  block: move input parsing code in qmp_transaction()
  block: package preparation code in qmp_transaction()

Message-id: 1369405947-14818-1-git-send-email-stefanha@redhat.com
Signed-off-by: NAnthony Liguori <aliguori@us.ibm.com>
......@@ -750,8 +750,8 @@ void do_commit(Monitor *mon, const QDict *qdict)
static void blockdev_do_action(int kind, void *data, Error **errp)
{
BlockdevAction action;
BlockdevActionList list;
TransactionAction action;
TransactionActionList list;
action.kind = kind;
action.data = data;
......@@ -773,27 +773,176 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
.has_mode = has_mode,
.mode = mode,
};
blockdev_do_action(BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, &snapshot,
errp);
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
&snapshot, errp);
}
/* 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 {
TransactionAction *action;
const BdrvActionOps *ops;
QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
};
/* external snapshot private data */
typedef struct ExternalSnapshotStates {
BlkTransactionStates common;
BlockDriverState *old_bs;
BlockDriverState *new_bs;
QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
} BlkTransactionStates;
} ExternalSnapshotStates;
static void external_snapshot_prepare(BlkTransactionStates *common,
Error **errp)
{
BlockDriver *proto_drv;
BlockDriver *drv;
int flags, ret;
Error *local_err = NULL;
const char *device;
const char *new_image_file;
const char *format = "qcow2";
enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
ExternalSnapshotStates *states =
DO_UPCAST(ExternalSnapshotStates, common, common);
TransactionAction *action = common->action;
/* get parameters */
g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
device = action->blockdev_snapshot_sync->device;
new_image_file = action->blockdev_snapshot_sync->snapshot_file;
if (action->blockdev_snapshot_sync->has_format) {
format = action->blockdev_snapshot_sync->format;
}
if (action->blockdev_snapshot_sync->has_mode) {
mode = action->blockdev_snapshot_sync->mode;
}
/* start processing */
drv = bdrv_find_format(format);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
}
states->old_bs = bdrv_find(device);
if (!states->old_bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (!bdrv_is_inserted(states->old_bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return;
}
if (bdrv_in_use(states->old_bs)) {
error_set(errp, QERR_DEVICE_IN_USE, device);
return;
}
if (!bdrv_is_read_only(states->old_bs)) {
if (bdrv_flush(states->old_bs)) {
error_set(errp, QERR_IO_ERROR);
return;
}
}
flags = states->old_bs->open_flags;
proto_drv = bdrv_find_protocol(new_image_file);
if (!proto_drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
}
/* create new image w/backing file */
if (mode != NEW_IMAGE_MODE_EXISTING) {
bdrv_img_create(new_image_file, format,
states->old_bs->filename,
states->old_bs->drv->format_name,
NULL, -1, flags, &local_err, false);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
return;
}
}
/* We will manually add the backing_hd field to the bs later */
states->new_bs = bdrv_new("");
/* TODO Inherit bs->options or only take explicit options with an
* extended QMP command? */
ret = bdrv_open(states->new_bs, new_image_file, NULL,
flags | BDRV_O_NO_BACKING, drv);
if (ret != 0) {
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
}
}
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 */
bdrv_append(states->new_bs, states->old_bs);
/* We don't need (or want) to use the transactional
* bdrv_reopen_multiple() across all the entries at once, because we
* don't want to abort all of them if one of them fails the reopen */
bdrv_reopen(states->new_bs, states->new_bs->open_flags & ~BDRV_O_RDWR,
NULL);
}
static void external_snapshot_abort(BlkTransactionStates *common)
{
ExternalSnapshotStates *states =
DO_UPCAST(ExternalSnapshotStates, common, common);
if (states->new_bs) {
bdrv_delete(states->new_bs);
}
}
static const BdrvActionOps actions[] = {
[TRANSACTION_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
* then we do not pivot any of the devices in the group, and abandon the
* snapshots
*/
void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
void qmp_transaction(TransactionActionList *dev_list, Error **errp)
{
int ret = 0;
BlockdevActionList *dev_entry = dev_list;
TransactionActionList *dev_entry = dev_list;
BlkTransactionStates *states, *next;
Error *local_err = NULL;
......@@ -805,109 +954,29 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
/* We don't do anything in this loop that commits us to the snapshot */
while (NULL != dev_entry) {
BlockdevAction *dev_info = NULL;
BlockDriver *proto_drv;
BlockDriver *drv;
int flags;
enum NewImageMode mode;
const char *new_image_file;
const char *device;
const char *format = "qcow2";
TransactionAction *dev_info = NULL;
const BdrvActionOps *ops;
dev_info = dev_entry->value;
dev_entry = dev_entry->next;
states = g_malloc0(sizeof(BlkTransactionStates));
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
switch (dev_info->kind) {
case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
device = dev_info->blockdev_snapshot_sync->device;
if (!dev_info->blockdev_snapshot_sync->has_mode) {
dev_info->blockdev_snapshot_sync->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
new_image_file = dev_info->blockdev_snapshot_sync->snapshot_file;
if (dev_info->blockdev_snapshot_sync->has_format) {
format = dev_info->blockdev_snapshot_sync->format;
}
mode = dev_info->blockdev_snapshot_sync->mode;
break;
default:
abort();
}
assert(dev_info->kind < ARRAY_SIZE(actions));
drv = bdrv_find_format(format);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
goto delete_and_fail;
}
states->old_bs = bdrv_find(device);
if (!states->old_bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
goto delete_and_fail;
}
if (!bdrv_is_inserted(states->old_bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
goto delete_and_fail;
}
if (bdrv_in_use(states->old_bs)) {
error_set(errp, QERR_DEVICE_IN_USE, device);
goto delete_and_fail;
}
if (!bdrv_is_read_only(states->old_bs)) {
if (bdrv_flush(states->old_bs)) {
error_set(errp, QERR_IO_ERROR);
goto delete_and_fail;
}
}
flags = states->old_bs->open_flags;
proto_drv = bdrv_find_protocol(new_image_file);
if (!proto_drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
goto delete_and_fail;
}
/* create new image w/backing file */
if (mode != NEW_IMAGE_MODE_EXISTING) {
bdrv_img_create(new_image_file, format,
states->old_bs->filename,
states->old_bs->drv->format_name,
NULL, -1, flags, &local_err, false);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
goto delete_and_fail;
}
}
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);
/* We will manually add the backing_hd field to the bs later */
states->new_bs = bdrv_new("");
/* TODO Inherit bs->options or only take explicit options with an
* extended QMP command? */
ret = bdrv_open(states->new_bs, new_image_file, NULL,
flags | BDRV_O_NO_BACKING, drv);
if (ret != 0) {
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
states->ops->prepare(states, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
goto delete_and_fail;
}
}
/* 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) {
/* This removes our old bs from the bdrv_states, and adds the new bs */
bdrv_append(states->new_bs, states->old_bs);
/* We don't need (or want) to use the transactional
* bdrv_reopen_multiple() across all the entries at once, because we
* don't want to abort all of them if one of them fails the reopen */
bdrv_reopen(states->new_bs, states->new_bs->open_flags & ~BDRV_O_RDWR,
NULL);
states->ops->commit(states);
}
/* success */
......@@ -919,12 +988,15 @@ delete_and_fail:
* the original bs for all images
*/
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
if (states->new_bs) {
bdrv_delete(states->new_bs);
if (states->ops->abort) {
states->ops->abort(states);
}
}
exit:
QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
if (states->ops->clean) {
states->ops->clean(states);
}
g_free(states);
}
}
......
......@@ -38,6 +38,9 @@ struct Coroutine {
void *entry_arg;
Coroutine *caller;
QSLIST_ENTRY(Coroutine) pool_next;
/* Coroutines that should be woken up when we yield or terminate */
QTAILQ_HEAD(, Coroutine) co_queue_wakeup;
QTAILQ_ENTRY(Coroutine) co_queue_next;
};
......@@ -45,5 +48,6 @@ Coroutine *qemu_coroutine_new(void);
void qemu_coroutine_delete(Coroutine *co);
CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to,
CoroutineAction action);
void coroutine_fn qemu_co_queue_run_restart(Coroutine *co);
#endif
......@@ -1609,12 +1609,12 @@
'*mode': 'NewImageMode' } }
##
# @BlockdevAction
# @TransactionAction
#
# A discriminated record of operations that can be performed with
# @transaction.
##
{ 'union': 'BlockdevAction',
{ 'union': 'TransactionAction',
'data': {
'blockdev-snapshot-sync': 'BlockdevSnapshot'
} }
......@@ -1622,25 +1622,24 @@
##
# @transaction
#
# Atomically operate on a group of one or more block devices. If
# any operation fails, then the entire set of actions will be
# abandoned and the appropriate error returned. The only operation
# supported is currently blockdev-snapshot-sync.
# Executes a number of transactionable QMP commands atomically. If any
# operation fails, then the entire set of actions will be abandoned and the
# appropriate error returned.
#
# List of:
# @BlockdevAction: information needed for the device snapshot
# @TransactionAction: information needed for the respective operation
#
# Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound
# Errors depend on the operations of the transaction
#
# Note: The transaction aborts on the first failure. Therefore, there will
# be only one device or snapshot file returned in an error condition, and
# Note: The transaction aborts on the first failure. Therefore, there will be
# information on only one failed operation returned in an error condition, and
# subsequent actions will not have been attempted.
#
# Since 1.1
##
{ 'command': 'transaction',
'data': { 'actions': [ 'BlockdevAction' ] } }
'data': { 'actions': [ 'TransactionAction' ] } }
##
# @blockdev-snapshot-sync
......
......@@ -26,39 +26,11 @@
#include "block/coroutine.h"
#include "block/coroutine_int.h"
#include "qemu/queue.h"
#include "block/aio.h"
#include "trace.h"
/* Coroutines are awoken from a BH to allow the current coroutine to complete
* its flow of execution. The BH may run after the CoQueue has been destroyed,
* so keep BH data in a separate heap-allocated struct.
*/
typedef struct {
QEMUBH *bh;
QTAILQ_HEAD(, Coroutine) entries;
} CoQueueNextData;
static void qemu_co_queue_next_bh(void *opaque)
{
CoQueueNextData *data = opaque;
Coroutine *next;
trace_qemu_co_queue_next_bh();
while ((next = QTAILQ_FIRST(&data->entries))) {
QTAILQ_REMOVE(&data->entries, next, co_queue_next);
qemu_coroutine_enter(next, NULL);
}
qemu_bh_delete(data->bh);
g_slice_free(CoQueueNextData, data);
}
void qemu_co_queue_init(CoQueue *queue)
{
QTAILQ_INIT(&queue->entries);
/* This will be exposed to callers once there are multiple AioContexts */
queue->ctx = qemu_get_aio_context();
}
void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
......@@ -77,23 +49,37 @@ void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue)
assert(qemu_in_coroutine());
}
/**
* qemu_co_queue_run_restart:
*
* Enter each coroutine that was previously marked for restart by
* qemu_co_queue_next() or qemu_co_queue_restart_all(). This function is
* invoked by the core coroutine code when the current coroutine yields or
* terminates.
*/
void qemu_co_queue_run_restart(Coroutine *co)
{
Coroutine *next;
trace_qemu_co_queue_run_restart(co);
while ((next = QTAILQ_FIRST(&co->co_queue_wakeup))) {
QTAILQ_REMOVE(&co->co_queue_wakeup, next, co_queue_next);
qemu_coroutine_enter(next, NULL);
}
}
static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
{
Coroutine *self = qemu_coroutine_self();
Coroutine *next;
CoQueueNextData *data;
if (QTAILQ_EMPTY(&queue->entries)) {
return false;
}
data = g_slice_new(CoQueueNextData);
data->bh = aio_bh_new(queue->ctx, qemu_co_queue_next_bh, data);
QTAILQ_INIT(&data->entries);
qemu_bh_schedule(data->bh);
while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) {
QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
QTAILQ_INSERT_TAIL(&data->entries, next, co_queue_next);
QTAILQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next);
trace_qemu_co_queue_next(next);
if (single) {
break;
......
......@@ -14,6 +14,7 @@
#include "trace.h"
#include "qemu-common.h"
#include "qemu/thread.h"
#include "block/coroutine.h"
#include "block/coroutine_int.h"
......@@ -23,6 +24,7 @@ enum {
};
/** Free list to speed up creation */
static QemuMutex pool_lock;
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
static unsigned int pool_size;
......@@ -30,31 +32,44 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
{
Coroutine *co;
qemu_mutex_lock(&pool_lock);
co = QSLIST_FIRST(&pool);
if (co) {
QSLIST_REMOVE_HEAD(&pool, pool_next);
pool_size--;
} else {
}
qemu_mutex_unlock(&pool_lock);
if (!co) {
co = qemu_coroutine_new();
}
co->entry = entry;
QTAILQ_INIT(&co->co_queue_wakeup);
return co;
}
static void coroutine_delete(Coroutine *co)
{
qemu_mutex_lock(&pool_lock);
if (pool_size < POOL_MAX_SIZE) {
QSLIST_INSERT_HEAD(&pool, co, pool_next);
co->caller = NULL;
pool_size++;
qemu_mutex_unlock(&pool_lock);
return;
}
qemu_mutex_unlock(&pool_lock);
qemu_coroutine_delete(co);
}
static void __attribute__((destructor)) coroutine_cleanup(void)
static void __attribute__((constructor)) coroutine_pool_init(void)
{
qemu_mutex_init(&pool_lock);
}
static void __attribute__((destructor)) coroutine_pool_cleanup(void)
{
Coroutine *co;
Coroutine *tmp;
......@@ -63,6 +78,8 @@ static void __attribute__((destructor)) coroutine_cleanup(void)
QSLIST_REMOVE_HEAD(&pool, pool_next);
qemu_coroutine_delete(co);
}
qemu_mutex_destroy(&pool_lock);
}
static void coroutine_swap(Coroutine *from, Coroutine *to)
......@@ -71,6 +88,8 @@ static void coroutine_swap(Coroutine *from, Coroutine *to)
ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);
qemu_co_queue_run_restart(to);
switch (ret) {
case COROUTINE_YIELD:
return;
......
......@@ -1635,12 +1635,43 @@ static const cmdinfo_t alloc_cmd = {
.oneline = "checks if a sector is present in the file",
};
static int map_is_allocated(int64_t sector_num, int64_t nb_sectors, int64_t *pnum)
{
int num, num_checked;
int ret, firstret;
num_checked = MIN(nb_sectors, INT_MAX);
ret = bdrv_is_allocated(bs, sector_num, num_checked, &num);
if (ret < 0) {
return ret;
}
firstret = ret;
*pnum = num;
while (nb_sectors > 0 && ret == firstret) {
sector_num += num;
nb_sectors -= num;
num_checked = MIN(nb_sectors, INT_MAX);
ret = bdrv_is_allocated(bs, sector_num, num_checked, &num);
if (ret == firstret) {
*pnum += num;
} else {
break;
}
}
return firstret;
}
static int map_f(int argc, char **argv)
{
int64_t offset;
int64_t nb_sectors;
char s1[64];
int num, num_checked;
int64_t num;
int ret;
const char *retstr;
......@@ -1648,12 +1679,17 @@ static int map_f(int argc, char **argv)
nb_sectors = bs->total_sectors;
do {
num_checked = MIN(nb_sectors, INT_MAX);
ret = bdrv_is_allocated(bs, offset, num_checked, &num);
ret = map_is_allocated(offset, nb_sectors, &num);
if (ret < 0) {
error_report("Failed to get allocation status: %s", strerror(-ret));
return 0;
}
retstr = ret ? " allocated" : "not allocated";
cvtstr(offset << 9ULL, s1, sizeof(s1));
printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n",
offset << 9ULL, num, num_checked, retstr, s1, ret);
printf("[% 24" PRId64 "] % 8" PRId64 "/% 8" PRId64 " sectors %s "
"at offset %s (%d)\n",
offset << 9ULL, num, nb_sectors, retstr, s1, ret);
offset += num;
nb_sectors -= num;
......
#!/bin/bash
#
# Test huge qcow2 images
#
# Copyright (C) 2013 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=kwolf@redhat.com
seq=`basename $0`
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto generic
_supported_os Linux
echo
echo "creating too large image (1 EB)"
_make_test_img $((1024*1024))T
echo
echo "creating too large image (1 EB) using qcow2.py"
_make_test_img 4G
./qcow2.py $TEST_IMG set-header size $((1024 ** 6))
_check_test_img
# success, all done
echo "*** done"
rm -f $seq.full
status=0
QA output created by 054
creating too large image (1 EB)
qemu-img: The image size is too large for file format 'qcow2'
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976
creating too large image (1 EB) using qcow2.py
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296
qemu-img: Could not open 'TEST_DIR/t.qcow2': File too large
*** done
......@@ -167,7 +167,7 @@ _cleanup_test_img()
_check_test_img()
{
$QEMU_IMG check "$@" -f $IMGFMT $TEST_IMG 2>&1 | \
$QEMU_IMG check "$@" -f $IMGFMT $TEST_IMG 2>&1 | _filter_testdir | \
sed -e '/allocated.*fragmented.*compressed clusters/d' \
-e 's/qemu-img: This image format does not support checks/No errors were found on the image./' \
-e '/Image end offset: [0-9]\+/d'
......
......@@ -60,3 +60,4 @@
#051 rw auto
052 rw auto backing
053 rw auto
054 rw auto
......@@ -149,6 +149,22 @@ def cmd_dump_header(fd):
h.dump()
h.dump_extensions()
def cmd_set_header(fd, name, value):
try:
value = int(value, 0)
except:
print "'%s' is not a valid number" % value
sys.exit(1)
fields = (field[2] for field in QcowHeader.fields)
if not name in fields:
print "'%s' is not a known header field" % name
sys.exit(1)
h = QcowHeader(fd)
h.__dict__[name] = value
h.update(fd)
def cmd_add_header_ext(fd, magic, data):
try:
magic = int(magic, 0)
......@@ -205,6 +221,7 @@ def cmd_set_feature_bit(fd, group, bit):
cmds = [
[ 'dump-header', cmd_dump_header, 0, 'Dump image header and header extensions' ],
[ 'set-header', cmd_set_header, 2, 'Set a field in the header'],
[ 'add-header-ext', cmd_add_header_ext, 2, 'Add a header extension' ],
[ 'del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension' ],
[ 'set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'],
......
......@@ -825,7 +825,7 @@ qemu_coroutine_yield(void *from, void *to) "from %p to %p"
qemu_coroutine_terminate(void *co) "self %p"
# qemu-coroutine-lock.c
qemu_co_queue_next_bh(void) ""
qemu_co_queue_run_restart(void *co) "co %p"
qemu_co_queue_next(void *nxt) "next %p"
qemu_co_mutex_lock_entry(void *mutex, void *self) "mutex %p self %p"
qemu_co_mutex_lock_return(void *mutex, void *self) "mutex %p self %p"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册