summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS7
-rw-r--r--block.c69
-rw-r--r--block/Makefile.objs3
-rw-r--r--block/backup.c21
-rw-r--r--block/blkdebug.c2
-rw-r--r--block/commit.c70
-rw-r--r--block/curl.c9
-rw-r--r--block/iscsi.c91
-rw-r--r--block/mirror.c119
-rw-r--r--block/parallels.c2
-rw-r--r--block/qcow2-cluster.c144
-rw-r--r--block/qcow2-refcount.c26
-rw-r--r--block/qcow2-snapshot.c2
-rw-r--r--block/qcow2.c202
-rw-r--r--block/qcow2.h7
-rw-r--r--block/raw-posix.c42
-rw-r--r--block/rbd.c15
-rw-r--r--block/snapshot.c4
-rw-r--r--block/stream.c50
-rw-r--r--block/vdi.c2
-rw-r--r--blockdev.c176
-rw-r--r--blockjob.c88
-rwxr-xr-xconfigure1
-rw-r--r--dump.c11
-rw-r--r--hw/9pfs/virtio-9p-proxy.c11
-rw-r--r--hw/arm/boot.c2
-rw-r--r--hw/block/dataplane/virtio-blk.c5
-rw-r--r--hw/core/loader.c17
-rw-r--r--hw/i386/kvmvapic.c1
-rw-r--r--hw/i386/pc.c19
-rw-r--r--hw/i386/pc_piix.c4
-rw-r--r--hw/i386/pc_q35.c3
-rw-r--r--hw/ide/ahci.c107
-rw-r--r--hw/ide/ahci.h8
-rw-r--r--hw/ide/core.c11
-rw-r--r--hw/ide/internal.h2
-rw-r--r--hw/m68k/an5206.c3
-rw-r--r--hw/m68k/dummy_m68k.c3
-rw-r--r--hw/m68k/mcf5208.c3
-rw-r--r--hw/microblaze/boot.c3
-rw-r--r--hw/misc/ivshmem.c118
-rw-r--r--hw/misc/vfio.c1
-rw-r--r--hw/openrisc/openrisc_sim.c2
-rw-r--r--hw/ppc/e500.c3
-rw-r--r--hw/ppc/ppc440_bamboo.c3
-rw-r--r--hw/scsi/megasas.c574
-rw-r--r--hw/scsi/mfi.h16
-rw-r--r--hw/scsi/scsi-bus.c71
-rw-r--r--hw/scsi/scsi-disk.c18
-rw-r--r--hw/scsi/scsi-generic.c7
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c3
-rw-r--r--hw/scsi/virtio-scsi.c8
-rw-r--r--hw/tricore/tricore_testboard.c2
-rw-r--r--hw/xtensa/pic_cpu.c4
-rw-r--r--hw/xtensa/xtfpga.c3
-rw-r--r--include/block/block.h11
-rw-r--r--include/block/block_int.h6
-rw-r--r--include/block/blockjob.h39
-rw-r--r--include/exec/memory.h19
-rw-r--r--include/hw/i386/pc.h2
-rw-r--r--include/hw/loader.h4
-rw-r--r--include/hw/pci/pci_ids.h1
-rw-r--r--include/hw/scsi/scsi.h7
-rw-r--r--include/qemu-common.h1
-rw-r--r--include/qemu/bitmap.h6
-rw-r--r--include/qemu/bitops.h4
-rw-r--r--include/qemu/osdep.h6
-rw-r--r--memory.c10
-rw-r--r--memory_mapping.c3
-rw-r--r--net/slirp.c4
-rw-r--r--net/tap.c4
-rw-r--r--net/tap_int.h3
-rw-r--r--os-posix.c66
-rw-r--r--os-win32.c5
-rw-r--r--qapi/block-core.json4
-rw-r--r--qemu-img-cmds.hx8
-rw-r--r--qemu-img.c176
-rw-r--r--qemu-img.texi18
-rw-r--r--qemu-options.hx3
-rw-r--r--rules.mak5
-rw-r--r--savevm.c3
-rwxr-xr-xscripts/kvm/kvm_stat150
-rw-r--r--target-arm/translate-a64.c1
-rw-r--r--target-i386/cpu.c8
-rw-r--r--target-i386/translate.c3
-rw-r--r--target-xtensa/core-dc232b.c2
-rw-r--r--target-xtensa/core-dc233c.c2
-rw-r--r--target-xtensa/core-fsf.c2
-rw-r--r--target-xtensa/cpu.h1
-rwxr-xr-xtarget-xtensa/import_core.sh53
-rw-r--r--target-xtensa/overlay_tool.h32
-rwxr-xr-xtests/qemu-iotests/0404
-rwxr-xr-xtests/qemu-iotests/0415
-rwxr-xr-xtests/qemu-iotests/06125
-rw-r--r--tests/qemu-iotests/061.out30
-rwxr-xr-xtests/qemu-iotests/07615
-rw-r--r--tests/qemu-iotests/076.out12
-rwxr-xr-xtests/qemu-iotests/097122
-rw-r--r--tests/qemu-iotests/097.out119
-rwxr-xr-xtests/qemu-iotests/09882
-rw-r--r--tests/qemu-iotests/098.out52
-rwxr-xr-xtests/qemu-iotests/10221
-rw-r--r--tests/qemu-iotests/102.out11
-rwxr-xr-xtests/qemu-iotests/1072
-rw-r--r--tests/qemu-iotests/common.filter7
-rw-r--r--tests/qemu-iotests/group4
-rw-r--r--tests/qemu-iotests/iotests.py3
-rw-r--r--tests/qemu-iotests/sample_images/fake.parallels.bz2bin141 -> 0 bytes
-rw-r--r--tests/qemu-iotests/sample_images/parallels-v1.bz2bin0 -> 147 bytes
-rw-r--r--tests/qemu-iotests/sample_images/parallels-v2.bz2bin0 -> 150 bytes
-rw-r--r--tests/tcg/xtensa/Makefile10
-rw-r--r--tests/tcg/xtensa/linker.ld112
-rw-r--r--tests/tcg/xtensa/linker.ld.S130
-rw-r--r--tests/test-bitops.c1
-rw-r--r--trace-events54
-rw-r--r--ui/Makefile.objs5
-rw-r--r--util/oslib-posix.c3
-rw-r--r--vl.c11
118 files changed, 2771 insertions, 939 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 94366ef..dd02d96 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -583,12 +583,13 @@ Xtensa Machines
sim
M: Max Filippov <jcmvbkbc@gmail.com>
S: Maintained
-F: hw/xtensa/xtensa_sim.c
+F: hw/xtensa/sim.c
-Avnet LX60
+XTFPGA (LX60, LX200, ML605, KC705)
M: Max Filippov <jcmvbkbc@gmail.com>
S: Maintained
-F: hw/xtensa/xtensa_lx60.c
+F: hw/xtensa/xtfpga.c
+F: hw/net/opencores_eth.c
Devices
-------
diff --git a/block.c b/block.c
index 88f6d9b..dacd881 100644
--- a/block.c
+++ b/block.c
@@ -519,6 +519,7 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
return;
}
bs->bl.opt_transfer_length = bs->file->bl.opt_transfer_length;
+ bs->bl.max_transfer_length = bs->file->bl.max_transfer_length;
bs->bl.opt_mem_alignment = bs->file->bl.opt_mem_alignment;
} else {
bs->bl.opt_mem_alignment = 512;
@@ -533,6 +534,9 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
bs->bl.opt_transfer_length =
MAX(bs->bl.opt_transfer_length,
bs->backing_hd->bl.opt_transfer_length);
+ bs->bl.max_transfer_length =
+ MIN_NON_ZERO(bs->bl.max_transfer_length,
+ bs->backing_hd->bl.max_transfer_length);
bs->bl.opt_mem_alignment =
MAX(bs->bl.opt_mem_alignment,
bs->backing_hd->bl.opt_mem_alignment);
@@ -1900,6 +1904,34 @@ static bool bdrv_requests_pending(BlockDriverState *bs)
return false;
}
+static bool bdrv_drain_one(BlockDriverState *bs)
+{
+ bool bs_busy;
+
+ bdrv_flush_io_queue(bs);
+ bdrv_start_throttled_reqs(bs);
+ bs_busy = bdrv_requests_pending(bs);
+ bs_busy |= aio_poll(bdrv_get_aio_context(bs), bs_busy);
+ return bs_busy;
+}
+
+/*
+ * Wait for pending requests to complete on a single BlockDriverState subtree
+ *
+ * See the warning in bdrv_drain_all(). This function can only be called if
+ * you are sure nothing can generate I/O because you have op blockers
+ * installed.
+ *
+ * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState
+ * AioContext.
+ */
+void bdrv_drain(BlockDriverState *bs)
+{
+ while (bdrv_drain_one(bs)) {
+ /* Keep iterating */
+ }
+}
+
/*
* Wait for pending requests to complete across all BlockDriverStates
*
@@ -1923,16 +1955,10 @@ void bdrv_drain_all(void)
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
AioContext *aio_context = bdrv_get_aio_context(bs);
- bool bs_busy;
aio_context_acquire(aio_context);
- bdrv_flush_io_queue(bs);
- bdrv_start_throttled_reqs(bs);
- bs_busy = bdrv_requests_pending(bs);
- bs_busy |= aio_poll(aio_context, bs_busy);
+ busy |= bdrv_drain_one(bs);
aio_context_release(aio_context);
-
- busy |= bs_busy;
}
}
}
@@ -3540,10 +3566,10 @@ static void send_qmp_error_event(BlockDriverState *bs,
BlockErrorAction action,
bool is_read, int error)
{
- BlockErrorAction ac;
+ IoOperationType optype;
- ac = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
- qapi_event_send_block_io_error(bdrv_get_device_name(bs), ac, action,
+ optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
+ qapi_event_send_block_io_error(bdrv_get_device_name(bs), optype, action,
bdrv_iostatus_is_enabled(bs),
error == ENOSPC, strerror(error),
&error_abort);
@@ -4442,6 +4468,11 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
merge = 0;
}
+ if (bs->bl.max_transfer_length && reqs[outidx].nb_sectors +
+ reqs[i].nb_sectors > bs->bl.max_transfer_length) {
+ merge = 0;
+ }
+
if (merge) {
size_t size;
QEMUIOVector *qiov = g_malloc0(sizeof(*qiov));
@@ -5750,12 +5781,13 @@ void bdrv_add_before_write_notifier(BlockDriverState *bs,
notifier_with_return_list_add(&bs->before_write_notifiers, notifier);
}
-int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts)
+int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
+ BlockDriverAmendStatusCB *status_cb)
{
if (!bs->drv->bdrv_amend_options) {
return -ENOTSUP;
}
- return bs->drv->bdrv_amend_options(bs, opts);
+ return bs->drv->bdrv_amend_options(bs, opts, status_cb);
}
/* This function will be called by the bdrv_recurse_is_first_non_filter method
@@ -5818,13 +5850,19 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate)
BlockDriverState *check_to_replace_node(const char *node_name, Error **errp)
{
BlockDriverState *to_replace_bs = bdrv_find_node(node_name);
+ AioContext *aio_context;
+
if (!to_replace_bs) {
error_setg(errp, "Node name '%s' not found", node_name);
return NULL;
}
+ aio_context = bdrv_get_aio_context(to_replace_bs);
+ aio_context_acquire(aio_context);
+
if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) {
- return NULL;
+ to_replace_bs = NULL;
+ goto out;
}
/* We don't want arbitrary node of the BDS chain to be replaced only the top
@@ -5834,9 +5872,12 @@ BlockDriverState *check_to_replace_node(const char *node_name, Error **errp)
*/
if (!bdrv_is_first_non_filter(to_replace_bs)) {
error_setg(errp, "Only top most non filter can be replaced");
- return NULL;
+ to_replace_bs = NULL;
+ goto out;
}
+out:
+ aio_context_release(aio_context);
return to_replace_bs;
}
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 27911b6..04b0e43 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -9,7 +9,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
block-obj-$(CONFIG_POSIX) += raw-posix.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
-block-obj-y += null.o
+block-obj-y += null.o mirror.o
block-obj-y += nbd.o nbd-client.o sheepdog.o
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
@@ -23,7 +23,6 @@ block-obj-y += accounting.o
common-obj-y += stream.o
common-obj-y += commit.o
-common-obj-y += mirror.o
common-obj-y += backup.o
iscsi.o-cflags := $(LIBISCSI_CFLAGS)
diff --git a/block/backup.c b/block/backup.c
index e334740..792e655 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -227,9 +227,25 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
}
}
+typedef struct {
+ int ret;
+} BackupCompleteData;
+
+static void backup_complete(BlockJob *job, void *opaque)
+{
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+ BackupCompleteData *data = opaque;
+
+ bdrv_unref(s->target);
+
+ block_job_completed(job, data->ret);
+ g_free(data);
+}
+
static void coroutine_fn backup_run(void *opaque)
{
BackupBlockJob *job = opaque;
+ BackupCompleteData *data;
BlockDriverState *bs = job->common.bs;
BlockDriverState *target = job->target;
BlockdevOnError on_target_error = job->on_target_error;
@@ -344,9 +360,10 @@ static void coroutine_fn backup_run(void *opaque)
hbitmap_free(job->bitmap);
bdrv_iostatus_disable(target);
- bdrv_unref(target);
- block_job_completed(&job->common, ret);
+ data = g_malloc(sizeof(*data));
+ data->ret = ret;
+ block_job_defer_to_main_loop(&job->common, backup_complete, data);
}
void backup_start(BlockDriverState *bs, BlockDriverState *target,
diff --git a/block/blkdebug.c b/block/blkdebug.c
index e046b92..862d93b 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -195,6 +195,8 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
[BLKDBG_PWRITEV] = "pwritev",
[BLKDBG_PWRITEV_ZERO] = "pwritev_zero",
[BLKDBG_PWRITEV_DONE] = "pwritev_done",
+
+ [BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare",
};
static int get_event_by_name(const char *name, BlkDebugEvent *event)
diff --git a/block/commit.c b/block/commit.c
index 60a2acc..cfa2bbe 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -60,17 +60,50 @@ static int coroutine_fn commit_populate(BlockDriverState *bs,
return 0;
}
-static void coroutine_fn commit_run(void *opaque)
+typedef struct {
+ int ret;
+} CommitCompleteData;
+
+static void commit_complete(BlockJob *job, void *opaque)
{
- CommitBlockJob *s = opaque;
+ CommitBlockJob *s = container_of(job, CommitBlockJob, common);
+ CommitCompleteData *data = opaque;
BlockDriverState *active = s->active;
BlockDriverState *top = s->top;
BlockDriverState *base = s->base;
BlockDriverState *overlay_bs;
+ int ret = data->ret;
+
+ if (!block_job_is_cancelled(&s->common) && ret == 0) {
+ /* success */
+ ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
+ }
+
+ /* restore base open flags here if appropriate (e.g., change the base back
+ * to r/o). These reopens do not need to be atomic, since we won't abort
+ * even on failure here */
+ if (s->base_flags != bdrv_get_flags(base)) {
+ bdrv_reopen(base, s->base_flags, NULL);
+ }
+ overlay_bs = bdrv_find_overlay(active, top);
+ if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
+ bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
+ }
+ g_free(s->backing_file_str);
+ block_job_completed(&s->common, ret);
+ g_free(data);
+}
+
+static void coroutine_fn commit_run(void *opaque)
+{
+ CommitBlockJob *s = opaque;
+ CommitCompleteData *data;
+ BlockDriverState *top = s->top;
+ BlockDriverState *base = s->base;
int64_t sector_num, end;
int ret = 0;
int n = 0;
- void *buf;
+ void *buf = NULL;
int bytes_written = 0;
int64_t base_len;
@@ -78,18 +111,18 @@ static void coroutine_fn commit_run(void *opaque)
if (s->common.len < 0) {
- goto exit_restore_reopen;
+ goto out;
}
ret = base_len = bdrv_getlength(base);
if (base_len < 0) {
- goto exit_restore_reopen;
+ goto out;
}
if (base_len < s->common.len) {
ret = bdrv_truncate(base, s->common.len);
if (ret) {
- goto exit_restore_reopen;
+ goto out;
}
}
@@ -128,7 +161,7 @@ wait:
if (s->on_error == BLOCKDEV_ON_ERROR_STOP ||
s->on_error == BLOCKDEV_ON_ERROR_REPORT||
(s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) {
- goto exit_free_buf;
+ goto out;
} else {
n = 0;
continue;
@@ -140,27 +173,12 @@ wait:
ret = 0;
- if (!block_job_is_cancelled(&s->common) && sector_num == end) {
- /* success */
- ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
- }
-
-exit_free_buf:
+out:
qemu_vfree(buf);
-exit_restore_reopen:
- /* restore base open flags here if appropriate (e.g., change the base back
- * to r/o). These reopens do not need to be atomic, since we won't abort
- * even on failure here */
- if (s->base_flags != bdrv_get_flags(base)) {
- bdrv_reopen(base, s->base_flags, NULL);
- }
- overlay_bs = bdrv_find_overlay(active, top);
- if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
- bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
- }
- g_free(s->backing_file_str);
- block_job_completed(&s->common, ret);
+ data = g_malloc(sizeof(*data));
+ data->ret = ret;
+ block_job_defer_to_main_loop(&s->common, commit_complete, data);
}
static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
diff --git a/block/curl.c b/block/curl.c
index b4157cc..bbee3ca 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -64,6 +64,7 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
#define SECTOR_SIZE 512
#define READ_AHEAD_DEFAULT (256 * 1024)
#define CURL_TIMEOUT_DEFAULT 5
+#define CURL_TIMEOUT_MAX 10000
#define FIND_RET_NONE 0
#define FIND_RET_OK 1
@@ -112,7 +113,7 @@ typedef struct BDRVCURLState {
char *url;
size_t readahead_size;
bool sslverify;
- int timeout;
+ uint64_t timeout;
char *cookie;
bool accept_range;
AioContext *aio_context;
@@ -390,7 +391,7 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
if (s->cookie) {
curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
}
- curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, s->timeout);
+ curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout);
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
(void *)curl_read_cb);
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
@@ -546,6 +547,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT,
CURL_TIMEOUT_DEFAULT);
+ if (s->timeout > CURL_TIMEOUT_MAX) {
+ error_setg(errp, "timeout parameter is too large or negative");
+ goto out_noclean;
+ }
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
diff --git a/block/iscsi.c b/block/iscsi.c
index 233f462..ed375fc 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -362,6 +362,12 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs,
return -EINVAL;
}
+ if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) {
+ error_report("iSCSI Error: Write of %d sectors exceeds max_xfer_len "
+ "of %d sectors", nb_sectors, bs->bl.max_transfer_length);
+ return -EINVAL;
+ }
+
lba = sector_qemu2lun(sector_num, iscsilun);
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
iscsi_co_init_iscsitask(iscsilun, &iTask);
@@ -529,6 +535,12 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
return -EINVAL;
}
+ if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) {
+ error_report("iSCSI Error: Read of %d sectors exceeds max_xfer_len "
+ "of %d sectors", nb_sectors, bs->bl.max_transfer_length);
+ return -EINVAL;
+ }
+
if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES &&
!iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
int64_t ret;
@@ -1219,6 +1231,40 @@ static void iscsi_attach_aio_context(BlockDriverState *bs,
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
}
+static bool iscsi_is_write_protected(IscsiLun *iscsilun)
+{
+ struct scsi_task *task;
+ struct scsi_mode_sense *ms = NULL;
+ bool wrprotected = false;
+
+ task = iscsi_modesense6_sync(iscsilun->iscsi, iscsilun->lun,
+ 1, SCSI_MODESENSE_PC_CURRENT,
+ 0x3F, 0, 255);
+ if (task == NULL) {
+ error_report("iSCSI: Failed to send MODE_SENSE(6) command: %s",
+ iscsi_get_error(iscsilun->iscsi));
+ goto out;
+ }
+
+ if (task->status != SCSI_STATUS_GOOD) {
+ error_report("iSCSI: Failed MODE_SENSE(6), LUN assumed writable");
+ goto out;
+ }
+ ms = scsi_datain_unmarshall(task);
+ if (!ms) {
+ error_report("iSCSI: Failed to unmarshall MODE_SENSE(6) data: %s",
+ iscsi_get_error(iscsilun->iscsi));
+ goto out;
+ }
+ wrprotected = ms->device_specific_parameter & 0x80;
+
+out:
+ if (task) {
+ scsi_free_scsi_task(task);
+ }
+ return wrprotected;
+}
+
/*
* We support iscsi url's on the form
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
@@ -1339,6 +1385,14 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
scsi_free_scsi_task(task);
task = NULL;
+ /* Check the write protect flag of the LUN if we want to write */
+ if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
+ iscsi_is_write_protected(iscsilun)) {
+ error_setg(errp, "Cannot open a write protected LUN as read-write");
+ ret = -EACCES;
+ goto out;
+ }
+
iscsi_readcapacity_sync(iscsilun, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -1447,31 +1501,44 @@ static void iscsi_close(BlockDriverState *bs)
memset(iscsilun, 0, sizeof(IscsiLun));
}
-static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp)
+static int sector_limits_lun2qemu(int64_t sector, IscsiLun *iscsilun)
{
- IscsiLun *iscsilun = bs->opaque;
+ return MIN(sector_lun2qemu(sector, iscsilun), INT_MAX / 2 + 1);
+}
+static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp)
+{
/* We don't actually refresh here, but just return data queried in
* iscsi_open(): iscsi targets don't change their limits. */
+
+ IscsiLun *iscsilun = bs->opaque;
+ uint32_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff;
+
+ if (iscsilun->bl.max_xfer_len) {
+ max_xfer_len = MIN(max_xfer_len, iscsilun->bl.max_xfer_len);
+ }
+
+ bs->bl.max_transfer_length = sector_limits_lun2qemu(max_xfer_len, iscsilun);
+
if (iscsilun->lbp.lbpu) {
if (iscsilun->bl.max_unmap < 0xffffffff) {
- bs->bl.max_discard = sector_lun2qemu(iscsilun->bl.max_unmap,
- iscsilun);
+ bs->bl.max_discard =
+ sector_limits_lun2qemu(iscsilun->bl.max_unmap, iscsilun);
}
- bs->bl.discard_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
- iscsilun);
+ bs->bl.discard_alignment =
+ sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun);
}
if (iscsilun->bl.max_ws_len < 0xffffffff) {
- bs->bl.max_write_zeroes = sector_lun2qemu(iscsilun->bl.max_ws_len,
- iscsilun);
+ bs->bl.max_write_zeroes =
+ sector_limits_lun2qemu(iscsilun->bl.max_ws_len, iscsilun);
}
if (iscsilun->lbp.lbpws) {
- bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
- iscsilun);
+ bs->bl.write_zeroes_alignment =
+ sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun);
}
- bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
- iscsilun);
+ bs->bl.opt_transfer_length =
+ sector_limits_lun2qemu(iscsilun->bl.opt_xfer_len, iscsilun);
}
/* Since iscsi_open() ignores bdrv_flags, there is nothing to do here in
diff --git a/block/mirror.c b/block/mirror.c
index e8a43eb..2c6dd2a 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -45,6 +45,7 @@ typedef struct MirrorBlockJob {
int64_t sector_num;
int64_t granularity;
size_t buf_size;
+ int64_t bdev_length;
unsigned long *cow_bitmap;
BdrvDirtyBitmap *dirty_bitmap;
HBitmapIter hbi;
@@ -54,6 +55,7 @@ typedef struct MirrorBlockJob {
unsigned long *in_flight_bitmap;
int in_flight;
+ int sectors_in_flight;
int ret;
} MirrorBlockJob;
@@ -87,6 +89,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
trace_mirror_iteration_done(s, op->sector_num, op->nb_sectors, ret);
s->in_flight--;
+ s->sectors_in_flight -= op->nb_sectors;
iov = op->qiov.iov;
for (i = 0; i < op->qiov.niov; i++) {
MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base;
@@ -98,8 +101,11 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
chunk_num = op->sector_num / sectors_per_chunk;
nb_chunks = op->nb_sectors / sectors_per_chunk;
bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks);
- if (s->cow_bitmap && ret >= 0) {
- bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
+ if (ret >= 0) {
+ if (s->cow_bitmap) {
+ bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
+ }
+ s->common.offset += (uint64_t)op->nb_sectors * BDRV_SECTOR_SIZE;
}
qemu_iovec_destroy(&op->qiov);
@@ -172,7 +178,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
hbitmap_next_sector = s->sector_num;
sector_num = s->sector_num;
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
- end = s->common.len >> BDRV_SECTOR_BITS;
+ end = s->bdev_length / BDRV_SECTOR_SIZE;
/* Extend the QEMUIOVector to include all adjacent blocks that will
* be copied in this operation.
@@ -284,6 +290,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
/* Copy the dirty cluster. */
s->in_flight++;
+ s->sectors_in_flight += nb_sectors;
trace_mirror_one_iteration(s, sector_num, nb_sectors);
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
mirror_read_complete, op);
@@ -314,9 +321,56 @@ static void mirror_drain(MirrorBlockJob *s)
}
}
+typedef struct {
+ int ret;
+} MirrorExitData;
+
+static void mirror_exit(BlockJob *job, void *opaque)
+{
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
+ MirrorExitData *data = opaque;
+ AioContext *replace_aio_context = NULL;
+
+ if (s->to_replace) {
+ replace_aio_context = bdrv_get_aio_context(s->to_replace);
+ aio_context_acquire(replace_aio_context);
+ }
+
+ if (s->should_complete && data->ret == 0) {
+ BlockDriverState *to_replace = s->common.bs;
+ if (s->to_replace) {
+ to_replace = s->to_replace;
+ }
+ if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
+ bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
+ }
+ bdrv_swap(s->target, to_replace);
+ if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) {
+ /* drop the bs loop chain formed by the swap: break the loop then
+ * trigger the unref from the top one */
+ BlockDriverState *p = s->base->backing_hd;
+ bdrv_set_backing_hd(s->base, NULL);
+ bdrv_unref(p);
+ }
+ }
+ if (s->to_replace) {
+ bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
+ error_free(s->replace_blocker);
+ bdrv_unref(s->to_replace);
+ }
+ if (replace_aio_context) {
+ aio_context_release(replace_aio_context);
+ }
+ g_free(s->replaces);
+ bdrv_unref(s->target);
+ block_job_completed(&s->common, data->ret);
+ g_free(data);
+}
+
static void coroutine_fn mirror_run(void *opaque)
{
MirrorBlockJob *s = opaque;
+ MirrorExitData *data;
BlockDriverState *bs = s->common.bs;
int64_t sector_num, end, sectors_per_chunk, length;
uint64_t last_pause_ns;
@@ -329,11 +383,11 @@ static void coroutine_fn mirror_run(void *opaque)
goto immediate_exit;
}
- s->common.len = bdrv_getlength(bs);
- if (s->common.len < 0) {
- ret = s->common.len;
+ s->bdev_length = bdrv_getlength(bs);
+ if (s->bdev_length < 0) {
+ ret = s->bdev_length;
goto immediate_exit;
- } else if (s->common.len == 0) {
+ } else if (s->bdev_length == 0) {
/* Report BLOCK_JOB_READY and wait for complete. */
block_job_event_ready(&s->common);
s->synced = true;
@@ -344,7 +398,7 @@ static void coroutine_fn mirror_run(void *opaque)
goto immediate_exit;
}
- length = DIV_ROUND_UP(s->common.len, s->granularity);
+ length = DIV_ROUND_UP(s->bdev_length, s->granularity);
s->in_flight_bitmap = bitmap_new(length);
/* If we have no backing file yet in the destination, we cannot let
@@ -364,7 +418,7 @@ static void coroutine_fn mirror_run(void *opaque)
}
}
- end = s->common.len >> BDRV_SECTOR_BITS;
+ end = s->bdev_length / BDRV_SECTOR_SIZE;
s->buf = qemu_try_blockalign(bs, s->buf_size);
if (s->buf == NULL) {
ret = -ENOMEM;
@@ -409,6 +463,12 @@ static void coroutine_fn mirror_run(void *opaque)
}
cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap);
+ /* s->common.offset contains the number of bytes already processed so
+ * far, cnt is the number of dirty sectors remaining and
+ * s->sectors_in_flight is the number of sectors currently being
+ * processed; together those are the current total operation length */
+ s->common.len = s->common.offset +
+ (cnt + s->sectors_in_flight) * BDRV_SECTOR_SIZE;
/* Note that even when no rate limit is applied we need to yield
* periodically with no pending I/O so that qemu_aio_flush() returns.
@@ -445,7 +505,6 @@ static void coroutine_fn mirror_run(void *opaque)
* report completion. This way, block-job-cancel will leave
* the target in a consistent state.
*/
- s->common.offset = end * BDRV_SECTOR_SIZE;
if (!s->synced) {
block_job_event_ready(&s->common);
s->synced = true;
@@ -467,15 +526,13 @@ static void coroutine_fn mirror_run(void *opaque)
* mirror_populate runs.
*/
trace_mirror_before_drain(s, cnt);
- bdrv_drain_all();
+ bdrv_drain(bs);
cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap);
}
ret = 0;
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
if (!s->synced) {
- /* Publish progress */
- s->common.offset = (end - cnt) * BDRV_SECTOR_SIZE;
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns);
if (block_job_is_cancelled(&s->common)) {
break;
@@ -510,31 +567,10 @@ immediate_exit:
g_free(s->in_flight_bitmap);
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
bdrv_iostatus_disable(s->target);
- if (s->should_complete && ret == 0) {
- BlockDriverState *to_replace = s->common.bs;
- if (s->to_replace) {
- to_replace = s->to_replace;
- }
- if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
- bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
- }
- bdrv_swap(s->target, to_replace);
- if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) {
- /* drop the bs loop chain formed by the swap: break the loop then
- * trigger the unref from the top one */
- BlockDriverState *p = s->base->backing_hd;
- bdrv_set_backing_hd(s->base, NULL);
- bdrv_unref(p);
- }
- }
- if (s->to_replace) {
- bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
- error_free(s->replace_blocker);
- bdrv_unref(s->to_replace);
- }
- g_free(s->replaces);
- bdrv_unref(s->target);
- block_job_completed(&s->common, ret);
+
+ data = g_malloc(sizeof(*data));
+ data->ret = ret;
+ block_job_defer_to_main_loop(&s->common, mirror_exit, data);
}
static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
@@ -574,16 +610,23 @@ static void mirror_complete(BlockJob *job, Error **errp)
/* check the target bs is not blocked and block all operations on it */
if (s->replaces) {
+ AioContext *replace_aio_context;
+
s->to_replace = check_to_replace_node(s->replaces, &local_err);
if (!s->to_replace) {
error_propagate(errp, local_err);
return;
}
+ replace_aio_context = bdrv_get_aio_context(s->to_replace);
+ aio_context_acquire(replace_aio_context);
+
error_setg(&s->replace_blocker,
"block device is in use by block-job-complete");
bdrv_op_block_all(s->to_replace, s->replace_blocker);
bdrv_ref(s->to_replace);
+
+ aio_context_release(replace_aio_context);
}
s->should_complete = true;
diff --git a/block/parallels.c b/block/parallels.c
index 2a814f3..4f9cd8d 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -155,7 +155,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
offset = sector_num % s->tracks;
/* not allocated */
- if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
+ if ((index >= s->catalog_size) || (s->catalog_bitmap[index] == 0))
return -1;
return
((uint64_t)s->catalog_bitmap[index] * s->off_multiplier + offset) * 512;
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 4d888c7..df0b2c9 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -1414,7 +1414,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
* clusters.
*/
static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
- unsigned int nb_clusters, enum qcow2_discard_type type)
+ unsigned int nb_clusters, enum qcow2_discard_type type, bool full_discard)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l2_table;
@@ -1436,23 +1436,30 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
old_l2_entry = be64_to_cpu(l2_table[l2_index + i]);
/*
- * Make sure that a discarded area reads back as zeroes for v3 images
- * (we cannot do it for v2 without actually writing a zero-filled
- * buffer). We can skip the operation if the cluster is already marked
- * as zero, or if it's unallocated and we don't have a backing file.
+ * If full_discard is false, make sure that a discarded area reads back
+ * as zeroes for v3 images (we cannot do it for v2 without actually
+ * writing a zero-filled buffer). We can skip the operation if the
+ * cluster is already marked as zero, or if it's unallocated and we
+ * don't have a backing file.
*
* TODO We might want to use bdrv_get_block_status(bs) here, but we're
* holding s->lock, so that doesn't work today.
+ *
+ * If full_discard is true, the sector should not read back as zeroes,
+ * but rather fall through to the backing file.
*/
switch (qcow2_get_cluster_type(old_l2_entry)) {
case QCOW2_CLUSTER_UNALLOCATED:
- if (!bs->backing_hd) {
+ if (full_discard || !bs->backing_hd) {
continue;
}
break;
case QCOW2_CLUSTER_ZERO:
- continue;
+ if (!full_discard) {
+ continue;
+ }
+ break;
case QCOW2_CLUSTER_NORMAL:
case QCOW2_CLUSTER_COMPRESSED:
@@ -1464,7 +1471,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
/* First remove L2 entries */
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
- if (s->qcow_version >= 3) {
+ if (!full_discard && s->qcow_version >= 3) {
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
} else {
l2_table[l2_index + i] = cpu_to_be64(0);
@@ -1483,7 +1490,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
}
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
- int nb_sectors, enum qcow2_discard_type type)
+ int nb_sectors, enum qcow2_discard_type type, bool full_discard)
{
BDRVQcowState *s = bs->opaque;
uint64_t end_offset;
@@ -1506,7 +1513,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
/* Each L2 table is handled by its own loop iteration */
while (nb_clusters > 0) {
- ret = discard_single_l2(bs, offset, nb_clusters, type);
+ ret = discard_single_l2(bs, offset, nb_clusters, type, full_discard);
if (ret < 0) {
goto fail;
}
@@ -1606,15 +1613,14 @@ fail:
* Expands all zero clusters in a specific L1 table (or deallocates them, for
* non-backed non-pre-allocated zero clusters).
*
- * expanded_clusters is a bitmap where every bit corresponds to one cluster in
- * the image file; a bit gets set if the corresponding cluster has been used for
- * zero expansion (i.e., has been filled with zeroes and is referenced from an
- * L2 table). nb_clusters contains the total cluster count of the image file,
- * i.e., the number of bits in expanded_clusters.
+ * l1_entries and *visited_l1_entries are used to keep track of progress for
+ * status_cb(). l1_entries contains the total number of L1 entries and
+ * *visited_l1_entries counts all visited L1 entries.
*/
static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
- int l1_size, uint8_t **expanded_clusters,
- uint64_t *nb_clusters)
+ int l1_size, int64_t *visited_l1_entries,
+ int64_t l1_entries,
+ BlockDriverAmendStatusCB *status_cb)
{
BDRVQcowState *s = bs->opaque;
bool is_active_l1 = (l1_table == s->l1_table);
@@ -1634,9 +1640,14 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
for (i = 0; i < l1_size; i++) {
uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK;
bool l2_dirty = false;
+ int l2_refcount;
if (!l2_offset) {
/* unallocated */
+ (*visited_l1_entries)++;
+ if (status_cb) {
+ status_cb(bs, *visited_l1_entries, l1_entries);
+ }
continue;
}
@@ -1653,33 +1664,19 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
goto fail;
}
+ l2_refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits);
+ if (l2_refcount < 0) {
+ ret = l2_refcount;
+ goto fail;
+ }
+
for (j = 0; j < s->l2_size; j++) {
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
- int64_t offset = l2_entry & L2E_OFFSET_MASK, cluster_index;
+ int64_t offset = l2_entry & L2E_OFFSET_MASK;
int cluster_type = qcow2_get_cluster_type(l2_entry);
bool preallocated = offset != 0;
- if (cluster_type == QCOW2_CLUSTER_NORMAL) {
- cluster_index = offset >> s->cluster_bits;
- assert((cluster_index >= 0) && (cluster_index < *nb_clusters));
- if ((*expanded_clusters)[cluster_index / 8] &
- (1 << (cluster_index % 8))) {
- /* Probably a shared L2 table; this cluster was a zero
- * cluster which has been expanded, its refcount
- * therefore most likely requires an update. */
- ret = qcow2_update_cluster_refcount(bs, cluster_index, 1,
- QCOW2_DISCARD_NEVER);
- if (ret < 0) {
- goto fail;
- }
- /* Since we just increased the refcount, the COPIED flag may
- * no longer be set. */
- l2_table[j] = cpu_to_be64(l2_entry & ~QCOW_OFLAG_COPIED);
- l2_dirty = true;
- }
- continue;
- }
- else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) {
+ if (cluster_type != QCOW2_CLUSTER_ZERO) {
continue;
}
@@ -1697,6 +1694,19 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
ret = offset;
goto fail;
}
+
+ if (l2_refcount > 1) {
+ /* For shared L2 tables, set the refcount accordingly (it is
+ * already 1 and needs to be l2_refcount) */
+ ret = qcow2_update_cluster_refcount(bs,
+ offset >> s->cluster_bits, l2_refcount - 1,
+ QCOW2_DISCARD_OTHER);
+ if (ret < 0) {
+ qcow2_free_clusters(bs, offset, s->cluster_size,
+ QCOW2_DISCARD_OTHER);
+ goto fail;
+ }
+ }
}
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
@@ -1718,29 +1728,12 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
goto fail;
}
- l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
- l2_dirty = true;
-
- cluster_index = offset >> s->cluster_bits;
-
- if (cluster_index >= *nb_clusters) {
- uint64_t old_bitmap_size = (*nb_clusters + 7) / 8;
- uint64_t new_bitmap_size;
- /* The offset may lie beyond the old end of the underlying image
- * file for growable files only */
- assert(bs->file->growable);
- *nb_clusters = size_to_clusters(s, bs->file->total_sectors *
- BDRV_SECTOR_SIZE);
- new_bitmap_size = (*nb_clusters + 7) / 8;
- *expanded_clusters = g_realloc(*expanded_clusters,
- new_bitmap_size);
- /* clear the newly allocated space */
- memset(&(*expanded_clusters)[old_bitmap_size], 0,
- new_bitmap_size - old_bitmap_size);
+ if (l2_refcount == 1) {
+ l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
+ } else {
+ l2_table[j] = cpu_to_be64(offset);
}
-
- assert((cluster_index >= 0) && (cluster_index < *nb_clusters));
- (*expanded_clusters)[cluster_index / 8] |= 1 << (cluster_index % 8);
+ l2_dirty = true;
}
if (is_active_l1) {
@@ -1769,6 +1762,11 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
}
}
}
+
+ (*visited_l1_entries)++;
+ if (status_cb) {
+ status_cb(bs, *visited_l1_entries, l1_entries);
+ }
}
ret = 0;
@@ -1795,25 +1793,25 @@ fail:
* allocation for pre-allocated ones). This is important for downgrading to a
* qcow2 version which doesn't yet support metadata zero clusters.
*/
-int qcow2_expand_zero_clusters(BlockDriverState *bs)
+int qcow2_expand_zero_clusters(BlockDriverState *bs,
+ BlockDriverAmendStatusCB *status_cb)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l1_table = NULL;
- uint64_t nb_clusters;
- uint8_t *expanded_clusters;
+ int64_t l1_entries = 0, visited_l1_entries = 0;
int ret;
int i, j;
- nb_clusters = size_to_clusters(s, bs->file->total_sectors *
- BDRV_SECTOR_SIZE);
- expanded_clusters = g_try_malloc0((nb_clusters + 7) / 8);
- if (expanded_clusters == NULL) {
- ret = -ENOMEM;
- goto fail;
+ if (status_cb) {
+ l1_entries = s->l1_size;
+ for (i = 0; i < s->nb_snapshots; i++) {
+ l1_entries += s->snapshots[i].l1_size;
+ }
}
ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size,
- &expanded_clusters, &nb_clusters);
+ &visited_l1_entries, l1_entries,
+ status_cb);
if (ret < 0) {
goto fail;
}
@@ -1847,7 +1845,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs)
}
ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size,
- &expanded_clusters, &nb_clusters);
+ &visited_l1_entries, l1_entries,
+ status_cb);
if (ret < 0) {
goto fail;
}
@@ -1856,7 +1855,6 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs)
ret = 0;
fail:
- g_free(expanded_clusters);
g_free(l1_table);
return ret;
}
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 1477031..9afdb40 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -91,7 +91,7 @@ static int load_refcount_block(BlockDriverState *bs,
* return value is the refcount of the cluster, negative values are -errno
* and indicate an error.
*/
-static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
+int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index)
{
BDRVQcowState *s = bs->opaque;
uint64_t refcount_table_index, block_index;
@@ -629,8 +629,7 @@ fail:
}
/*
- * Increases or decreases the refcount of a given cluster by one.
- * addend must be 1 or -1.
+ * Increases or decreases the refcount of a given cluster.
*
* If the return value is non-negative, it is the new refcount of the cluster.
* If it is negative, it is -errno and indicates an error.
@@ -649,7 +648,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
return ret;
}
- return get_refcount(bs, cluster_index);
+ return qcow2_get_refcount(bs, cluster_index);
}
@@ -670,7 +669,7 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
retry:
for(i = 0; i < nb_clusters; i++) {
uint64_t next_cluster_index = s->free_cluster_index++;
- refcount = get_refcount(bs, next_cluster_index);
+ refcount = qcow2_get_refcount(bs, next_cluster_index);
if (refcount < 0) {
return refcount;
@@ -734,7 +733,7 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
/* Check how many clusters there are free */
cluster_index = offset >> s->cluster_bits;
for(i = 0; i < nb_clusters; i++) {
- refcount = get_refcount(bs, cluster_index++);
+ refcount = qcow2_get_refcount(bs, cluster_index++);
if (refcount < 0) {
return refcount;
@@ -981,7 +980,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
cluster_index, addend,
QCOW2_DISCARD_SNAPSHOT);
} else {
- refcount = get_refcount(bs, cluster_index);
+ refcount = qcow2_get_refcount(bs, cluster_index);
}
if (refcount < 0) {
@@ -1021,7 +1020,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
refcount = qcow2_update_cluster_refcount(bs, l2_offset >>
s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT);
} else {
- refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
+ refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits);
}
if (refcount < 0) {
ret = refcount;
@@ -1332,8 +1331,8 @@ fail:
* Checks the OFLAG_COPIED flag for all L1 and L2 entries.
*
* This function does not print an error message nor does it increment
- * check_errors if get_refcount fails (this is because such an error will have
- * been already detected and sufficiently signaled by the calling function
+ * check_errors if qcow2_get_refcount fails (this is because such an error will
+ * have been already detected and sufficiently signaled by the calling function
* (qcow2_check_refcounts) by the time this function is called).
*/
static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
@@ -1354,7 +1353,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
continue;
}
- refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
+ refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits);
if (refcount < 0) {
/* don't print message nor increment check_errors */
continue;
@@ -1396,7 +1395,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if ((cluster_type == QCOW2_CLUSTER_NORMAL) ||
((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) {
- refcount = get_refcount(bs, data_offset >> s->cluster_bits);
+ refcount = qcow2_get_refcount(bs,
+ data_offset >> s->cluster_bits);
if (refcount < 0) {
/* don't print message nor increment check_errors */
continue;
@@ -1632,7 +1632,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
int refcount1, refcount2, ret;
for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) {
- refcount1 = get_refcount(bs, i);
+ refcount1 = qcow2_get_refcount(bs, i);
if (refcount1 < 0) {
fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n",
i, strerror(-refcount1));
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index f52d7fd..5b3903c 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -441,7 +441,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
align_offset(sn->vm_state_size, s->cluster_size)
>> BDRV_SECTOR_BITS,
- QCOW2_DISCARD_NEVER);
+ QCOW2_DISCARD_NEVER, false);
#ifdef DEBUG_ALLOC
{
diff --git a/block/qcow2.c b/block/qcow2.c
index d031515..d120494 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2089,7 +2089,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
qemu_co_mutex_lock(&s->lock);
ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
- nb_sectors, QCOW2_DISCARD_REQUEST);
+ nb_sectors, QCOW2_DISCARD_REQUEST, false);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
@@ -2230,6 +2230,195 @@ fail:
return ret;
}
+static int make_completely_empty(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, l1_clusters;
+ int64_t offset;
+ uint64_t *new_reftable = NULL;
+ uint64_t rt_entry, l1_size2;
+ struct {
+ uint64_t l1_offset;
+ uint64_t reftable_offset;
+ uint32_t reftable_clusters;
+ } QEMU_PACKED l1_ofs_rt_ofs_cls;
+
+ ret = qcow2_cache_empty(bs, s->l2_table_cache);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = qcow2_cache_empty(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ /* Refcounts will be broken utterly */
+ ret = qcow2_mark_dirty(bs);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
+
+ l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t));
+ l1_size2 = (uint64_t)s->l1_size * sizeof(uint64_t);
+
+ /* After this call, neither the in-memory nor the on-disk refcount
+ * information accurately describe the actual references */
+
+ ret = bdrv_write_zeroes(bs->file, s->l1_table_offset / BDRV_SECTOR_SIZE,
+ l1_clusters * s->cluster_sectors, 0);
+ if (ret < 0) {
+ goto fail_broken_refcounts;
+ }
+ memset(s->l1_table, 0, l1_size2);
+
+ BLKDBG_EVENT(bs->file, BLKDBG_EMPTY_IMAGE_PREPARE);
+
+ /* Overwrite enough clusters at the beginning of the sectors to place
+ * the refcount table, a refcount block and the L1 table in; this may
+ * overwrite parts of the existing refcount and L1 table, which is not
+ * an issue because the dirty flag is set, complete data loss is in fact
+ * desired and partial data loss is consequently fine as well */
+ ret = bdrv_write_zeroes(bs->file, s->cluster_size / BDRV_SECTOR_SIZE,
+ (2 + l1_clusters) * s->cluster_size /
+ BDRV_SECTOR_SIZE, 0);
+ /* This call (even if it failed overall) may have overwritten on-disk
+ * refcount structures; in that case, the in-memory refcount information
+ * will probably differ from the on-disk information which makes the BDS
+ * unusable */
+ if (ret < 0) {
+ goto fail_broken_refcounts;
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
+ BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_UPDATE);
+
+ /* "Create" an empty reftable (one cluster) directly after the image
+ * header and an empty L1 table three clusters after the image header;
+ * the cluster between those two will be used as the first refblock */
+ cpu_to_be64w(&l1_ofs_rt_ofs_cls.l1_offset, 3 * s->cluster_size);
+ cpu_to_be64w(&l1_ofs_rt_ofs_cls.reftable_offset, s->cluster_size);
+ cpu_to_be32w(&l1_ofs_rt_ofs_cls.reftable_clusters, 1);
+ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset),
+ &l1_ofs_rt_ofs_cls, sizeof(l1_ofs_rt_ofs_cls));
+ if (ret < 0) {
+ goto fail_broken_refcounts;
+ }
+
+ s->l1_table_offset = 3 * s->cluster_size;
+
+ new_reftable = g_try_new0(uint64_t, s->cluster_size / sizeof(uint64_t));
+ if (!new_reftable) {
+ ret = -ENOMEM;
+ goto fail_broken_refcounts;
+ }
+
+ s->refcount_table_offset = s->cluster_size;
+ s->refcount_table_size = s->cluster_size / sizeof(uint64_t);
+
+ g_free(s->refcount_table);
+ s->refcount_table = new_reftable;
+ new_reftable = NULL;
+
+ /* Now the in-memory refcount information again corresponds to the on-disk
+ * information (reftable is empty and no refblocks (the refblock cache is
+ * empty)); however, this means some clusters (e.g. the image header) are
+ * referenced, but not refcounted, but the normal qcow2 code assumes that
+ * the in-memory information is always correct */
+
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC);
+
+ /* Enter the first refblock into the reftable */
+ rt_entry = cpu_to_be64(2 * s->cluster_size);
+ ret = bdrv_pwrite_sync(bs->file, s->cluster_size,
+ &rt_entry, sizeof(rt_entry));
+ if (ret < 0) {
+ goto fail_broken_refcounts;
+ }
+ s->refcount_table[0] = 2 * s->cluster_size;
+
+ s->free_cluster_index = 0;
+ assert(3 + l1_clusters <= s->refcount_block_size);
+ offset = qcow2_alloc_clusters(bs, 3 * s->cluster_size + l1_size2);
+ if (offset < 0) {
+ ret = offset;
+ goto fail_broken_refcounts;
+ } else if (offset > 0) {
+ error_report("First cluster in emptied image is in use");
+ abort();
+ }
+
+ /* Now finally the in-memory information corresponds to the on-disk
+ * structures and is correct */
+ ret = qcow2_mark_clean(bs);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ return 0;
+
+fail_broken_refcounts:
+ /* The BDS is unusable at this point. If we wanted to make it usable, we
+ * would have to call qcow2_refcount_close(), qcow2_refcount_init(),
+ * qcow2_check_refcounts(), qcow2_refcount_close() and qcow2_refcount_init()
+ * again. However, because the functions which could have caused this error
+ * path to be taken are used by those functions as well, it's very likely
+ * that that sequence will fail as well. Therefore, just eject the BDS. */
+ bs->drv = NULL;
+
+fail:
+ g_free(new_reftable);
+ return ret;
+}
+
+static int qcow2_make_empty(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t start_sector;
+ int sector_step = INT_MAX / BDRV_SECTOR_SIZE;
+ int l1_clusters, ret = 0;
+
+ l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t));
+
+ if (s->qcow_version >= 3 && !s->snapshots &&
+ 3 + l1_clusters <= s->refcount_block_size) {
+ /* The following function only works for qcow2 v3 images (it requires
+ * the dirty flag) and only as long as there are no snapshots (because
+ * it completely empties the image). Furthermore, the L1 table and three
+ * additional clusters (image header, refcount table, one refcount
+ * block) have to fit inside one refcount block. */
+ return make_completely_empty(bs);
+ }
+
+ /* This fallback code simply discards every active cluster; this is slow,
+ * but works in all cases */
+ for (start_sector = 0; start_sector < bs->total_sectors;
+ start_sector += sector_step)
+ {
+ /* As this function is generally used after committing an external
+ * snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the
+ * default action for this kind of discard is to pass the discard,
+ * which will ideally result in an actually smaller image file, as
+ * is probably desired. */
+ ret = qcow2_discard_clusters(bs, start_sector * BDRV_SECTOR_SIZE,
+ MIN(sector_step,
+ bs->total_sectors - start_sector),
+ QCOW2_DISCARD_SNAPSHOT, true);
+ if (ret < 0) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
@@ -2361,7 +2550,8 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
* Downgrades an image's version. To achieve this, any incompatible features
* have to be removed.
*/
-static int qcow2_downgrade(BlockDriverState *bs, int target_version)
+static int qcow2_downgrade(BlockDriverState *bs, int target_version,
+ BlockDriverAmendStatusCB *status_cb)
{
BDRVQcowState *s = bs->opaque;
int current_version = s->qcow_version;
@@ -2410,7 +2600,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version)
/* clearing autoclear features is trivial */
s->autoclear_features = 0;
- ret = qcow2_expand_zero_clusters(bs);
+ ret = qcow2_expand_zero_clusters(bs, status_cb);
if (ret < 0) {
return ret;
}
@@ -2424,7 +2614,8 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version)
return 0;
}
-static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts)
+static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
+ BlockDriverAmendStatusCB *status_cb)
{
BDRVQcowState *s = bs->opaque;
int old_version = s->qcow_version, new_version = old_version;
@@ -2502,7 +2693,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts)
return ret;
}
} else {
- ret = qcow2_downgrade(bs, new_version);
+ ret = qcow2_downgrade(bs, new_version, status_cb);
if (ret < 0) {
return ret;
}
@@ -2676,6 +2867,7 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_co_discard = qcow2_co_discard,
.bdrv_truncate = qcow2_truncate,
.bdrv_write_compressed = qcow2_write_compressed,
+ .bdrv_make_empty = qcow2_make_empty,
.bdrv_snapshot_create = qcow2_snapshot_create,
.bdrv_snapshot_goto = qcow2_snapshot_goto,
diff --git a/block/qcow2.h b/block/qcow2.h
index 577ccd1..6e39a1b 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -487,6 +487,8 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
int qcow2_refcount_init(BlockDriverState *bs);
void qcow2_refcount_close(BlockDriverState *bs);
+int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index);
+
int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
int addend, enum qcow2_discard_type type);
@@ -534,10 +536,11 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
- int nb_sectors, enum qcow2_discard_type type);
+ int nb_sectors, enum qcow2_discard_type type, bool full_discard);
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
-int qcow2_expand_zero_clusters(BlockDriverState *bs);
+int qcow2_expand_zero_clusters(BlockDriverState *bs,
+ BlockDriverAmendStatusCB *status_cb);
/* qcow2-snapshot.c functions */
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 475cf74..e100ae2 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -1481,12 +1481,12 @@ out:
return result;
}
-static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data,
- off_t *hole, int nb_sectors, int *pnum)
+static int try_fiemap(BlockDriverState *bs, off_t start, off_t *data,
+ off_t *hole, int nb_sectors)
{
#ifdef CONFIG_FIEMAP
BDRVRawState *s = bs->opaque;
- int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
+ int ret = 0;
struct {
struct fiemap fm;
struct fiemap_extent fe;
@@ -1527,18 +1527,14 @@ static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data,
#endif
}
-static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data,
- off_t *hole, int *pnum)
+static int try_seek_hole(BlockDriverState *bs, off_t start, off_t *data,
+ off_t *hole)
{
#if defined SEEK_HOLE && defined SEEK_DATA
BDRVRawState *s = bs->opaque;
*hole = lseek(s->fd, start, SEEK_HOLE);
if (*hole == -1) {
- /* -ENXIO indicates that sector_num was past the end of the file.
- * There is a virtual hole there. */
- assert(errno != -ENXIO);
-
return -errno;
}
@@ -1552,7 +1548,7 @@ static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data,
}
}
- return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
+ return 0;
#else
return -ENOTSUP;
#endif
@@ -1578,7 +1574,8 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
int nb_sectors, int *pnum)
{
off_t start, data = 0, hole = 0;
- int64_t ret;
+ int64_t total_size;
+ int ret;
ret = fd_open(bs);
if (ret < 0) {
@@ -1586,29 +1583,38 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
}
start = sector_num * BDRV_SECTOR_SIZE;
+ total_size = bdrv_getlength(bs);
+ if (total_size < 0) {
+ return total_size;
+ } else if (start >= total_size) {
+ *pnum = 0;
+ return 0;
+ } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) {
+ nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE);
+ }
- ret = try_seek_hole(bs, start, &data, &hole, pnum);
+ ret = try_seek_hole(bs, start, &data, &hole);
if (ret < 0) {
- ret = try_fiemap(bs, start, &data, &hole, nb_sectors, pnum);
+ ret = try_fiemap(bs, start, &data, &hole, nb_sectors);
if (ret < 0) {
/* Assume everything is allocated. */
data = 0;
hole = start + nb_sectors * BDRV_SECTOR_SIZE;
- ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
+ ret = 0;
}
}
+ assert(ret >= 0);
+
if (data <= start) {
/* On a data extent, compute sectors to the end of the extent. */
*pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
+ return ret | BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
} else {
/* On a hole, compute sectors to the beginning of the next extent. */
*pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
- ret &= ~BDRV_BLOCK_DATA;
- ret |= BDRV_BLOCK_ZERO;
+ return ret | BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID | start;
}
-
- return ret;
}
static coroutine_fn BlockAIOCB *raw_aio_discard(BlockDriverState *bs,
diff --git a/block/rbd.c b/block/rbd.c
index 47cab8b..5b5a64a 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -887,6 +887,18 @@ static BlockAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
}
#endif
+#ifdef LIBRBD_SUPPORTS_INVALIDATE
+static void qemu_rbd_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
+{
+ BDRVRBDState *s = bs->opaque;
+ int r = rbd_invalidate_cache(s->image);
+ if (r < 0) {
+ error_setg_errno(errp, -r, "Failed to invalidate the cache");
+ }
+}
+#endif
+
static QemuOptsList qemu_rbd_create_opts = {
.name = "rbd-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(qemu_rbd_create_opts.head),
@@ -936,6 +948,9 @@ static BlockDriver bdrv_rbd = {
.bdrv_snapshot_delete = qemu_rbd_snap_remove,
.bdrv_snapshot_list = qemu_rbd_snap_list,
.bdrv_snapshot_goto = qemu_rbd_snap_rollback,
+#ifdef LIBRBD_SUPPORTS_INVALIDATE
+ .bdrv_invalidate_cache = qemu_rbd_invalidate_cache,
+#endif
};
static void bdrv_rbd_init(void)
diff --git a/block/snapshot.c b/block/snapshot.c
index 85c52ff..698e1a1 100644
--- a/block/snapshot.c
+++ b/block/snapshot.c
@@ -236,6 +236,10 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
error_setg(errp, "snapshot_id and name are both NULL");
return -EINVAL;
}
+
+ /* drain all pending i/o before deleting snapshot */
+ bdrv_drain_all();
+
if (drv->bdrv_snapshot_delete) {
return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
}
diff --git a/block/stream.c b/block/stream.c
index a1dc8da..a628901 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -79,9 +79,39 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
bdrv_refresh_limits(top, NULL);
}
+typedef struct {
+ int ret;
+ bool reached_end;
+} StreamCompleteData;
+
+static void stream_complete(BlockJob *job, void *opaque)
+{
+ StreamBlockJob *s = container_of(job, StreamBlockJob, common);
+ StreamCompleteData *data = opaque;
+ BlockDriverState *base = s->base;
+
+ if (!block_job_is_cancelled(&s->common) && data->reached_end &&
+ data->ret == 0) {
+ const char *base_id = NULL, *base_fmt = NULL;
+ if (base) {
+ base_id = s->backing_file_str;
+ if (base->drv) {
+ base_fmt = base->drv->format_name;
+ }
+ }
+ data->ret = bdrv_change_backing_file(job->bs, base_id, base_fmt);
+ close_unused_images(job->bs, base, base_id);
+ }
+
+ g_free(s->backing_file_str);
+ block_job_completed(&s->common, data->ret);
+ g_free(data);
+}
+
static void coroutine_fn stream_run(void *opaque)
{
StreamBlockJob *s = opaque;
+ StreamCompleteData *data;
BlockDriverState *bs = s->common.bs;
BlockDriverState *base = s->base;
int64_t sector_num, end;
@@ -183,21 +213,13 @@ wait:
/* Do not remove the backing file if an error was there but ignored. */
ret = error;
- if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) {
- const char *base_id = NULL, *base_fmt = NULL;
- if (base) {
- base_id = s->backing_file_str;
- if (base->drv) {
- base_fmt = base->drv->format_name;
- }
- }
- ret = bdrv_change_backing_file(bs, base_id, base_fmt);
- close_unused_images(bs, base, base_id);
- }
-
qemu_vfree(buf);
- g_free(s->backing_file_str);
- block_job_completed(&s->common, ret);
+
+ /* Modify backing chain and close BDSes in main loop */
+ data = g_malloc(sizeof(*data));
+ data->ret = ret;
+ data->reached_end = sector_num == end;
+ block_job_defer_to_main_loop(&s->common, stream_complete, data);
}
static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
diff --git a/block/vdi.c b/block/vdi.c
index 19701ee..e1d211c 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -137,12 +137,14 @@ static inline int uuid_is_null(const uuid_t uu)
return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0;
}
+# if defined(CONFIG_VDI_DEBUG)
static inline void uuid_unparse(const uuid_t uu, char *out)
{
snprintf(out, 37, UUID_FMT,
uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
}
+# endif
#endif
typedef struct {
diff --git a/blockdev.c b/blockdev.c
index eb743a9..57910b8 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -115,14 +115,21 @@ void blockdev_mark_auto_del(BlockBackend *blk)
{
DriveInfo *dinfo = blk_legacy_dinfo(blk);
BlockDriverState *bs = blk_bs(blk);
+ AioContext *aio_context;
if (!dinfo) {
return;
}
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
if (bs->job) {
block_job_cancel(bs->job);
}
+
+ aio_context_release(aio_context);
+
dinfo->auto_del = 1;
}
@@ -1922,6 +1929,11 @@ out:
static void block_job_cb(void *opaque, int ret)
{
+ /* Note that this function may be executed from another AioContext besides
+ * the QEMU main loop. If you need to access anything that assumes the
+ * QEMU global mutex, use a BH or introduce a mutex.
+ */
+
BlockDriverState *bs = opaque;
const char *msg = NULL;
@@ -1951,6 +1963,7 @@ void qmp_block_stream(const char *device,
{
BlockDriverState *bs;
BlockDriverState *base_bs = NULL;
+ AioContext *aio_context;
Error *local_err = NULL;
const char *base_name = NULL;
@@ -1964,16 +1977,20 @@ void qmp_block_stream(const char *device,
return;
}
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) {
- return;
+ goto out;
}
if (has_base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
error_set(errp, QERR_BASE_NOT_FOUND, base);
- return;
+ goto out;
}
+ assert(bdrv_get_aio_context(base_bs) == aio_context);
base_name = base;
}
@@ -1982,7 +1999,7 @@ void qmp_block_stream(const char *device,
if (base_bs == NULL && has_backing_file) {
error_setg(errp, "backing file specified, but streaming the "
"entire chain");
- return;
+ goto out;
}
/* backing_file string overrides base bs filename */
@@ -1992,10 +2009,13 @@ void qmp_block_stream(const char *device,
on_error, block_job_cb, bs, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
trace_qmp_block_stream(bs, bs->job);
+
+out:
+ aio_context_release(aio_context);
}
void qmp_block_commit(const char *device,
@@ -2007,6 +2027,7 @@ void qmp_block_commit(const char *device,
{
BlockDriverState *bs;
BlockDriverState *base_bs, *top_bs;
+ AioContext *aio_context;
Error *local_err = NULL;
/* This will be part of the QMP command, if/when the
* BlockdevOnError change for blkmirror makes it in
@@ -2017,9 +2038,6 @@ void qmp_block_commit(const char *device,
speed = 0;
}
- /* drain all i/o before commits */
- bdrv_drain_all();
-
/* Important Note:
* libvirt relies on the DeviceNotFound error class in order to probe for
* live commit feature versions; for this to work, we must make sure to
@@ -2031,8 +2049,14 @@ void qmp_block_commit(const char *device,
return;
}
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
+ /* drain all i/o before commits */
+ bdrv_drain_all();
+
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, errp)) {
- return;
+ goto out;
}
/* default top_bs is the active layer */
@@ -2046,9 +2070,11 @@ void qmp_block_commit(const char *device,
if (top_bs == NULL) {
error_setg(errp, "Top image file %s not found", top ? top : "NULL");
- return;
+ goto out;
}
+ assert(bdrv_get_aio_context(top_bs) == aio_context);
+
if (has_base && base) {
base_bs = bdrv_find_backing_image(top_bs, base);
} else {
@@ -2057,20 +2083,22 @@ void qmp_block_commit(const char *device,
if (base_bs == NULL) {
error_set(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL");
- return;
+ goto out;
}
+ assert(bdrv_get_aio_context(base_bs) == aio_context);
+
/* Do not allow attempts to commit an image into itself */
if (top_bs == base_bs) {
error_setg(errp, "cannot commit an image into itself");
- return;
+ goto out;
}
if (top_bs == bs) {
if (has_backing_file) {
error_setg(errp, "'backing-file' specified,"
" but 'top' is the active layer");
- return;
+ goto out;
}
commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
bs, &local_err);
@@ -2080,8 +2108,11 @@ void qmp_block_commit(const char *device,
}
if (local_err != NULL) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
+
+out:
+ aio_context_release(aio_context);
}
void qmp_drive_backup(const char *device, const char *target,
@@ -2096,6 +2127,7 @@ void qmp_drive_backup(const char *device, const char *target,
BlockDriverState *bs;
BlockDriverState *target_bs;
BlockDriverState *source = NULL;
+ AioContext *aio_context;
BlockDriver *drv = NULL;
Error *local_err = NULL;
int flags;
@@ -2121,9 +2153,12 @@ void qmp_drive_backup(const char *device, const char *target,
return;
}
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
if (!bdrv_is_inserted(bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
- return;
+ goto out;
}
if (!has_format) {
@@ -2133,12 +2168,12 @@ void qmp_drive_backup(const char *device, const char *target,
drv = bdrv_find_format(format);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
- return;
+ goto out;
}
}
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
- return;
+ goto out;
}
flags = bs->open_flags | BDRV_O_RDWR;
@@ -2158,7 +2193,7 @@ void qmp_drive_backup(const char *device, const char *target,
size = bdrv_getlength(bs);
if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed");
- return;
+ goto out;
}
if (mode != NEW_IMAGE_MODE_EXISTING) {
@@ -2175,23 +2210,28 @@ void qmp_drive_backup(const char *device, const char *target,
if (local_err) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
target_bs = NULL;
ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
+ bdrv_set_aio_context(target_bs, aio_context);
+
backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
block_job_cb, bs, &local_err);
if (local_err != NULL) {
bdrv_unref(target_bs);
error_propagate(errp, local_err);
- return;
+ goto out;
}
+
+out:
+ aio_context_release(aio_context);
}
BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
@@ -2216,6 +2256,7 @@ void qmp_drive_mirror(const char *device, const char *target,
{
BlockDriverState *bs;
BlockDriverState *source, *target_bs;
+ AioContext *aio_context;
BlockDriver *drv = NULL;
Error *local_err = NULL;
QDict *options = NULL;
@@ -2258,9 +2299,12 @@ void qmp_drive_mirror(const char *device, const char *target,
return;
}
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
if (!bdrv_is_inserted(bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
- return;
+ goto out;
}
if (!has_format) {
@@ -2270,12 +2314,12 @@ void qmp_drive_mirror(const char *device, const char *target,
drv = bdrv_find_format(format);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
- return;
+ goto out;
}
}
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
- return;
+ goto out;
}
flags = bs->open_flags | BDRV_O_RDWR;
@@ -2290,29 +2334,36 @@ void qmp_drive_mirror(const char *device, const char *target,
size = bdrv_getlength(bs);
if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed");
- return;
+ goto out;
}
if (has_replaces) {
BlockDriverState *to_replace_bs;
+ AioContext *replace_aio_context;
+ int64_t replace_size;
if (!has_node_name) {
error_setg(errp, "a node-name must be provided when replacing a"
" named node of the graph");
- return;
+ goto out;
}
to_replace_bs = check_to_replace_node(replaces, &local_err);
if (!to_replace_bs) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
- if (size != bdrv_getlength(to_replace_bs)) {
+ replace_aio_context = bdrv_get_aio_context(to_replace_bs);
+ aio_context_acquire(replace_aio_context);
+ replace_size = bdrv_getlength(to_replace_bs);
+ aio_context_release(replace_aio_context);
+
+ if (size != replace_size) {
error_setg(errp, "cannot replace image with a mirror image of "
"different size");
- return;
+ goto out;
}
}
@@ -2341,7 +2392,7 @@ void qmp_drive_mirror(const char *device, const char *target,
if (local_err) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
if (has_node_name) {
@@ -2357,9 +2408,11 @@ void qmp_drive_mirror(const char *device, const char *target,
flags | BDRV_O_NO_BACKING, drv, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
- return;
+ goto out;
}
+ bdrv_set_aio_context(target_bs, aio_context);
+
/* pass the node name to replace to mirror start since it's loose coupling
* and will allow to check whether the node still exist at mirror completion
*/
@@ -2371,24 +2424,42 @@ void qmp_drive_mirror(const char *device, const char *target,
if (local_err != NULL) {
bdrv_unref(target_bs);
error_propagate(errp, local_err);
- return;
+ goto out;
}
+
+out:
+ aio_context_release(aio_context);
}
-static BlockJob *find_block_job(const char *device)
+/* Get the block job for a given device name and acquire its AioContext */
+static BlockJob *find_block_job(const char *device, AioContext **aio_context)
{
BlockDriverState *bs;
bs = bdrv_find(device);
- if (!bs || !bs->job) {
- return NULL;
+ if (!bs) {
+ goto notfound;
}
+
+ *aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(*aio_context);
+
+ if (!bs->job) {
+ aio_context_release(*aio_context);
+ goto notfound;
+ }
+
return bs->job;
+
+notfound:
+ *aio_context = NULL;
+ return NULL;
}
void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
{
- BlockJob *job = find_block_job(device);
+ AioContext *aio_context;
+ BlockJob *job = find_block_job(device, &aio_context);
if (!job) {
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
@@ -2396,34 +2467,40 @@ void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
}
block_job_set_speed(job, speed, errp);
+ aio_context_release(aio_context);
}
void qmp_block_job_cancel(const char *device,
bool has_force, bool force, Error **errp)
{
- BlockJob *job = find_block_job(device);
-
- if (!has_force) {
- force = false;
- }
+ AioContext *aio_context;
+ BlockJob *job = find_block_job(device, &aio_context);
if (!job) {
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
return;
}
+
+ if (!has_force) {
+ force = false;
+ }
+
if (job->paused && !force) {
error_setg(errp, "The block job for device '%s' is currently paused",
device);
- return;
+ goto out;
}
trace_qmp_block_job_cancel(job);
block_job_cancel(job);
+out:
+ aio_context_release(aio_context);
}
void qmp_block_job_pause(const char *device, Error **errp)
{
- BlockJob *job = find_block_job(device);
+ AioContext *aio_context;
+ BlockJob *job = find_block_job(device, &aio_context);
if (!job) {
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
@@ -2432,11 +2509,13 @@ void qmp_block_job_pause(const char *device, Error **errp)
trace_qmp_block_job_pause(job);
block_job_pause(job);
+ aio_context_release(aio_context);
}
void qmp_block_job_resume(const char *device, Error **errp)
{
- BlockJob *job = find_block_job(device);
+ AioContext *aio_context;
+ BlockJob *job = find_block_job(device, &aio_context);
if (!job) {
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
@@ -2445,11 +2524,13 @@ void qmp_block_job_resume(const char *device, Error **errp)
trace_qmp_block_job_resume(job);
block_job_resume(job);
+ aio_context_release(aio_context);
}
void qmp_block_job_complete(const char *device, Error **errp)
{
- BlockJob *job = find_block_job(device);
+ AioContext *aio_context;
+ BlockJob *job = find_block_job(device, &aio_context);
if (!job) {
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
@@ -2458,6 +2539,7 @@ void qmp_block_job_complete(const char *device, Error **errp)
trace_qmp_block_job_complete(job);
block_job_complete(job, errp);
+ aio_context_release(aio_context);
}
void qmp_change_backing_file(const char *device,
@@ -2602,12 +2684,18 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
BlockDriverState *bs;
for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+
+ aio_context_acquire(aio_context);
+
if (bs->job) {
BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
elem->value = block_job_query(bs->job);
*p_next = elem;
p_next = &elem->next;
}
+
+ aio_context_release(aio_context);
}
return head;
diff --git a/blockjob.c b/blockjob.c
index ff0908a..ba2255d 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -50,6 +50,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
error_setg(&job->blocker, "block device is in use by block job: %s",
BlockJobType_lookup[driver->job_type]);
bdrv_op_block_all(bs, job->blocker);
+ bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
job->driver = driver;
job->bs = bs;
@@ -153,7 +154,7 @@ void block_job_iostatus_reset(BlockJob *job)
}
}
-struct BlockCancelData {
+struct BlockFinishData {
BlockJob *job;
BlockCompletionFunc *cb;
void *opaque;
@@ -161,19 +162,22 @@ struct BlockCancelData {
int ret;
};
-static void block_job_cancel_cb(void *opaque, int ret)
+static void block_job_finish_cb(void *opaque, int ret)
{
- struct BlockCancelData *data = opaque;
+ struct BlockFinishData *data = opaque;
data->cancelled = block_job_is_cancelled(data->job);
data->ret = ret;
data->cb(data->opaque, ret);
}
-int block_job_cancel_sync(BlockJob *job)
+static int block_job_finish_sync(BlockJob *job,
+ void (*finish)(BlockJob *, Error **errp),
+ Error **errp)
{
- struct BlockCancelData data;
+ struct BlockFinishData data;
BlockDriverState *bs = job->bs;
+ Error *local_err = NULL;
assert(bs->job == job);
@@ -184,15 +188,37 @@ int block_job_cancel_sync(BlockJob *job)
data.cb = job->cb;
data.opaque = job->opaque;
data.ret = -EINPROGRESS;
- job->cb = block_job_cancel_cb;
+ job->cb = block_job_finish_cb;
job->opaque = &data;
- block_job_cancel(job);
+ finish(job, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -EBUSY;
+ }
while (data.ret == -EINPROGRESS) {
aio_poll(bdrv_get_aio_context(bs), true);
}
return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret;
}
+/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
+ * used with block_job_finish_sync() without the need for (rather nasty)
+ * function pointer casts there. */
+static void block_job_cancel_err(BlockJob *job, Error **errp)
+{
+ block_job_cancel(job);
+}
+
+int block_job_cancel_sync(BlockJob *job)
+{
+ return block_job_finish_sync(job, &block_job_cancel_err, NULL);
+}
+
+int block_job_complete_sync(BlockJob *job, Error **errp)
+{
+ return block_job_finish_sync(job, &block_job_complete, errp);
+}
+
void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
{
assert(job->busy);
@@ -236,6 +262,7 @@ BlockJobInfo *block_job_query(BlockJob *job)
info->offset = job->offset;
info->speed = job->speed;
info->io_status = job->iostatus;
+ info->ready = job->ready;
return info;
}
@@ -271,6 +298,8 @@ void block_job_event_completed(BlockJob *job, const char *msg)
void block_job_event_ready(BlockJob *job)
{
+ job->ready = true;
+
qapi_event_send_block_job_ready(job->driver->job_type,
bdrv_get_device_name(job->bs),
job->len,
@@ -314,3 +343,48 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
}
return action;
}
+
+typedef struct {
+ BlockJob *job;
+ QEMUBH *bh;
+ AioContext *aio_context;
+ BlockJobDeferToMainLoopFn *fn;
+ void *opaque;
+} BlockJobDeferToMainLoopData;
+
+static void block_job_defer_to_main_loop_bh(void *opaque)
+{
+ BlockJobDeferToMainLoopData *data = opaque;
+ AioContext *aio_context;
+
+ qemu_bh_delete(data->bh);
+
+ /* Prevent race with block_job_defer_to_main_loop() */
+ aio_context_acquire(data->aio_context);
+
+ /* Fetch BDS AioContext again, in case it has changed */
+ aio_context = bdrv_get_aio_context(data->job->bs);
+ aio_context_acquire(aio_context);
+
+ data->fn(data->job, data->opaque);
+
+ aio_context_release(aio_context);
+
+ aio_context_release(data->aio_context);
+
+ g_free(data);
+}
+
+void block_job_defer_to_main_loop(BlockJob *job,
+ BlockJobDeferToMainLoopFn *fn,
+ void *opaque)
+{
+ BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data));
+ data->job = job;
+ data->bh = qemu_bh_new(block_job_defer_to_main_loop_bh, data);
+ data->aio_context = bdrv_get_aio_context(job->bs);
+ data->fn = fn;
+ data->opaque = opaque;
+
+ qemu_bh_schedule(data->bh);
+}
diff --git a/configure b/configure
index a9e4d49..2f17bf3 100755
--- a/configure
+++ b/configure
@@ -4908,6 +4908,7 @@ echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
if test "$sparse" = "yes" ; then
echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak
+ echo "CXX := REAL_CC=\"\$(CXX)\" cgcc" >> $config_host_mak
echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak
echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak
fi
diff --git a/dump.c b/dump.c
index 06a4915..9c7dad8 100644
--- a/dump.c
+++ b/dump.c
@@ -604,10 +604,9 @@ static void dump_iterate(DumpState *s, Error **errp)
{
GuestPhysBlock *block;
int64_t size;
- int ret;
Error *local_err = NULL;
- while (1) {
+ do {
block = s->next_block;
size = block->target_end - block->target_start;
@@ -623,11 +622,9 @@ static void dump_iterate(DumpState *s, Error **errp)
return;
}
- ret = get_next_block(s, block);
- if (ret == 1) {
- dump_completed(s);
- }
- }
+ } while (!get_next_block(s, block));
+
+ dump_completed(s);
}
static void create_vmcore(DumpState *s, Error **errp)
diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c
index b57966d..59c7445 100644
--- a/hw/9pfs/virtio-9p-proxy.c
+++ b/hw/9pfs/virtio-9p-proxy.c
@@ -1104,14 +1104,15 @@ static int connect_namedsocket(const char *path)
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0) {
- fprintf(stderr, "socket %s\n", strerror(errno));
+ fprintf(stderr, "failed to create socket: %s\n", strerror(errno));
return -1;
}
strcpy(helper.sun_path, path);
helper.sun_family = AF_UNIX;
size = strlen(helper.sun_path) + sizeof(helper.sun_family);
if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) {
- fprintf(stderr, "socket error\n");
+ fprintf(stderr, "failed to connect to %s: %s\n", path, strerror(errno));
+ close(sockfd);
return -1;
}
@@ -1154,10 +1155,12 @@ static int proxy_init(FsContext *ctx)
sock_id = atoi(ctx->fs_root);
if (sock_id < 0) {
fprintf(stderr, "socket descriptor not initialized\n");
- g_free(proxy);
- return -1;
}
}
+ if (sock_id < 0) {
+ g_free(proxy);
+ return -1;
+ }
g_free(ctx->fs_root);
ctx->fs_root = NULL;
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index bffbea5..0014c34 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -580,7 +580,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
- &is_linux);
+ &is_linux, NULL, NULL);
}
/* On aarch64, it's the bootloader's job to uncompress the kernel. */
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 45b1164..1222a37 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -196,6 +196,11 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
blk_op_block_all(conf->conf.blk, s->blocker);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker);
+ blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker);
+ blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT, s->blocker);
+ blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR, s->blocker);
+ blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker);
+ blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker);
*dataplane = s;
}
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 5f3a859..bbe6eb3 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -477,7 +477,9 @@ static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
/* Load a U-Boot image. */
static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
- int *is_linux, uint8_t image_type)
+ int *is_linux, uint8_t image_type,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque)
{
int fd;
int size;
@@ -511,6 +513,9 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
switch (hdr->ih_type) {
case IH_TYPE_KERNEL:
address = hdr->ih_load;
+ if (translate_fn) {
+ address = translate_fn(translate_opaque, address);
+ }
if (loadaddr) {
*loadaddr = hdr->ih_load;
}
@@ -587,15 +592,19 @@ out:
}
int load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr,
- int *is_linux)
+ int *is_linux,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque)
{
- return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL);
+ return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL,
+ translate_fn, translate_opaque);
}
/* Load a ramdisk. */
int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz)
{
- return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK);
+ return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK,
+ NULL, NULL);
}
/* This simply prevents g_malloc in the function below from allocating
diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index 2dc362b..c6d34b2 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -405,7 +405,6 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
}
if (!kvm_enabled()) {
- cpu_restore_state(cs, cs->mem_io_pc);
cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base,
&current_flags);
}
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 61aba9f..889e888 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1688,6 +1688,20 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
pcms->max_ram_below_4g = value;
}
+static bool pc_machine_get_vmport(Object *obj, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ return pcms->vmport;
+}
+
+static void pc_machine_set_vmport(Object *obj, bool value, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ pcms->vmport = value;
+}
+
static void pc_machine_initfn(Object *obj)
{
PCMachineState *pcms = PC_MACHINE(obj);
@@ -1700,6 +1714,11 @@ static void pc_machine_initfn(Object *obj)
pc_machine_get_max_ram_below_4g,
pc_machine_set_max_ram_below_4g,
NULL, NULL, NULL);
+ pcms->vmport = !xen_enabled();
+ object_property_add_bool(obj, PC_MACHINE_VMPORT,
+ pc_machine_get_vmport,
+ pc_machine_set_vmport,
+ NULL);
}
static void pc_machine_class_init(ObjectClass *oc, void *data)
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 91a20cb..1cda5dd 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -234,8 +234,8 @@ static void pc_init1(MachineState *machine,
pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL);
/* init basic PC hardware */
- pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled(),
- 0x4);
+ pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy,
+ !pc_machine->vmport, 0x4);
pc_nic_init(isa_bus, pci_bus);
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index e225c6d..4d9e3cd 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -242,7 +242,8 @@ static void pc_q35_init(MachineState *machine)
pc_register_ferr_irq(gsi[13]);
/* init basic PC hardware */
- pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, false, 0xff0104);
+ pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy,
+ !pc_machine->vmport, 0xff0104);
/* connect pm stuff to lpc */
ich9_lpc_pm_init(lpc);
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 70958e3..61dbed1 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -49,6 +49,9 @@ static int handle_cmd(AHCIState *s,int port,int slot);
static void ahci_reset_port(AHCIState *s, int port);
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis);
static void ahci_init_d2h(AHCIDevice *ad);
+static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write);
+static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes);
+
static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
{
@@ -567,24 +570,24 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
AHCIDevice *ad = &s->dev[port];
AHCIPortRegs *pr = &ad->port_regs;
IDEState *ide_state;
- uint8_t *sdb_fis;
+ SDBFIS *sdb_fis;
if (!s->dev[port].res_fis ||
!(pr->cmd & PORT_CMD_FIS_RX)) {
return;
}
- sdb_fis = &ad->res_fis[RES_FIS_SDBFIS];
+ sdb_fis = (SDBFIS *)&ad->res_fis[RES_FIS_SDBFIS];
ide_state = &ad->port.ifs[0];
- /* clear memory */
- *(uint32_t*)sdb_fis = 0;
-
- /* write values */
- sdb_fis[0] = ide_state->error;
- sdb_fis[2] = ide_state->status & 0x77;
+ sdb_fis->type = 0xA1;
+ /* Interrupt pending & Notification bit */
+ sdb_fis->flags = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
+ sdb_fis->status = ide_state->status & 0x77;
+ sdb_fis->error = ide_state->error;
+ /* update SAct field in SDB_FIS */
s->dev[port].finished |= finished;
- *(uint32_t*)(sdb_fis + 4) = cpu_to_le32(ad->finished);
+ sdb_fis->payload = cpu_to_le32(ad->finished);
/* Update shadow registers (except BSY 0x80 and DRQ 0x08) */
pr->tfdata = (ad->port.ifs[0].error << 8) |
@@ -600,6 +603,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
uint8_t *pio_fis, *cmd_fis;
uint64_t tbl_addr;
dma_addr_t cmd_len = 0x80;
+ IDEState *s = &ad->port.ifs[0];
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
return;
@@ -629,21 +633,21 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
pio_fis[0] = 0x5f;
pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
- pio_fis[2] = ad->port.ifs[0].status;
- pio_fis[3] = ad->port.ifs[0].error;
-
- pio_fis[4] = cmd_fis[4];
- pio_fis[5] = cmd_fis[5];
- pio_fis[6] = cmd_fis[6];
- pio_fis[7] = cmd_fis[7];
- pio_fis[8] = cmd_fis[8];
- pio_fis[9] = cmd_fis[9];
- pio_fis[10] = cmd_fis[10];
- pio_fis[11] = cmd_fis[11];
+ pio_fis[2] = s->status;
+ pio_fis[3] = s->error;
+
+ pio_fis[4] = s->sector;
+ pio_fis[5] = s->lcyl;
+ pio_fis[6] = s->hcyl;
+ pio_fis[7] = s->select;
+ pio_fis[8] = s->hob_sector;
+ pio_fis[9] = s->hob_lcyl;
+ pio_fis[10] = s->hob_hcyl;
+ pio_fis[11] = 0;
pio_fis[12] = cmd_fis[12];
pio_fis[13] = cmd_fis[13];
pio_fis[14] = 0;
- pio_fis[15] = ad->port.ifs[0].status;
+ pio_fis[15] = s->status;
pio_fis[16] = len & 255;
pio_fis[17] = len >> 8;
pio_fis[18] = 0;
@@ -670,6 +674,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
int i;
dma_addr_t cmd_len = 0x80;
int cmd_mapped = 0;
+ IDEState *s = &ad->port.ifs[0];
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
return;
@@ -687,17 +692,17 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
d2h_fis[0] = 0x34;
d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
- d2h_fis[2] = ad->port.ifs[0].status;
- d2h_fis[3] = ad->port.ifs[0].error;
-
- d2h_fis[4] = cmd_fis[4];
- d2h_fis[5] = cmd_fis[5];
- d2h_fis[6] = cmd_fis[6];
- d2h_fis[7] = cmd_fis[7];
- d2h_fis[8] = cmd_fis[8];
- d2h_fis[9] = cmd_fis[9];
- d2h_fis[10] = cmd_fis[10];
- d2h_fis[11] = cmd_fis[11];
+ d2h_fis[2] = s->status;
+ d2h_fis[3] = s->error;
+
+ d2h_fis[4] = s->sector;
+ d2h_fis[5] = s->lcyl;
+ d2h_fis[6] = s->hcyl;
+ d2h_fis[7] = s->select;
+ d2h_fis[8] = s->hob_sector;
+ d2h_fis[9] = s->hob_lcyl;
+ d2h_fis[10] = s->hob_hcyl;
+ d2h_fis[11] = 0;
d2h_fis[12] = cmd_fis[12];
d2h_fis[13] = cmd_fis[13];
for (i = 14; i < 20; i++) {
@@ -1103,16 +1108,12 @@ static void ahci_start_transfer(IDEDMA *dma)
}
}
- /* update number of transferred bytes */
- ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + size);
-
out:
/* declare that we processed everything */
s->data_ptr = s->data_end;
- if (has_sglist) {
- qemu_sglist_destroy(&s->sg);
- }
+ /* Update number of transferred bytes, destroy sglist */
+ ahci_commit_buf(dma, size);
s->end_transfer_func(s);
@@ -1133,6 +1134,11 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s,
dma_cb(s, 0);
}
+/**
+ * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist.
+ * Not currently invoked by PIO R/W chains,
+ * which invoke ahci_populate_sglist via ahci_start_transfer.
+ */
static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
@@ -1145,6 +1151,24 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
return s->io_buffer_size != 0;
}
+/**
+ * Destroys the scatter-gather list,
+ * and updates the command header with a bytes-read value.
+ * called explicitly via ahci_dma_rw_buf (ATAPI DMA),
+ * and ahci_start_transfer (PIO R/W),
+ * and called via callback from ide_dma_cb for DMA R/W paths.
+ */
+static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes)
+{
+ AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+ IDEState *s = &ad->port.ifs[0];
+
+ tx_bytes += le32_to_cpu(ad->cur_cmd->status);
+ ad->cur_cmd->status = cpu_to_le32(tx_bytes);
+
+ qemu_sglist_destroy(&s->sg);
+}
+
static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
@@ -1162,11 +1186,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
dma_buf_write(p, l, &s->sg);
}
- /* free sglist that was created in ahci_populate_sglist() */
- qemu_sglist_destroy(&s->sg);
+ /* free sglist, update byte count */
+ ahci_commit_buf(dma, l);
- /* update number of transferred bytes */
- ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l);
s->io_buffer_index += l;
s->io_buffer_offset += l;
@@ -1209,6 +1231,7 @@ static const IDEDMAOps ahci_dma_ops = {
.start_dma = ahci_start_dma,
.start_transfer = ahci_start_transfer,
.prepare_buf = ahci_dma_prepare_buf,
+ .commit_buf = ahci_commit_buf,
.rw_buf = ahci_dma_rw_buf,
.set_unit = ahci_dma_set_unit,
.cmd_done = ahci_cmd_done,
diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
index e8ea34a..b123237 100644
--- a/hw/ide/ahci.h
+++ b/hw/ide/ahci.h
@@ -327,6 +327,14 @@ typedef struct NCQFrame {
uint8_t reserved10;
} QEMU_PACKED NCQFrame;
+typedef struct SDBFIS {
+ uint8_t type;
+ uint8_t flags;
+ uint8_t status;
+ uint8_t error;
+ uint32_t payload;
+} QEMU_PACKED SDBFIS;
+
void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports);
void ahci_uninit(AHCIState *s);
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 44e3d50..d316ccf 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -634,8 +634,11 @@ void ide_sector_read(IDEState *s)
ide_sector_read_cb, s);
}
-static void dma_buf_commit(IDEState *s)
+static void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
{
+ if (s->bus->dma->ops->commit_buf) {
+ s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes);
+ }
qemu_sglist_destroy(&s->sg);
}
@@ -650,6 +653,7 @@ void ide_set_inactive(IDEState *s, bool more)
void ide_dma_error(IDEState *s)
{
+ dma_buf_commit(s, 0);
ide_abort_command(s);
ide_set_inactive(s, false);
ide_set_irq(s->bus);
@@ -665,7 +669,6 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
s->bus->error_status = op;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
if (op & IDE_RETRY_DMA) {
- dma_buf_commit(s);
ide_dma_error(s);
} else {
ide_rw_error(s);
@@ -709,7 +712,8 @@ void ide_dma_cb(void *opaque, int ret)
sector_num = ide_get_sector(s);
if (n > 0) {
- dma_buf_commit(s);
+ assert(s->io_buffer_size == s->sg.size);
+ dma_buf_commit(s, s->io_buffer_size);
sector_num += n;
ide_set_sector(s, sector_num);
s->nsector -= n;
@@ -740,7 +744,6 @@ void ide_dma_cb(void *opaque, int ret)
if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) &&
!ide_sect_range_ok(s, sector_num, n)) {
- dma_buf_commit(s);
ide_dma_error(s);
return;
}
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 68ac610..907493d 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -322,6 +322,7 @@ typedef void EndTransferFunc(IDEState *);
typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *);
typedef void DMAVoidFunc(IDEDMA *);
typedef int DMAIntFunc(IDEDMA *, int);
+typedef void DMAu32Func(IDEDMA *, uint32_t);
typedef void DMAStopFunc(IDEDMA *, bool);
typedef void DMARestartFunc(void *, int, RunState);
@@ -430,6 +431,7 @@ struct IDEDMAOps {
DMAStartFunc *start_dma;
DMAVoidFunc *start_transfer;
DMAIntFunc *prepare_buf;
+ DMAu32Func *commit_buf;
DMAIntFunc *rw_buf;
DMAIntFunc *set_unit;
DMAStopFunc *set_inactive;
diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c
index a9ac270..f1f1350 100644
--- a/hw/m68k/an5206.c
+++ b/hw/m68k/an5206.c
@@ -74,7 +74,8 @@ static void an5206_init(MachineState *machine)
NULL, NULL, 1, ELF_MACHINE, 0);
entry = elf_entry;
if (kernel_size < 0) {
- kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
+ kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
+ NULL, NULL);
}
if (kernel_size < 0) {
kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR,
diff --git a/hw/m68k/dummy_m68k.c b/hw/m68k/dummy_m68k.c
index 957ef82..facd561 100644
--- a/hw/m68k/dummy_m68k.c
+++ b/hw/m68k/dummy_m68k.c
@@ -50,7 +50,8 @@ static void dummy_m68k_init(MachineState *machine)
NULL, NULL, 1, ELF_MACHINE, 0);
entry = elf_entry;
if (kernel_size < 0) {
- kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
+ kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
+ NULL, NULL);
}
if (kernel_size < 0) {
kernel_size = load_image_targphys(kernel_filename,
diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c
index 188230f..a01a445 100644
--- a/hw/m68k/mcf5208.c
+++ b/hw/m68k/mcf5208.c
@@ -279,7 +279,8 @@ static void mcf5208evb_init(MachineState *machine)
NULL, NULL, 1, ELF_MACHINE, 0);
entry = elf_entry;
if (kernel_size < 0) {
- kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
+ kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
+ NULL, NULL);
}
if (kernel_size < 0) {
kernel_size = load_image_targphys(kernel_filename, 0x40000000,
diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c
index 6bf36d0..a2843cd 100644
--- a/hw/microblaze/boot.c
+++ b/hw/microblaze/boot.c
@@ -154,7 +154,8 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
if (kernel_size < 0) {
hwaddr uentry, loadaddr;
- kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
+ kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0,
+ NULL, NULL);
boot_info.bootstrap_pc = uentry;
high = (loadaddr + kernel_size + 3) & ~3;
}
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index bd9d718..5d272c8 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -24,10 +24,12 @@
#include "migration/migration.h"
#include "qapi/qmp/qerror.h"
#include "qemu/event_notifier.h"
+#include "qemu/fifo8.h"
#include "sysemu/char.h"
#include <sys/mman.h>
#include <sys/types.h>
+#include <limits.h>
#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET
#define PCI_DEVICE_ID_IVSHMEM 0x1110
@@ -73,6 +75,7 @@ typedef struct IVShmemState {
CharDriverState **eventfd_chr;
CharDriverState *server_chr;
+ Fifo8 incoming_fifo;
MemoryRegion ivshmem_mmio;
/* We might need to register the BAR before we actually have the memory.
@@ -130,7 +133,7 @@ static void ivshmem_update_irq(IVShmemState *s, int val)
/* don't print ISR resets */
if (isr) {
IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
- isr ? 1 : 0, s->intrstatus, s->intrmask);
+ isr ? 1 : 0, s->intrstatus, s->intrmask);
}
pci_set_irq(d, (isr != 0));
@@ -297,8 +300,8 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *
chr = qemu_chr_open_eventfd(eventfd);
if (chr == NULL) {
- fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
- exit(-1);
+ error_report("creating eventfd for eventfd %d failed", eventfd);
+ exit(1);
}
qemu_chr_fe_claim_no_fail(chr);
@@ -325,16 +328,15 @@ static int check_shm_size(IVShmemState *s, int fd) {
struct stat buf;
if (fstat(fd, &buf) < 0) {
- fprintf(stderr, "ivshmem: exiting: fstat on fd %d failed: %s\n",
- fd, strerror(errno));
+ error_report("exiting: fstat on fd %d failed: %s",
+ fd, strerror(errno));
return -1;
}
if (s->ivshmem_size > buf.st_size) {
- fprintf(stderr,
- "IVSHMEM ERROR: Requested memory size greater"
- " than shared object size (%" PRIu64 " > %" PRIu64")\n",
- s->ivshmem_size, (uint64_t)buf.st_size);
+ error_report("Requested memory size greater"
+ " than shared object size (%" PRIu64 " > %" PRIu64")",
+ s->ivshmem_size, (uint64_t)buf.st_size);
return -1;
} else {
return 0;
@@ -387,6 +389,9 @@ static void close_guest_eventfds(IVShmemState *s, int posn)
if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
return;
}
+ if (posn < 0 || posn >= s->nb_peers) {
+ return;
+ }
guest_curr_max = s->peers[posn].nb_eventfds;
@@ -405,14 +410,24 @@ static void close_guest_eventfds(IVShmemState *s, int posn)
/* this function increase the dynamic storage need to store data about other
* guests */
-static void increase_dynamic_storage(IVShmemState *s, int new_min_size) {
+static int increase_dynamic_storage(IVShmemState *s, int new_min_size)
+{
int j, old_nb_alloc;
+ /* check for integer overflow */
+ if (new_min_size >= INT_MAX / sizeof(Peer) - 1 || new_min_size <= 0) {
+ return -1;
+ }
+
old_nb_alloc = s->nb_peers;
- while (new_min_size >= s->nb_peers)
- s->nb_peers = s->nb_peers * 2;
+ if (new_min_size >= s->nb_peers) {
+ /* +1 because #new_min_size is used as last array index */
+ s->nb_peers = new_min_size + 1;
+ } else {
+ return 0;
+ }
IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers);
s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer));
@@ -422,23 +437,57 @@ static void increase_dynamic_storage(IVShmemState *s, int new_min_size) {
s->peers[j].eventfds = NULL;
s->peers[j].nb_eventfds = 0;
}
+
+ return 0;
}
-static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
+static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
{
IVShmemState *s = opaque;
int incoming_fd, tmp_fd;
int guest_max_eventfd;
long incoming_posn;
- memcpy(&incoming_posn, buf, sizeof(long));
+ if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) {
+ memcpy(&incoming_posn, buf, size);
+ } else {
+ const uint8_t *p;
+ uint32_t num;
+
+ IVSHMEM_DPRINTF("short read of %d bytes\n", size);
+ num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo));
+ fifo8_push_all(&s->incoming_fifo, buf, num);
+ if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) {
+ return;
+ }
+ size -= num;
+ buf += num;
+ p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num);
+ g_assert(num == sizeof(incoming_posn));
+ memcpy(&incoming_posn, p, sizeof(incoming_posn));
+ if (size > 0) {
+ fifo8_push_all(&s->incoming_fifo, buf, size);
+ }
+ }
+
+ if (incoming_posn < -1) {
+ IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn);
+ return;
+ }
+
/* pick off s->server_chr->msgfd and store it, posn should accompany msg */
tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr);
IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
/* make sure we have enough space for this guest */
if (incoming_posn >= s->nb_peers) {
- increase_dynamic_storage(s, incoming_posn);
+ if (increase_dynamic_storage(s, incoming_posn) < 0) {
+ error_report("increase_dynamic_storage() failed");
+ if (tmp_fd != -1) {
+ close(tmp_fd);
+ }
+ return;
+ }
}
if (tmp_fd == -1) {
@@ -460,8 +509,8 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
incoming_fd = dup(tmp_fd);
if (incoming_fd == -1) {
- fprintf(stderr, "could not allocate file descriptor %s\n",
- strerror(errno));
+ error_report("could not allocate file descriptor %s", strerror(errno));
+ close(tmp_fd);
return;
}
@@ -473,7 +522,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
s->max_peer = 0;
if (check_shm_size(s, incoming_fd) == -1) {
- exit(-1);
+ exit(1);
}
/* mmap the region and map into the BAR2 */
@@ -484,7 +533,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
vmstate_register_ram(&s->ivshmem, DEVICE(s));
IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n",
- map_ptr, s->ivshmem_size);
+ map_ptr, s->ivshmem_size);
memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
@@ -505,7 +554,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
/* this is an eventfd for a particular guest VM */
IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
- guest_max_eventfd, incoming_fd);
+ guest_max_eventfd, incoming_fd);
event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd],
incoming_fd);
@@ -567,13 +616,13 @@ static uint64_t ivshmem_get_size(IVShmemState * s) {
value <<= 30;
break;
default:
- fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg);
+ error_report("invalid ram size: %s", s->sizearg);
exit(1);
}
/* BARs must be a power of 2 */
if (!is_power_of_two(value)) {
- fprintf(stderr, "ivshmem: size must be power of 2\n");
+ error_report("size must be power of 2");
exit(1);
}
@@ -625,7 +674,7 @@ static int ivshmem_load(QEMUFile* f, void *opaque, int version_id)
}
if (proxy->role_val == IVSHMEM_PEER) {
- fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n");
+ error_report("'peer' devices are not migratable");
return -EINVAL;
}
@@ -663,13 +712,15 @@ static int pci_ivshmem_init(PCIDevice *dev)
s->ivshmem_size = ivshmem_get_size(s);
}
+ fifo8_create(&s->incoming_fifo, sizeof(long));
+
register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load,
dev);
/* IRQFD requires MSI */
if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
!ivshmem_has_feature(s, IVSHMEM_MSI)) {
- fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n");
+ error_report("ioeventfd/irqfd requires MSI");
exit(1);
}
@@ -680,7 +731,7 @@ static int pci_ivshmem_init(PCIDevice *dev)
} else if (strncmp(s->role, "master", 7) == 0) {
s->role_val = IVSHMEM_MASTER;
} else {
- fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n");
+ error_report("'role' must be 'peer' or 'master'");
exit(1);
}
} else {
@@ -720,12 +771,12 @@ static int pci_ivshmem_init(PCIDevice *dev)
* to the ivshmem server to receive the memory region */
if (s->shmobj != NULL) {
- fprintf(stderr, "WARNING: do not specify both 'chardev' "
- "and 'shm' with ivshmem\n");
+ error_report("WARNING: do not specify both 'chardev' "
+ "and 'shm' with ivshmem");
}
IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
- s->server_chr->filename);
+ s->server_chr->filename);
if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
ivshmem_setup_msi(s);
@@ -749,7 +800,7 @@ static int pci_ivshmem_init(PCIDevice *dev)
int fd;
if (s->shmobj == NULL) {
- fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n");
+ error_report("Must specify 'chardev' or 'shm' to ivshmem");
exit(1);
}
@@ -761,18 +812,18 @@ static int pci_ivshmem_init(PCIDevice *dev)
S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
/* truncate file to length PCI device's memory */
if (ftruncate(fd, s->ivshmem_size) != 0) {
- fprintf(stderr, "ivshmem: could not truncate shared file\n");
+ error_report("could not truncate shared file");
}
} else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
- fprintf(stderr, "ivshmem: could not open shared file\n");
- exit(-1);
+ error_report("could not open shared file");
+ exit(1);
}
if (check_shm_size(s, fd) == -1) {
- exit(-1);
+ exit(1);
}
create_shared_memory_BAR(s, fd);
@@ -796,6 +847,7 @@ static void pci_ivshmem_uninit(PCIDevice *dev)
memory_region_del_subregion(&s->bar, &s->ivshmem);
vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
unregister_savevm(DEVICE(dev), "ivshmem", s);
+ fifo8_destroy(&s->incoming_fifo);
}
static Property ivshmem_properties[] = {
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
index b5e7981..fd318a1 100644
--- a/hw/misc/vfio.c
+++ b/hw/misc/vfio.c
@@ -2911,6 +2911,7 @@ static int vfio_mmap_bar(VFIODevice *vdev, VFIOBAR *bar,
}
memory_region_init_ram_ptr(submem, OBJECT(vdev), name, size, *map);
+ memory_region_set_skip_dump(submem);
} else {
empty_region:
/* Create a zero sized sub-region to make cleanup easy. */
diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c
index c110033..1da0657 100644
--- a/hw/openrisc/openrisc_sim.c
+++ b/hw/openrisc/openrisc_sim.c
@@ -72,7 +72,7 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size,
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename,
- &entry, NULL, NULL);
+ &entry, NULL, NULL, NULL, NULL);
}
if (kernel_size < 0) {
kernel_size = load_image_targphys(kernel_filename,
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 16c85ef..2157d87 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -830,7 +830,8 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
* Hrm. No ELF image? Try a uImage, maybe someone is giving us an
* ePAPR compliant kernel
*/
- kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL);
+ kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL,
+ NULL, NULL);
if (kernel_size < 0) {
fprintf(stderr, "qemu: could not load firmware '%s'\n", filename);
exit(1);
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index 81a06d3..778970a 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -253,7 +253,8 @@ static void bamboo_init(MachineState *machine)
/* Load kernel. */
if (kernel_filename) {
- success = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
+ success = load_uimage(kernel_filename, &entry, &loadaddr, NULL,
+ NULL, NULL);
if (success < 0) {
success = load_elf(kernel_filename, NULL, NULL, &elf_entry,
&elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index 36a04f3..604252a 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -31,9 +31,11 @@
#include "mfi.h"
-#define MEGASAS_VERSION "1.70"
+#define MEGASAS_VERSION_GEN1 "1.70"
+#define MEGASAS_VERSION_GEN2 "1.80"
#define MEGASAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */
#define MEGASAS_DEFAULT_FRAMES 1000 /* Windows requires this */
+#define MEGASAS_GEN2_DEFAULT_FRAMES 1008 /* Windows requires this */
#define MEGASAS_MAX_SGE 128 /* Firmware limit */
#define MEGASAS_DEFAULT_SGE 80
#define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */
@@ -91,6 +93,8 @@ typedef struct MegasasState {
int intr_mask;
int doorbell;
int busy;
+ int diag;
+ int adp_reset;
MegasasCmd *event_cmd;
int event_locale;
@@ -111,14 +115,30 @@ typedef struct MegasasState {
uint64_t producer_pa;
MegasasCmd frames[MEGASAS_MAX_FRAMES];
-
+ DECLARE_BITMAP(frame_map, MEGASAS_MAX_FRAMES);
SCSIBus bus;
} MegasasState;
-#define TYPE_MEGASAS "megasas"
+typedef struct MegasasBaseClass {
+ PCIDeviceClass parent_class;
+ const char *product_name;
+ const char *product_version;
+ int mmio_bar;
+ int ioport_bar;
+ int osts;
+} MegasasBaseClass;
+
+#define TYPE_MEGASAS_BASE "megasas-base"
+#define TYPE_MEGASAS_GEN1 "megasas"
+#define TYPE_MEGASAS_GEN2 "megasas-gen2"
#define MEGASAS(obj) \
- OBJECT_CHECK(MegasasState, (obj), TYPE_MEGASAS)
+ OBJECT_CHECK(MegasasState, (obj), TYPE_MEGASAS_BASE)
+
+#define MEGASAS_DEVICE_CLASS(oc) \
+ OBJECT_CLASS_CHECK(MegasasBaseClass, (oc), TYPE_MEGASAS_BASE)
+#define MEGASAS_DEVICE_GET_CLASS(oc) \
+ OBJECT_GET_CLASS(MegasasBaseClass, (oc), TYPE_MEGASAS_BASE)
#define MEGASAS_INTR_DISABLED_MASK 0xFFFFFFFF
@@ -443,34 +463,20 @@ static MegasasCmd *megasas_lookup_frame(MegasasState *s,
return cmd;
}
-static MegasasCmd *megasas_next_frame(MegasasState *s,
- hwaddr frame)
+static void megasas_unmap_frame(MegasasState *s, MegasasCmd *cmd)
{
- MegasasCmd *cmd = NULL;
- int num = 0, index;
+ PCIDevice *p = PCI_DEVICE(s);
- cmd = megasas_lookup_frame(s, frame);
- if (cmd) {
- trace_megasas_qf_found(cmd->index, cmd->pa);
- return cmd;
- }
- index = s->reply_queue_head;
- num = 0;
- while (num < s->fw_cmds) {
- if (!s->frames[index].pa) {
- cmd = &s->frames[index];
- break;
- }
- index = megasas_next_index(s, index, s->fw_cmds);
- num++;
- }
- if (!cmd) {
- trace_megasas_qf_failed(frame);
- }
- trace_megasas_qf_new(index, cmd);
- return cmd;
+ pci_dma_unmap(p, cmd->frame, cmd->pa_size, 0, 0);
+ cmd->frame = NULL;
+ cmd->pa = 0;
+ clear_bit(cmd->index, s->frame_map);
}
+/*
+ * This absolutely needs to be locked if
+ * qemu ever goes multithreaded.
+ */
static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
hwaddr frame, uint64_t context, int count)
{
@@ -478,37 +484,50 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
MegasasCmd *cmd = NULL;
int frame_size = MFI_FRAME_SIZE * 16;
hwaddr frame_size_p = frame_size;
+ unsigned long index;
- cmd = megasas_next_frame(s, frame);
- /* All frames busy */
- if (!cmd) {
+ index = 0;
+ while (index < s->fw_cmds) {
+ index = find_next_zero_bit(s->frame_map, s->fw_cmds, index);
+ if (!s->frames[index].pa)
+ break;
+ /* Busy frame found */
+ trace_megasas_qf_mapped(index);
+ }
+ if (index >= s->fw_cmds) {
+ /* All frames busy */
+ trace_megasas_qf_busy(frame);
return NULL;
}
- if (!cmd->pa) {
- cmd->pa = frame;
- /* Map all possible frames */
- cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0);
- if (frame_size_p != frame_size) {
- trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame);
- if (cmd->frame) {
- pci_dma_unmap(pcid, cmd->frame, frame_size_p, 0, 0);
- cmd->frame = NULL;
- cmd->pa = 0;
- }
- s->event_count++;
- return NULL;
- }
- cmd->pa_size = frame_size_p;
- cmd->context = context;
- if (!megasas_use_queue64(s)) {
- cmd->context &= (uint64_t)0xFFFFFFFF;
+ cmd = &s->frames[index];
+ set_bit(index, s->frame_map);
+ trace_megasas_qf_new(index, frame);
+
+ cmd->pa = frame;
+ /* Map all possible frames */
+ cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0);
+ if (frame_size_p != frame_size) {
+ trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame);
+ if (cmd->frame) {
+ megasas_unmap_frame(s, cmd);
}
+ s->event_count++;
+ return NULL;
+ }
+ cmd->pa_size = frame_size_p;
+ cmd->context = context;
+ if (!megasas_use_queue64(s)) {
+ cmd->context &= (uint64_t)0xFFFFFFFF;
}
cmd->count = count;
s->busy++;
+ if (s->consumer_pa) {
+ s->reply_queue_tail = ldl_le_phys(&address_space_memory,
+ s->consumer_pa);
+ }
trace_megasas_qf_enqueue(cmd->index, cmd->count, cmd->context,
- s->reply_queue_head, s->busy);
+ s->reply_queue_head, s->reply_queue_tail, s->busy);
return cmd;
}
@@ -520,39 +539,47 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context)
/* Decrement busy count */
s->busy--;
-
if (s->reply_queue_pa) {
/*
* Put command on the reply queue.
* Context is opaque, but emulation is running in
* little endian. So convert it.
*/
- tail = s->reply_queue_head;
if (megasas_use_queue64(s)) {
- queue_offset = tail * sizeof(uint64_t);
+ queue_offset = s->reply_queue_head * sizeof(uint64_t);
stq_le_phys(&address_space_memory,
s->reply_queue_pa + queue_offset, context);
} else {
- queue_offset = tail * sizeof(uint32_t);
+ queue_offset = s->reply_queue_head * sizeof(uint32_t);
stl_le_phys(&address_space_memory,
s->reply_queue_pa + queue_offset, context);
}
- s->reply_queue_head = megasas_next_index(s, tail, s->fw_cmds);
- trace_megasas_qf_complete(context, tail, queue_offset,
- s->busy, s->doorbell);
+ s->reply_queue_tail = ldl_le_phys(&address_space_memory,
+ s->consumer_pa);
+ trace_megasas_qf_complete(context, s->reply_queue_head,
+ s->reply_queue_tail, s->busy);
}
if (megasas_intr_enabled(s)) {
+ /* Update reply queue pointer */
+ s->reply_queue_tail = ldl_le_phys(&address_space_memory,
+ s->consumer_pa);
+ tail = s->reply_queue_head;
+ s->reply_queue_head = megasas_next_index(s, tail, s->fw_cmds);
+ trace_megasas_qf_update(s->reply_queue_head, s->reply_queue_tail,
+ s->busy);
+ stl_le_phys(&address_space_memory,
+ s->producer_pa, s->reply_queue_head);
/* Notify HBA */
- s->doorbell++;
- if (s->doorbell == 1) {
- if (msix_enabled(pci_dev)) {
- trace_megasas_msix_raise(0);
- msix_notify(pci_dev, 0);
- } else if (msi_enabled(pci_dev)) {
- trace_megasas_msi_raise(0);
- msi_notify(pci_dev, 0);
- } else {
+ if (msix_enabled(pci_dev)) {
+ trace_megasas_msix_raise(0);
+ msix_notify(pci_dev, 0);
+ } else if (msi_enabled(pci_dev)) {
+ trace_megasas_msi_raise(0);
+ msi_notify(pci_dev, 0);
+ } else {
+ s->doorbell++;
+ if (s->doorbell == 1) {
trace_megasas_irq_raise();
pci_irq_assert(pci_dev);
}
@@ -564,18 +591,16 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context)
static void megasas_reset_frames(MegasasState *s)
{
- PCIDevice *pcid = PCI_DEVICE(s);
int i;
MegasasCmd *cmd;
for (i = 0; i < s->fw_cmds; i++) {
cmd = &s->frames[i];
if (cmd->pa) {
- pci_dma_unmap(pcid, cmd->frame, cmd->pa_size, 0, 0);
- cmd->frame = NULL;
- cmd->pa = 0;
+ megasas_unmap_frame(s, cmd);
}
}
+ bitmap_zero(s->frame_map, MEGASAS_MAX_FRAMES);
}
static void megasas_abort_command(MegasasCmd *cmd)
@@ -590,16 +615,19 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
{
PCIDevice *pcid = PCI_DEVICE(s);
uint32_t pa_hi, pa_lo;
- hwaddr iq_pa, initq_size;
- struct mfi_init_qinfo *initq;
+ hwaddr iq_pa, initq_size = sizeof(struct mfi_init_qinfo);
+ struct mfi_init_qinfo *initq = NULL;
uint32_t flags;
int ret = MFI_STAT_OK;
+ if (s->reply_queue_pa) {
+ trace_megasas_initq_mapped(s->reply_queue_pa);
+ goto out;
+ }
pa_lo = le32_to_cpu(cmd->frame->init.qinfo_new_addr_lo);
pa_hi = le32_to_cpu(cmd->frame->init.qinfo_new_addr_hi);
iq_pa = (((uint64_t) pa_hi << 32) | pa_lo);
trace_megasas_init_firmware((uint64_t)iq_pa);
- initq_size = sizeof(*initq);
initq = pci_dma_map(pcid, iq_pa, &initq_size, 0);
if (!initq || initq_size != sizeof(*initq)) {
trace_megasas_initq_map_failed(cmd->index);
@@ -686,11 +714,12 @@ static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size)
static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
{
PCIDevice *pci_dev = PCI_DEVICE(s);
+ PCIDeviceClass *pci_class = PCI_DEVICE_GET_CLASS(pci_dev);
+ MegasasBaseClass *base_class = MEGASAS_DEVICE_GET_CLASS(s);
struct mfi_ctrl_info info;
size_t dcmd_size = sizeof(info);
BusChild *kid;
- int num_ld_disks = 0;
- uint16_t sdev_id;
+ int num_pd_disks = 0;
memset(&info, 0x0, cmd->iov_size);
if (cmd->iov_size < dcmd_size) {
@@ -699,10 +728,10 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
return MFI_STAT_INVALID_PARAMETER;
}
- info.pci.vendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC);
- info.pci.device = cpu_to_le16(PCI_DEVICE_ID_LSI_SAS1078);
- info.pci.subvendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC);
- info.pci.subdevice = cpu_to_le16(0x1013);
+ info.pci.vendor = cpu_to_le16(pci_class->vendor_id);
+ info.pci.device = cpu_to_le16(pci_class->device_id);
+ info.pci.subvendor = cpu_to_le16(pci_class->subsystem_vendor_id);
+ info.pci.subdevice = cpu_to_le16(pci_class->subsystem_id);
/*
* For some reason the firmware supports
@@ -719,20 +748,22 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
info.device.port_count = 8;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ uint16_t pd_id;
- if (num_ld_disks < 8) {
- sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
- info.device.port_addr[num_ld_disks] =
- cpu_to_le64(megasas_get_sata_addr(sdev_id));
+ if (num_pd_disks < 8) {
+ pd_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF);
+ info.device.port_addr[num_pd_disks] =
+ cpu_to_le64(megasas_get_sata_addr(pd_id));
}
- num_ld_disks++;
+ num_pd_disks++;
}
- memcpy(info.product_name, "MegaRAID SAS 8708EM2", 20);
+ memcpy(info.product_name, base_class->product_name, 24);
snprintf(info.serial_number, 32, "%s", s->hba_serial);
snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
memcpy(info.image_component[0].name, "APP", 3);
- memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9);
+ snprintf(info.image_component[0].version, 10, "%s-QEMU",
+ base_class->product_version);
memcpy(info.image_component[0].build_date, "Apr 1 2014", 11);
memcpy(info.image_component[0].build_time, "12:34:56", 8);
info.image_component_count = 1;
@@ -751,13 +782,14 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
info.max_arms = 32;
info.max_spans = 8;
info.max_arrays = MEGASAS_MAX_ARRAYS;
- info.max_lds = s->fw_luns;
+ info.max_lds = MFI_MAX_LD;
info.max_cmds = cpu_to_le16(s->fw_cmds);
info.max_sg_elements = cpu_to_le16(s->fw_sge);
info.max_request_size = cpu_to_le32(MEGASAS_MAX_SECTORS);
- info.lds_present = cpu_to_le16(num_ld_disks);
- info.pd_present = cpu_to_le16(num_ld_disks);
- info.pd_disks_present = cpu_to_le16(num_ld_disks);
+ if (!megasas_is_jbod(s))
+ info.lds_present = cpu_to_le16(num_pd_disks);
+ info.pd_present = cpu_to_le16(num_pd_disks);
+ info.pd_disks_present = cpu_to_le16(num_pd_disks);
info.hw_present = cpu_to_le32(MFI_INFO_HW_NVRAM |
MFI_INFO_HW_MEM |
MFI_INFO_HW_FLASH);
@@ -916,7 +948,6 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd)
size_t dcmd_size = sizeof(info);
BusChild *kid;
uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks;
- uint16_t sdev_id;
memset(&info, 0, dcmd_size);
offset = 8;
@@ -928,22 +959,25 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd)
}
max_pd_disks = (cmd->iov_size - offset) / sizeof(struct mfi_pd_address);
- if (max_pd_disks > s->fw_luns) {
- max_pd_disks = s->fw_luns;
+ if (max_pd_disks > MFI_MAX_SYS_PDS) {
+ max_pd_disks = MFI_MAX_SYS_PDS;
}
-
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ uint16_t pd_id;
+
+ if (num_pd_disks >= max_pd_disks)
+ break;
- sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
- info.addr[num_pd_disks].device_id = cpu_to_le16(sdev_id);
+ pd_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF);
+ info.addr[num_pd_disks].device_id = cpu_to_le16(pd_id);
info.addr[num_pd_disks].encl_device_id = 0xFFFF;
info.addr[num_pd_disks].encl_index = 0;
- info.addr[num_pd_disks].slot_number = (sdev->id & 0xFF);
+ info.addr[num_pd_disks].slot_number = sdev->id & 0xFF;
info.addr[num_pd_disks].scsi_dev_type = sdev->type;
info.addr[num_pd_disks].connect_port_bitmap = 0x1;
info.addr[num_pd_disks].sas_addr[0] =
- cpu_to_le64(megasas_get_sata_addr(sdev_id));
+ cpu_to_le64(megasas_get_sata_addr(pd_id));
num_pd_disks++;
offset += sizeof(struct mfi_pd_address);
}
@@ -978,7 +1012,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
struct mfi_pd_info *info = cmd->iov_buf;
size_t dcmd_size = sizeof(struct mfi_pd_info);
uint64_t pd_size;
- uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF);
+ uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
uint8_t cmdbuf[6];
SCSIRequest *req;
size_t len, resid;
@@ -1034,7 +1068,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
info->fw_state = cpu_to_le16(MFI_PD_STATE_OFFLINE);
}
- info->ref.v.device_id = cpu_to_le16(sdev_id);
+ info->ref.v.device_id = cpu_to_le16(pd_id);
info->state.ddf.pd_type = cpu_to_le16(MFI_PD_DDF_TYPE_IN_VD|
MFI_PD_DDF_TYPE_INTF_SAS);
blk_get_geometry(sdev->conf.blk, &pd_size);
@@ -1045,7 +1079,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
info->slot_number = (sdev->id & 0xFF);
info->path_info.count = 1;
info->path_info.sas_addr[0] =
- cpu_to_le64(megasas_get_sata_addr(sdev_id));
+ cpu_to_le64(megasas_get_sata_addr(pd_id));
info->connected_port_bitmap = 0x1;
info->device_speed = 1;
info->link_speed = 1;
@@ -1060,6 +1094,7 @@ static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd)
{
size_t dcmd_size = sizeof(struct mfi_pd_info);
uint16_t pd_id;
+ uint8_t target_id, lun_id;
SCSIDevice *sdev = NULL;
int retval = MFI_STAT_DEVICE_NOT_FOUND;
@@ -1069,7 +1104,9 @@ static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd)
/* mbox0 has the ID */
pd_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
- sdev = scsi_device_find(&s->bus, 0, pd_id, 0);
+ target_id = (pd_id >> 8) & 0xFF;
+ lun_id = pd_id & 0xFF;
+ sdev = scsi_device_find(&s->bus, 0, target_id, lun_id);
trace_megasas_dcmd_pd_get_info(cmd->index, pd_id);
if (sdev) {
@@ -1084,20 +1121,24 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
{
struct mfi_ld_list info;
size_t dcmd_size = sizeof(info), resid;
- uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns;
+ uint32_t num_ld_disks = 0, max_ld_disks;
uint64_t ld_size;
BusChild *kid;
memset(&info, 0, dcmd_size);
- if (cmd->iov_size < dcmd_size) {
+ if (cmd->iov_size > dcmd_size) {
trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
dcmd_size);
return MFI_STAT_INVALID_PARAMETER;
}
+ max_ld_disks = (cmd->iov_size - 8) / 16;
if (megasas_is_jbod(s)) {
max_ld_disks = 0;
}
+ if (max_ld_disks > MFI_MAX_LD) {
+ max_ld_disks = MFI_MAX_LD;
+ }
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
@@ -1107,7 +1148,6 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
/* Logical device size is in blocks */
blk_get_geometry(sdev->conf.blk, &ld_size);
info.ld_list[num_ld_disks].ld.v.target_id = sdev->id;
- info.ld_list[num_ld_disks].ld.v.lun_id = sdev->lun;
info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size);
num_ld_disks++;
@@ -1123,15 +1163,49 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd)
{
uint16_t flags;
+ struct mfi_ld_targetid_list info;
+ size_t dcmd_size = sizeof(info), resid;
+ uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns;
+ BusChild *kid;
/* mbox0 contains flags */
flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
trace_megasas_dcmd_ld_list_query(cmd->index, flags);
- if (flags == MR_LD_QUERY_TYPE_ALL ||
- flags == MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) {
- return megasas_dcmd_ld_get_list(s, cmd);
+ if (flags != MR_LD_QUERY_TYPE_ALL &&
+ flags != MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) {
+ max_ld_disks = 0;
}
+ memset(&info, 0, dcmd_size);
+ if (cmd->iov_size < 12) {
+ trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+ dcmd_size);
+ return MFI_STAT_INVALID_PARAMETER;
+ }
+ dcmd_size = sizeof(uint32_t) * 2 + 3;
+ max_ld_disks = cmd->iov_size - dcmd_size;
+ if (megasas_is_jbod(s)) {
+ max_ld_disks = 0;
+ }
+ if (max_ld_disks > MFI_MAX_LD) {
+ max_ld_disks = MFI_MAX_LD;
+ }
+ QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+ SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+
+ if (num_ld_disks >= max_ld_disks) {
+ break;
+ }
+ info.targetid[num_ld_disks] = sdev->lun;
+ num_ld_disks++;
+ dcmd_size++;
+ }
+ info.ld_count = cpu_to_le32(num_ld_disks);
+ info.size = dcmd_size;
+ trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks);
+
+ resid = dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
+ cmd->iov_size = dcmd_size - resid;
return MFI_STAT_OK;
}
@@ -1143,7 +1217,7 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
uint8_t cdb[6];
SCSIRequest *req;
ssize_t len, resid;
- uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF);
+ uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
uint64_t ld_size;
if (!cmd->iov_buf) {
@@ -1259,7 +1333,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
- uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
+ uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF);
struct mfi_array *array;
struct mfi_ld_config *ld;
uint64_t pd_size;
@@ -1285,7 +1359,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
array_offset += sizeof(struct mfi_array);
ld = (struct mfi_ld_config *)(data + ld_offset);
memset(ld, 0, sizeof(struct mfi_ld_config));
- ld->properties.ld.v.target_id = (sdev->id & 0xFF);
+ ld->properties.ld.v.target_id = sdev->id;
ld->properties.default_cache_policy = MR_LD_CACHE_READ_AHEAD |
MR_LD_CACHE_READ_ADAPTIVE;
ld->properties.current_cache_policy = MR_LD_CACHE_READ_AHEAD |
@@ -1347,9 +1421,23 @@ static int megasas_ctrl_shutdown(MegasasState *s, MegasasCmd *cmd)
return MFI_STAT_OK;
}
+/* Some implementations use CLUSTER RESET LD to simulate a device reset */
static int megasas_cluster_reset_ld(MegasasState *s, MegasasCmd *cmd)
{
- return MFI_STAT_INVALID_DCMD;
+ uint16_t target_id;
+ int i;
+
+ /* mbox0 contains the device index */
+ target_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
+ trace_megasas_dcmd_reset_ld(cmd->index, target_id);
+ for (i = 0; i < s->fw_cmds; i++) {
+ MegasasCmd *tmp_cmd = &s->frames[i];
+ if (tmp_cmd->req && tmp_cmd->req->dev->id == target_id) {
+ SCSIDevice *d = tmp_cmd->req->dev;
+ qdev_reset_all(&d->qdev);
+ }
+ }
+ return MFI_STAT_OK;
}
static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd)
@@ -1566,16 +1654,23 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
bool is_logical)
{
uint8_t *cdb;
- int len;
bool is_write;
struct SCSIDevice *sdev = NULL;
cdb = cmd->frame->pass.cdb;
- if (cmd->frame->header.target_id < s->fw_luns) {
- sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
- cmd->frame->header.lun_id);
+ if (is_logical) {
+ if (cmd->frame->header.target_id >= MFI_MAX_LD ||
+ cmd->frame->header.lun_id != 0) {
+ trace_megasas_scsi_target_not_present(
+ mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
+ cmd->frame->header.target_id, cmd->frame->header.lun_id);
+ return MFI_STAT_DEVICE_NOT_FOUND;
+ }
}
+ sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
+ cmd->frame->header.lun_id);
+
cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len);
trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd],
is_logical, cmd->frame->header.target_id,
@@ -1619,16 +1714,16 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
}
is_write = (cmd->req->cmd.mode == SCSI_XFER_TO_DEV);
- len = megasas_enqueue_req(cmd, is_write);
- if (len > 0) {
+ if (cmd->iov_size) {
if (is_write) {
- trace_megasas_scsi_write_start(cmd->index, len);
+ trace_megasas_scsi_write_start(cmd->index, cmd->iov_size);
} else {
- trace_megasas_scsi_read_start(cmd->index, len);
+ trace_megasas_scsi_read_start(cmd->index, cmd->iov_size);
}
} else {
trace_megasas_scsi_nodata(cmd->index);
}
+ megasas_enqueue_req(cmd, is_write);
return MFI_STAT_INVALID_STATUS;
}
@@ -1646,7 +1741,8 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi);
lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
- if (cmd->frame->header.target_id < s->fw_luns) {
+ if (cmd->frame->header.target_id < MFI_MAX_LD &&
+ cmd->frame->header.lun_id == 0) {
sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
cmd->frame->header.lun_id);
}
@@ -1797,6 +1893,7 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status,
cmd->req = NULL;
}
cmd->frame->header.cmd_status = cmd_status;
+ megasas_unmap_frame(cmd->state, cmd);
megasas_complete_frame(cmd->state, cmd->context);
}
@@ -1900,6 +1997,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
} else {
megasas_frame_set_cmd_status(frame_addr, frame_status);
}
+ megasas_unmap_frame(s, cmd);
megasas_complete_frame(s, cmd->context);
}
}
@@ -1908,38 +2006,55 @@ static uint64_t megasas_mmio_read(void *opaque, hwaddr addr,
unsigned size)
{
MegasasState *s = opaque;
+ PCIDevice *pci_dev = PCI_DEVICE(s);
+ MegasasBaseClass *base_class = MEGASAS_DEVICE_GET_CLASS(s);
uint32_t retval = 0;
switch (addr) {
case MFI_IDB:
retval = 0;
+ trace_megasas_mmio_readl("MFI_IDB", retval);
break;
case MFI_OMSG0:
case MFI_OSP0:
- retval = (megasas_use_msix(s) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) |
+ retval = (msix_present(pci_dev) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) |
(s->fw_state & MFI_FWSTATE_MASK) |
((s->fw_sge & 0xff) << 16) |
(s->fw_cmds & 0xFFFF);
+ trace_megasas_mmio_readl(addr == MFI_OMSG0 ? "MFI_OMSG0" : "MFI_OSP0",
+ retval);
break;
case MFI_OSTS:
if (megasas_intr_enabled(s) && s->doorbell) {
- retval = MFI_1078_RM | 1;
+ retval = base_class->osts;
}
+ trace_megasas_mmio_readl("MFI_OSTS", retval);
break;
case MFI_OMSK:
retval = s->intr_mask;
+ trace_megasas_mmio_readl("MFI_OMSK", retval);
break;
case MFI_ODCR0:
- retval = s->doorbell;
+ retval = s->doorbell ? 1 : 0;
+ trace_megasas_mmio_readl("MFI_ODCR0", retval);
+ break;
+ case MFI_DIAG:
+ retval = s->diag;
+ trace_megasas_mmio_readl("MFI_DIAG", retval);
+ break;
+ case MFI_OSP1:
+ retval = 15;
+ trace_megasas_mmio_readl("MFI_OSP1", retval);
break;
default:
trace_megasas_mmio_invalid_readl(addr);
break;
}
- trace_megasas_mmio_readl(addr, retval);
return retval;
}
+static int adp_reset_seq[] = {0x00, 0x04, 0x0b, 0x02, 0x07, 0x0d};
+
static void megasas_mmio_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
@@ -1949,9 +2064,9 @@ static void megasas_mmio_write(void *opaque, hwaddr addr,
uint32_t frame_count;
int i;
- trace_megasas_mmio_writel(addr, val);
switch (addr) {
case MFI_IDB:
+ trace_megasas_mmio_writel("MFI_IDB", val);
if (val & MFI_FWINIT_ABORT) {
/* Abort all pending cmds */
for (i = 0; i < s->fw_cmds; i++) {
@@ -1965,8 +2080,13 @@ static void megasas_mmio_write(void *opaque, hwaddr addr,
if (val & MFI_FWINIT_MFIMODE) {
/* discard MFIs */
}
+ if (val & MFI_FWINIT_STOP_ADP) {
+ /* Terminal error, stop processing */
+ s->fw_state = MFI_FWSTATE_FAULT;
+ }
break;
case MFI_OMSK:
+ trace_megasas_mmio_writel("MFI_OMSK", val);
s->intr_mask = val;
if (!megasas_intr_enabled(s) &&
!msi_enabled(pci_dev) &&
@@ -1984,29 +2104,34 @@ static void megasas_mmio_write(void *opaque, hwaddr addr,
}
} else {
trace_megasas_intr_disabled();
+ megasas_soft_reset(s);
}
break;
case MFI_ODCR0:
+ trace_megasas_mmio_writel("MFI_ODCR0", val);
s->doorbell = 0;
- if (s->producer_pa && megasas_intr_enabled(s)) {
- /* Update reply queue pointer */
- trace_megasas_qf_update(s->reply_queue_head, s->busy);
- stl_le_phys(&address_space_memory,
- s->producer_pa, s->reply_queue_head);
- if (!msix_enabled(pci_dev)) {
+ if (megasas_intr_enabled(s)) {
+ if (!msix_enabled(pci_dev) && !msi_enabled(pci_dev)) {
trace_megasas_irq_lower();
pci_irq_deassert(pci_dev);
}
}
break;
case MFI_IQPH:
+ trace_megasas_mmio_writel("MFI_IQPH", val);
/* Received high 32 bits of a 64 bit MFI frame address */
s->frame_hi = val;
break;
case MFI_IQPL:
+ trace_megasas_mmio_writel("MFI_IQPL", val);
/* Received low 32 bits of a 64 bit MFI frame address */
+ /* Fallthrough */
case MFI_IQP:
- /* Received 32 bit MFI frame address */
+ if (addr == MFI_IQP) {
+ trace_megasas_mmio_writel("MFI_IQP", val);
+ /* Received 64 bit MFI frame address */
+ s->frame_hi = 0;
+ }
frame_addr = (val & ~0x1F);
/* Add possible 64 bit offset */
frame_addr |= ((uint64_t)s->frame_hi << 32);
@@ -2014,6 +2139,30 @@ static void megasas_mmio_write(void *opaque, hwaddr addr,
frame_count = (val >> 1) & 0xF;
megasas_handle_frame(s, frame_addr, frame_count);
break;
+ case MFI_SEQ:
+ trace_megasas_mmio_writel("MFI_SEQ", val);
+ /* Magic sequence to start ADP reset */
+ if (adp_reset_seq[s->adp_reset] == val) {
+ s->adp_reset++;
+ } else {
+ s->adp_reset = 0;
+ s->diag = 0;
+ }
+ if (s->adp_reset == 6) {
+ s->diag = MFI_DIAG_WRITE_ENABLE;
+ }
+ break;
+ case MFI_DIAG:
+ trace_megasas_mmio_writel("MFI_DIAG", val);
+ /* ADP reset */
+ if ((s->diag & MFI_DIAG_WRITE_ENABLE) &&
+ (val & MFI_DIAG_RESET_ADP)) {
+ s->diag |= MFI_DIAG_RESET_ADP;
+ megasas_soft_reset(s);
+ s->adp_reset = 0;
+ s->diag = 0;
+ }
+ break;
default:
trace_megasas_mmio_invalid_writel(addr, val);
break;
@@ -2072,11 +2221,26 @@ static void megasas_soft_reset(MegasasState *s)
int i;
MegasasCmd *cmd;
- trace_megasas_reset();
+ trace_megasas_reset(s->fw_state);
for (i = 0; i < s->fw_cmds; i++) {
cmd = &s->frames[i];
megasas_abort_command(cmd);
}
+ if (s->fw_state == MFI_FWSTATE_READY) {
+ BusChild *kid;
+
+ /*
+ * The EFI firmware doesn't handle UA,
+ * so we need to clear the Power On/Reset UA
+ * after the initial reset.
+ */
+ QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+ SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+
+ sdev->unit_attention = SENSE_CODE(NO_SENSE);
+ scsi_device_unit_attention_reported(sdev);
+ }
+ }
megasas_reset_frames(s);
s->reply_queue_len = s->fw_cmds;
s->reply_queue_pa = 0;
@@ -2098,7 +2262,7 @@ static void megasas_scsi_reset(DeviceState *dev)
megasas_soft_reset(s);
}
-static const VMStateDescription vmstate_megasas = {
+static const VMStateDescription vmstate_megasas_gen1 = {
.name = "megasas",
.version_id = 0,
.minimum_version_id = 0,
@@ -2116,6 +2280,25 @@ static const VMStateDescription vmstate_megasas = {
}
};
+static const VMStateDescription vmstate_megasas_gen2 = {
+ .name = "megasas-gen2",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCIE_DEVICE(parent_obj, MegasasState),
+ VMSTATE_MSIX(parent_obj, MegasasState),
+
+ VMSTATE_INT32(fw_state, MegasasState),
+ VMSTATE_INT32(intr_mask, MegasasState),
+ VMSTATE_INT32(doorbell, MegasasState),
+ VMSTATE_UINT64(reply_queue_pa, MegasasState),
+ VMSTATE_UINT64(consumer_pa, MegasasState),
+ VMSTATE_UINT64(producer_pa, MegasasState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void megasas_scsi_uninit(PCIDevice *d)
{
MegasasState *s = MEGASAS(d);
@@ -2143,6 +2326,7 @@ static int megasas_scsi_init(PCIDevice *dev)
{
DeviceState *d = DEVICE(dev);
MegasasState *s = MEGASAS(dev);
+ MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s);
uint8_t *pci_conf;
int i, bar_type;
Error *err = NULL;
@@ -2166,20 +2350,25 @@ static int megasas_scsi_init(PCIDevice *dev)
s->flags &= ~MEGASAS_MASK_USE_MSI;
}
if (megasas_use_msix(s) &&
- msix_init(dev, 15, &s->mmio_io, 0, 0x2000,
- &s->mmio_io, 0, 0x3800, 0x68)) {
+ msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000,
+ &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) {
s->flags &= ~MEGASAS_MASK_USE_MSIX;
}
+ if (pci_is_express(dev)) {
+ pcie_endpoint_cap_init(dev, 0xa0);
+ }
bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64;
- pci_register_bar(dev, 0, bar_type, &s->mmio_io);
- pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io);
+ pci_register_bar(dev, b->ioport_bar,
+ PCI_BASE_ADDRESS_SPACE_IO, &s->port_io);
+ pci_register_bar(dev, b->mmio_bar, bar_type, &s->mmio_io);
pci_register_bar(dev, 3, bar_type, &s->queue_io);
if (megasas_use_msix(s)) {
msix_vector_use(dev, 0);
}
+ s->fw_state = MFI_FWSTATE_READY;
if (!s->sas_addr) {
s->sas_addr = ((NAA_LOCALLY_ASSIGNED_ID << 24) |
IEEE_COMPANY_LOCALLY_ASSIGNED) << 36;
@@ -2202,8 +2391,12 @@ static int megasas_scsi_init(PCIDevice *dev)
}
trace_megasas_init(s->fw_sge, s->fw_cmds,
megasas_is_jbod(s) ? "jbod" : "raid");
- s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ?
- MAX_SCSI_DEVS : MFI_MAX_LD;
+
+ if (megasas_is_jbod(s)) {
+ s->fw_luns = MFI_MAX_SYS_PDS;
+ } else {
+ s->fw_luns = MFI_MAX_LD;
+ }
s->producer_pa = 0;
s->consumer_pa = 0;
for (i = 0; i < s->fw_cmds; i++) {
@@ -2232,7 +2425,7 @@ megasas_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len)
msi_write_config(pci, addr, val, len);
}
-static Property megasas_properties[] = {
+static Property megasas_properties_gen1[] = {
DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge,
MEGASAS_DEFAULT_SGE),
DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds,
@@ -2248,36 +2441,119 @@ static Property megasas_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static Property megasas_properties_gen2[] = {
+ DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge,
+ MEGASAS_DEFAULT_SGE),
+ DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds,
+ MEGASAS_GEN2_DEFAULT_FRAMES),
+ DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial),
+ DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0),
+ DEFINE_PROP_BIT("use_msi", MegasasState, flags,
+ MEGASAS_FLAG_USE_MSI, true),
+ DEFINE_PROP_BIT("use_msix", MegasasState, flags,
+ MEGASAS_FLAG_USE_MSIX, true),
+ DEFINE_PROP_BIT("use_jbod", MegasasState, flags,
+ MEGASAS_FLAG_USE_JBOD, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+typedef struct MegasasInfo {
+ const char *name;
+ const char *desc;
+ const char *product_name;
+ const char *product_version;
+ uint16_t device_id;
+ uint16_t subsystem_id;
+ int ioport_bar;
+ int mmio_bar;
+ bool is_express;
+ int osts;
+ const VMStateDescription *vmsd;
+ Property *props;
+} MegasasInfo;
+
+static struct MegasasInfo megasas_devices[] = {
+ {
+ .name = TYPE_MEGASAS_GEN1,
+ .desc = "LSI MegaRAID SAS 1078",
+ .product_name = "LSI MegaRAID SAS 8708EM2",
+ .product_version = MEGASAS_VERSION_GEN1,
+ .device_id = PCI_DEVICE_ID_LSI_SAS1078,
+ .subsystem_id = 0x1013,
+ .ioport_bar = 2,
+ .mmio_bar = 0,
+ .osts = MFI_1078_RM | 1,
+ .is_express = false,
+ .vmsd = &vmstate_megasas_gen1,
+ .props = megasas_properties_gen1,
+ },{
+ .name = TYPE_MEGASAS_GEN2,
+ .desc = "LSI MegaRAID SAS 2108",
+ .product_name = "LSI MegaRAID SAS 9260-8i",
+ .product_version = MEGASAS_VERSION_GEN2,
+ .device_id = PCI_DEVICE_ID_LSI_SAS0079,
+ .subsystem_id = 0x9261,
+ .ioport_bar = 0,
+ .mmio_bar = 1,
+ .osts = MFI_GEN2_RM,
+ .is_express = true,
+ .vmsd = &vmstate_megasas_gen2,
+ .props = megasas_properties_gen2,
+ }
+};
+
static void megasas_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+ MegasasBaseClass *e = MEGASAS_DEVICE_CLASS(oc);
+ const MegasasInfo *info = data;
pc->init = megasas_scsi_init;
pc->exit = megasas_scsi_uninit;
pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
- pc->device_id = PCI_DEVICE_ID_LSI_SAS1078;
+ pc->device_id = info->device_id;
pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
- pc->subsystem_id = 0x1013;
+ pc->subsystem_id = info->subsystem_id;
pc->class_id = PCI_CLASS_STORAGE_RAID;
- dc->props = megasas_properties;
+ pc->is_express = info->is_express;
+ e->mmio_bar = info->mmio_bar;
+ e->ioport_bar = info->ioport_bar;
+ e->osts = info->osts;
+ e->product_name = info->product_name;
+ e->product_version = info->product_version;
+ dc->props = info->props;
dc->reset = megasas_scsi_reset;
- dc->vmsd = &vmstate_megasas;
+ dc->vmsd = info->vmsd;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->desc = "LSI MegaRAID SAS 1078";
+ dc->desc = info->desc;
pc->config_write = megasas_write_config;
}
static const TypeInfo megasas_info = {
- .name = TYPE_MEGASAS,
+ .name = TYPE_MEGASAS_BASE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(MegasasState),
- .class_init = megasas_class_init,
+ .class_size = sizeof(MegasasBaseClass),
+ .abstract = true,
};
static void megasas_register_types(void)
{
+ int i;
+
type_register_static(&megasas_info);
+ for (i = 0; i < ARRAY_SIZE(megasas_devices); i++) {
+ const MegasasInfo *info = &megasas_devices[i];
+ TypeInfo type_info = {};
+
+ type_info.name = info->name;
+ type_info.parent = TYPE_MEGASAS_BASE;
+ type_info.class_data = (void *)info;
+ type_info.class_init = megasas_class_init;
+
+ type_register(&type_info);
+ }
}
type_init(megasas_register_types)
diff --git a/hw/scsi/mfi.h b/hw/scsi/mfi.h
index a3034f6..29d4177 100644
--- a/hw/scsi/mfi.h
+++ b/hw/scsi/mfi.h
@@ -60,6 +60,7 @@
#define MFI_ODR0 0x9c /* outbound doorbell register0 */
#define MFI_ODCR0 0xa0 /* outbound doorbell clear register0 */
#define MFI_OSP0 0xb0 /* outbound scratch pad0 */
+#define MFI_OSP1 0xb4 /* outbound scratch pad1 */
#define MFI_IQPL 0xc0 /* Inbound queue port (low bytes) */
#define MFI_IQPH 0xc4 /* Inbound queue port (high bytes) */
#define MFI_DIAG 0xf8 /* Host diag */
@@ -116,6 +117,12 @@
#define MFI_FWINIT_STOP_ADP 0x00000020 /* Move to operational, stop */
#define MFI_FWINIT_ADP_RESET 0x00000040 /* Reset ADP */
+/*
+ * Control bits for the DIAG register
+ */
+#define MFI_DIAG_WRITE_ENABLE 0x00000080
+#define MFI_DIAG_RESET_ADP 0x00000004
+
/* MFI Commands */
typedef enum {
MFI_CMD_INIT = 0x00,
@@ -1094,7 +1101,7 @@ struct mfi_pd_list {
union mfi_ld_ref {
struct {
uint8_t target_id;
- uint8_t lun_id;
+ uint8_t reserved;
uint16_t seq;
} v;
uint32_t ref;
@@ -1111,6 +1118,13 @@ struct mfi_ld_list {
} ld_list[MFI_MAX_LD];
} QEMU_PACKED;
+struct mfi_ld_targetid_list {
+ uint32_t size;
+ uint32_t ld_count;
+ uint8_t pad[3];
+ uint8_t targetid[MFI_MAX_LD];
+} QEMU_PACKED;
+
enum mfi_ld_access {
MFI_LD_ACCESS_RW = 0,
MFI_LD_ACCSSS_RO = 2,
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index af7707c..24f7b74 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -51,14 +51,6 @@ static void scsi_device_realize(SCSIDevice *s, Error **errp)
}
}
-static void scsi_device_unrealize(SCSIDevice *s, Error **errp)
-{
- SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
- if (sc->unrealize) {
- sc->unrealize(s, errp);
- }
-}
-
int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf,
void *hba_private)
{
@@ -84,7 +76,7 @@ static SCSIRequest *scsi_device_alloc_req(SCSIDevice *s, uint32_t tag, uint32_t
return NULL;
}
-static void scsi_device_unit_attention_reported(SCSIDevice *s)
+void scsi_device_unit_attention_reported(SCSIDevice *s)
{
SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
if (sc->unit_attention_reported) {
@@ -218,7 +210,9 @@ static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp)
if (dev->vmsentry) {
qemu_del_vm_change_state_handler(dev->vmsentry);
}
- scsi_device_unrealize(dev, errp);
+
+ scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE));
+ blockdev_mark_auto_del(dev->conf.blk);
}
/* handle legacy '-drive if=scsi,...' cmd line args */
@@ -826,7 +820,7 @@ static int ata_passthrough_xfer_unit(SCSIDevice *dev, uint8_t *buf)
return xfer_unit;
}
-static int ata_passthrough_12_xfer_size(SCSIDevice *dev, uint8_t *buf)
+static int ata_passthrough_12_xfer(SCSIDevice *dev, uint8_t *buf)
{
int length = buf[2] & 0x3;
int xfer;
@@ -849,7 +843,7 @@ static int ata_passthrough_12_xfer_size(SCSIDevice *dev, uint8_t *buf)
return xfer * unit;
}
-static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf)
+static int ata_passthrough_16_xfer(SCSIDevice *dev, uint8_t *buf)
{
int extend = buf[1] & 0x1;
int length = buf[2] & 0x3;
@@ -875,16 +869,16 @@ static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf)
return xfer * unit;
}
-uint32_t scsi_data_cdb_length(uint8_t *buf)
+uint32_t scsi_data_cdb_xfer(uint8_t *buf)
{
if ((buf[0] >> 5) == 0 && buf[4] == 0) {
return 256;
} else {
- return scsi_cdb_length(buf);
+ return scsi_cdb_xfer(buf);
}
}
-uint32_t scsi_cdb_length(uint8_t *buf)
+uint32_t scsi_cdb_xfer(uint8_t *buf)
{
switch (buf[0] >> 5) {
case 0:
@@ -905,9 +899,9 @@ uint32_t scsi_cdb_length(uint8_t *buf)
}
}
-static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
{
- cmd->xfer = scsi_cdb_length(buf);
+ cmd->xfer = scsi_cdb_xfer(buf);
switch (buf[0]) {
case TEST_UNIT_READY:
case REWIND:
@@ -1038,17 +1032,17 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
/* BLANK command of MMC */
cmd->xfer = 0;
} else {
- cmd->xfer = ata_passthrough_12_xfer_size(dev, buf);
+ cmd->xfer = ata_passthrough_12_xfer(dev, buf);
}
break;
case ATA_PASSTHROUGH_16:
- cmd->xfer = ata_passthrough_16_xfer_size(dev, buf);
+ cmd->xfer = ata_passthrough_16_xfer(dev, buf);
break;
}
return 0;
}
-static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+static int scsi_req_stream_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
{
switch (buf[0]) {
/* stream commands */
@@ -1103,12 +1097,12 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu
break;
/* generic commands */
default:
- return scsi_req_length(cmd, dev, buf);
+ return scsi_req_xfer(cmd, dev, buf);
}
return 0;
}
-static int scsi_req_medium_changer_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+static int scsi_req_medium_changer_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
{
switch (buf[0]) {
/* medium changer commands */
@@ -1125,7 +1119,7 @@ static int scsi_req_medium_changer_length(SCSICommand *cmd, SCSIDevice *dev, uin
/* generic commands */
default:
- return scsi_req_length(cmd, dev, buf);
+ return scsi_req_xfer(cmd, dev, buf);
}
return 0;
}
@@ -1214,38 +1208,45 @@ static uint64_t scsi_cmd_lba(SCSICommand *cmd)
return lba;
}
-int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf)
-{
- int rc;
+int scsi_cdb_length(uint8_t *buf) {
+ int cdb_len;
- cmd->lba = -1;
switch (buf[0] >> 5) {
case 0:
- cmd->len = 6;
+ cdb_len = 6;
break;
case 1:
case 2:
- cmd->len = 10;
+ cdb_len = 10;
break;
case 4:
- cmd->len = 16;
+ cdb_len = 16;
break;
case 5:
- cmd->len = 12;
+ cdb_len = 12;
break;
default:
- return -1;
+ cdb_len = -1;
}
+ return cdb_len;
+}
+
+int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf)
+{
+ int rc;
+
+ cmd->lba = -1;
+ cmd->len = scsi_cdb_length(buf);
switch (dev->type) {
case TYPE_TAPE:
- rc = scsi_req_stream_length(cmd, dev, buf);
+ rc = scsi_req_stream_xfer(cmd, dev, buf);
break;
case TYPE_MEDIUM_CHANGER:
- rc = scsi_req_medium_changer_length(cmd, dev, buf);
+ rc = scsi_req_medium_changer_xfer(cmd, dev, buf);
break;
default:
- rc = scsi_req_length(cmd, dev, buf);
+ rc = scsi_req_xfer(cmd, dev, buf);
break;
}
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 010eefd..2f75d7d 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1666,7 +1666,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
{
SCSIRequest *req = &r->req;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
- uint32_t nb_sectors = scsi_data_cdb_length(r->req.cmd.buf);
+ uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf);
WriteSameCBData *data;
uint8_t *buf;
int i;
@@ -2061,7 +2061,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
return 0;
}
- len = scsi_data_cdb_length(r->req.cmd.buf);
+ len = scsi_data_cdb_xfer(r->req.cmd.buf);
switch (command) {
case READ_6:
case READ_10:
@@ -2138,14 +2138,6 @@ static void scsi_disk_reset(DeviceState *dev)
s->tray_open = 0;
}
-static void scsi_unrealize(SCSIDevice *dev, Error **errp)
-{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
-
- scsi_device_purge_requests(&s->qdev, SENSE_CODE(NO_SENSE));
- blockdev_mark_auto_del(s->qdev.conf.blk);
-}
-
static void scsi_disk_resize_cb(void *opaque)
{
SCSIDiskState *s = opaque;
@@ -2396,7 +2388,7 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
{
int i;
- for (i = 1; i < req->cmd.len; i++) {
+ for (i = 1; i < scsi_cdb_length(buf); i++) {
printf(" 0x%02x", buf[i]);
}
printf("\n");
@@ -2612,7 +2604,6 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
sc->realize = scsi_hd_realize;
- sc->unrealize = scsi_unrealize;
sc->alloc_req = scsi_new_request;
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
dc->fw_name = "disk";
@@ -2643,7 +2634,6 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
sc->realize = scsi_cd_realize;
- sc->unrealize = scsi_unrealize;
sc->alloc_req = scsi_new_request;
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
dc->fw_name = "disk";
@@ -2672,7 +2662,6 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data)
SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
sc->realize = scsi_block_realize;
- sc->unrealize = scsi_unrealize;
sc->alloc_req = scsi_block_new_request;
sc->parse_cdb = scsi_block_parse_cdb;
dc->fw_name = "disk";
@@ -2710,7 +2699,6 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data)
SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
sc->realize = scsi_disk_realize;
- sc->unrealize = scsi_unrealize;
sc->alloc_req = scsi_new_request;
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
dc->fw_name = "disk";
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index f2e53af..6b9e4e1 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -368,12 +368,6 @@ static void scsi_generic_reset(DeviceState *dev)
scsi_device_purge_requests(s, SENSE_CODE(RESET));
}
-static void scsi_unrealize(SCSIDevice *s, Error **errp)
-{
- scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE));
- blockdev_mark_auto_del(s->conf.blk);
-}
-
static void scsi_generic_realize(SCSIDevice *s, Error **errp)
{
int rc;
@@ -478,7 +472,6 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
sc->realize = scsi_generic_realize;
- sc->unrealize = scsi_unrealize;
sc->alloc_req = scsi_new_request;
sc->parse_cdb = scsi_generic_parse_cdb;
dc->fw_name = "disk";
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index 3097d54..9651e6f 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -241,9 +241,10 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s)
}
}
- aio_context_release(s->ctx);
s->dataplane_starting = false;
s->dataplane_started = true;
+ aio_context_release(s->ctx);
+ return;
fail_vrings:
virtio_scsi_clear_aio(s);
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 7d40ecc..fdcacfd 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -804,10 +804,12 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp,
virtio_init(vdev, "virtio-scsi", VIRTIO_ID_SCSI,
sizeof(VirtIOSCSIConfig));
- if (s->conf.num_queues <= 0 || s->conf.num_queues > VIRTIO_PCI_QUEUE_MAX) {
- error_setg(errp, "Invalid number of queues (= %" PRId32 "), "
+ if (s->conf.num_queues == 0 ||
+ s->conf.num_queues > VIRTIO_PCI_QUEUE_MAX - 2) {
+ error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
"must be a positive integer less than %d.",
- s->conf.num_queues, VIRTIO_PCI_QUEUE_MAX);
+ s->conf.num_queues, VIRTIO_PCI_QUEUE_MAX - 2);
+ virtio_cleanup(vdev);
return;
}
s->cmd_vqs = g_malloc0(s->conf.num_queues * sizeof(VirtQueue *));
diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c
index eeb4922..a059a20 100644
--- a/hw/tricore/tricore_testboard.c
+++ b/hw/tricore/tricore_testboard.c
@@ -71,11 +71,11 @@ static void tricore_testboard_init(MachineState *machine, int board_id)
machine->cpu_model = "tc1796";
}
cpu = cpu_tricore_init(machine->cpu_model);
- env = &cpu->env;
if (!cpu) {
error_report("Unable to find CPU definition");
exit(1);
}
+ env = &cpu->env;
memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024, &error_abort);
vmstate_register_ram_global(ext_cram);
memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024, &error_abort);
diff --git a/hw/xtensa/pic_cpu.c b/hw/xtensa/pic_cpu.c
index e2005bd..18825d1 100644
--- a/hw/xtensa/pic_cpu.c
+++ b/hw/xtensa/pic_cpu.c
@@ -31,14 +31,14 @@
void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d)
{
- uint32_t old_ccount = env->sregs[CCOUNT];
+ uint32_t old_ccount = env->sregs[CCOUNT] + 1;
env->sregs[CCOUNT] += d;
if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
int i;
for (i = 0; i < env->config->nccompare; ++i) {
- if (env->sregs[CCOMPARE + i] - old_ccount <= d) {
+ if (env->sregs[CCOMPARE + i] - old_ccount < d) {
xtensa_timer_irq(env, i, 1);
}
}
diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c
index 0100c31..e5a6bba 100644
--- a/hw/xtensa/xtfpga.c
+++ b/hw/xtensa/xtfpga.c
@@ -328,7 +328,8 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
} else {
hwaddr ep;
int is_linux;
- success = load_uimage(kernel_filename, &ep, NULL, &is_linux);
+ success = load_uimage(kernel_filename, &ep, NULL, &is_linux,
+ translate_phys_addr, cpu);
if (success > 0 && is_linux) {
entry_point = ep;
} else {
diff --git a/include/block/block.h b/include/block/block.h
index 341054d..13e4537 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -268,7 +268,13 @@ typedef enum {
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
-int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts);
+/* The units of offset and total_work_size may be chosen arbitrarily by the
+ * block driver; total_work_size may change during the course of the amendment
+ * operation */
+typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset,
+ int64_t total_work_size);
+int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
+ BlockDriverAmendStatusCB *status_cb);
/* external snapshots */
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
@@ -328,6 +334,7 @@ int bdrv_flush(BlockDriverState *bs);
int coroutine_fn bdrv_co_flush(BlockDriverState *bs);
int bdrv_flush_all(void);
void bdrv_close_all(void);
+void bdrv_drain(BlockDriverState *bs);
void bdrv_drain_all(void);
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
@@ -498,6 +505,8 @@ typedef enum {
BLKDBG_PWRITEV_ZERO,
BLKDBG_PWRITEV_DONE,
+ BLKDBG_EMPTY_IMAGE_PREPARE,
+
BLKDBG_EVENT_MAX,
} BlkDebugEvent;
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 8898c6c..a1c17b9 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -232,7 +232,8 @@ struct BlockDriver {
int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result,
BdrvCheckMode fix);
- int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts);
+ int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
+ BlockDriverAmendStatusCB *status_cb);
void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
@@ -289,6 +290,9 @@ typedef struct BlockLimits {
/* optimal transfer length in sectors */
int opt_transfer_length;
+ /* maximal transfer length in sectors */
+ int max_transfer_length;
+
/* memory alignment so that no bounce buffer is needed */
size_t opt_mem_alignment;
} BlockLimits;
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index acb399f..b6d4ebb 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -91,6 +91,11 @@ struct BlockJob {
*/
bool busy;
+ /**
+ * Set to true when the job is ready to be completed.
+ */
+ bool ready;
+
/** Status that is published by the query-block-jobs QMP API */
BlockDeviceIoStatus iostatus;
@@ -273,6 +278,21 @@ bool block_job_is_paused(BlockJob *job);
int block_job_cancel_sync(BlockJob *job);
/**
+ * block_job_complete_sync:
+ * @job: The job to be completed.
+ * @errp: Error object which may be set by block_job_complete(); this is not
+ * necessarily set on every error, the job return value has to be
+ * checked as well.
+ *
+ * Synchronously complete the job. The completion callback is called before the
+ * function returns, unless it is NULL (which is permissible when using this
+ * function).
+ *
+ * Returns the return value from the job.
+ */
+int block_job_complete_sync(BlockJob *job, Error **errp);
+
+/**
* block_job_iostatus_reset:
* @job: The job whose I/O status should be reset.
*
@@ -295,4 +315,23 @@ void block_job_iostatus_reset(BlockJob *job);
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
BlockdevOnError on_err,
int is_read, int error);
+
+typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
+
+/**
+ * block_job_defer_to_main_loop:
+ * @job: The job
+ * @fn: The function to run in the main loop
+ * @opaque: The opaque value that is passed to @fn
+ *
+ * Execute a given function in the main loop with the BlockDriverState
+ * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and
+ * anything that uses bdrv_drain_all() in the main loop.
+ *
+ * The @job AioContext is held while @fn executes.
+ */
+void block_job_defer_to_main_loop(BlockJob *job,
+ BlockJobDeferToMainLoopFn *fn,
+ void *opaque);
+
#endif
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 072aad2..74a58b4 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -150,6 +150,7 @@ struct MemoryRegion {
bool terminates;
bool romd_mode;
bool ram;
+ bool skip_dump;
bool readonly; /* For RAM regions */
bool enabled;
bool rom_device;
@@ -457,6 +458,24 @@ uint64_t memory_region_size(MemoryRegion *mr);
bool memory_region_is_ram(MemoryRegion *mr);
/**
+ * memory_region_is_skip_dump: check whether a memory region should not be
+ * dumped
+ *
+ * Returns %true is a memory region should not be dumped(e.g. VFIO BAR MMAP).
+ *
+ * @mr: the memory region being queried
+ */
+bool memory_region_is_skip_dump(MemoryRegion *mr);
+
+/**
+ * memory_region_set_skip_dump: Set skip_dump flag, dump will ignore this memory
+ * region
+ *
+ * @mr: the memory region being queried
+ */
+void memory_region_set_skip_dump(MemoryRegion *mr);
+
+/**
* memory_region_is_romd: check whether a memory region is in ROMD mode
*
* Returns %true if a memory region is a ROM device and currently set to allow
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index c4ee520..2545268 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -35,11 +35,13 @@ struct PCMachineState {
HotplugHandler *acpi_dev;
uint64_t max_ram_below_4g;
+ bool vmport;
};
#define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device"
#define PC_MACHINE_MEMHP_REGION_SIZE "hotplug-memory-region-size"
#define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g"
+#define PC_MACHINE_VMPORT "vmport"
/**
* PCMachineClass:
diff --git a/include/hw/loader.h b/include/hw/loader.h
index 9190387..054c6a2 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -30,7 +30,9 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
int load_aout(const char *filename, hwaddr addr, int max_sz,
int bswap_needed, hwaddr target_page_size);
int load_uimage(const char *filename, hwaddr *ep,
- hwaddr *loadaddr, int *is_linux);
+ hwaddr *loadaddr, int *is_linux,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque);
/**
* load_ramdisk:
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index e597070..321d622 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -56,6 +56,7 @@
#define PCI_DEVICE_ID_LSI_53C810 0x0001
#define PCI_DEVICE_ID_LSI_53C895A 0x0012
#define PCI_DEVICE_ID_LSI_SAS1078 0x0060
+#define PCI_DEVICE_ID_LSI_SAS0079 0x0079
#define PCI_VENDOR_ID_DEC 0x1011
#define PCI_DEVICE_ID_DEC_21154 0x0026
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index 6bc841a..cdaf0f8 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -84,7 +84,6 @@ struct SCSIRequest {
typedef struct SCSIDeviceClass {
DeviceClass parent_class;
void (*realize)(SCSIDevice *dev, Error **errp);
- void (*unrealize)(SCSIDevice *dev, Error **errp);
int (*parse_cdb)(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf,
void *hba_private);
SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun,
@@ -239,8 +238,9 @@ extern const struct SCSISense sense_code_SPACE_ALLOC_FAILED;
#define SENSE_CODE(x) sense_code_ ## x
-uint32_t scsi_data_cdb_length(uint8_t *buf);
-uint32_t scsi_cdb_length(uint8_t *buf);
+uint32_t scsi_data_cdb_xfer(uint8_t *buf);
+uint32_t scsi_cdb_xfer(uint8_t *buf);
+int scsi_cdb_length(uint8_t *buf);
int scsi_sense_valid(SCSISense sense);
int scsi_build_sense(uint8_t *in_buf, int in_len,
uint8_t *buf, int len, bool fixed);
@@ -271,6 +271,7 @@ void scsi_req_retry(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense);
void scsi_device_report_change(SCSIDevice *dev, SCSISense sense);
+void scsi_device_unit_attention_reported(SCSIDevice *dev);
int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
diff --git a/include/qemu-common.h b/include/qemu-common.h
index b87e9c2..f862214 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -357,7 +357,6 @@ char *qemu_find_file(int type, const char *name);
void os_setup_early_signal_handling(void);
char *os_find_datadir(void);
void os_parse_cmd_args(int index, const char *optarg);
-void os_pidfile_error(void);
/* Convert a byte between binary and BCD. */
static inline uint8_t to_bcd(uint8_t val)
diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h
index edf4f17..f0273c9 100644
--- a/include/qemu/bitmap.h
+++ b/include/qemu/bitmap.h
@@ -12,7 +12,11 @@
#ifndef BITMAP_H
#define BITMAP_H
-#include "qemu-common.h"
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "qemu/osdep.h"
#include "qemu/bitops.h"
/*
diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index 7e2d5c9..181bd46 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -12,7 +12,9 @@
#ifndef BITOPS_H
#define BITOPS_H
-#include "qemu-common.h"
+#include <stdint.h>
+#include <assert.h>
+
#include "host-utils.h"
#define BITS_PER_BYTE CHAR_BIT
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index 1565404..c032434 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -68,6 +68,12 @@ typedef signed int int_fast16_t;
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
+/* Minimum function that returns zero only iff both values are zero.
+ * Intended for use with unsigned values only. */
+#ifndef MIN_NON_ZERO
+#define MIN_NON_ZERO(a, b) (((a) != 0 && (a) < (b)) ? (a) : (b))
+#endif
+
#ifndef ROUND_UP
#define ROUND_UP(n,d) (((n) + (d) - 1) & -(d))
#endif
diff --git a/memory.c b/memory.c
index 30f77b2..0f4fdc7 100644
--- a/memory.c
+++ b/memory.c
@@ -1185,6 +1185,11 @@ void memory_region_init_ram_ptr(MemoryRegion *mr,
mr->ram_addr = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_abort);
}
+void memory_region_set_skip_dump(MemoryRegion *mr)
+{
+ mr->skip_dump = true;
+}
+
void memory_region_init_alias(MemoryRegion *mr,
Object *owner,
const char *name,
@@ -1306,6 +1311,11 @@ bool memory_region_is_ram(MemoryRegion *mr)
return mr->ram;
}
+bool memory_region_is_skip_dump(MemoryRegion *mr)
+{
+ return mr->skip_dump;
+}
+
bool memory_region_is_logging(MemoryRegion *mr)
{
return mr->dirty_log_mask;
diff --git a/memory_mapping.c b/memory_mapping.c
index 87a6ed5..7b69801 100644
--- a/memory_mapping.c
+++ b/memory_mapping.c
@@ -203,7 +203,8 @@ static void guest_phys_blocks_region_add(MemoryListener *listener,
GuestPhysBlock *predecessor;
/* we only care about RAM */
- if (!memory_region_is_ram(section->mr)) {
+ if (!memory_region_is_ram(section->mr) ||
+ memory_region_is_skip_dump(section->mr)) {
return;
}
diff --git a/net/slirp.c b/net/slirp.c
index c171119..920af30 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -549,8 +549,8 @@ static int slirp_smb(SlirpState* s, const char *exported_dir,
);
fclose(f);
- snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
- CONFIG_SMBD_COMMAND, smb_conf);
+ snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -l %s -s %s",
+ CONFIG_SMBD_COMMAND, s->smb_dir, smb_conf);
if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0 ||
slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 445) < 0) {
diff --git a/net/tap.c b/net/tap.c
index a40f7f0..bde6b58 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -598,7 +598,6 @@ static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
s = net_tap_fd_init(peer, model, name, fd, vnet_hdr);
if (!s) {
- close(fd);
return -1;
}
@@ -797,6 +796,7 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
if (net_init_tap_one(tap, peer, "bridge", name, ifname,
script, downscript, vhostfdname,
vnet_hdr, fd)) {
+ close(fd);
return -1;
}
} else {
@@ -824,6 +824,7 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
if (queues > 1 && i == 0 && !tap->has_ifname) {
if (tap_fd_get_ifname(fd, ifname)) {
error_report("Fail to get ifname");
+ close(fd);
return -1;
}
}
@@ -832,6 +833,7 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
i >= 1 ? "no" : script,
i >= 1 ? "no" : downscript,
vhostfdname, vnet_hdr, fd)) {
+ close(fd);
return -1;
}
}
diff --git a/net/tap_int.h b/net/tap_int.h
index 86bb224..79afdf2 100644
--- a/net/tap_int.h
+++ b/net/tap_int.h
@@ -29,9 +29,6 @@
#include "qemu-common.h"
#include "qapi-types.h"
-#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
-#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
-
int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
int vnet_hdr_required, int mq_required);
diff --git a/os-posix.c b/os-posix.c
index 4898ebf..ba091f1 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -47,7 +47,7 @@
static struct passwd *user_pwd;
static const char *chroot_dir;
static int daemonize;
-static int fds[2];
+static int daemon_pipe;
void os_setup_early_signal_handling(void)
{
@@ -205,6 +205,7 @@ void os_daemonize(void)
{
if (daemonize) {
pid_t pid;
+ int fds[2];
if (pipe(fds) == -1) {
exit(1);
@@ -217,26 +218,21 @@ void os_daemonize(void)
close(fds[1]);
- again:
- len = read(fds[0], &status, 1);
- if (len == -1 && (errno == EINTR)) {
- goto again;
- }
- if (len != 1) {
- exit(1);
- }
- else if (status == 1) {
- fprintf(stderr, "Could not acquire pidfile\n");
- exit(1);
- } else {
- exit(0);
- }
- } else if (pid < 0) {
- exit(1);
- }
+ do {
+ len = read(fds[0], &status, 1);
+ } while (len < 0 && errno == EINTR);
+
+ /* only exit successfully if our child actually wrote
+ * a one-byte zero to our pipe, upon successful init */
+ exit(len == 1 && status == 0 ? 0 : 1);
+
+ } else if (pid < 0) {
+ exit(1);
+ }
close(fds[0]);
- qemu_set_cloexec(fds[1]);
+ daemon_pipe = fds[1];
+ qemu_set_cloexec(daemon_pipe);
setsid();
@@ -259,17 +255,6 @@ void os_setup_post(void)
int fd = 0;
if (daemonize) {
- uint8_t status = 0;
- ssize_t len;
-
- again1:
- len = write(fds[1], &status, 1);
- if (len == -1 && (errno == EINTR)) {
- goto again1;
- }
- if (len != 1) {
- exit(1);
- }
if (chdir("/")) {
perror("not able to chdir to /");
exit(1);
@@ -284,23 +269,22 @@ void os_setup_post(void)
change_process_uid();
if (daemonize) {
+ uint8_t status = 0;
+ ssize_t len;
+
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
- }
-}
-void os_pidfile_error(void)
-{
- if (daemonize) {
- uint8_t status = 1;
- if (write(fds[1], &status, 1) != 1) {
- perror("daemonize. Writing to pipe\n");
+ do {
+ len = write(daemon_pipe, &status, 1);
+ } while (len < 0 && errno == EINTR);
+ if (len != 1) {
+ exit(1);
}
- } else
- fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno));
+ }
}
void os_set_line_buffering(void)
@@ -319,8 +303,6 @@ int qemu_create_pidfile(const char *filename)
return -1;
}
if (lockf(fd, F_TLOCK, 0) == -1) {
- fprintf(stderr, "lock file '%s' failed: %s\n",
- filename, strerror(errno));
close(fd);
return -1;
}
diff --git a/os-win32.c b/os-win32.c
index 5f95caa..c0daf8e 100644
--- a/os-win32.c
+++ b/os-win32.c
@@ -104,11 +104,6 @@ void os_parse_cmd_args(int index, const char *optarg)
return;
}
-void os_pidfile_error(void)
-{
- fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno));
-}
-
int qemu_create_pidfile(const char *filename)
{
char buffer[128];
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 8f7089e..77a0cfb 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -514,12 +514,14 @@
#
# @io-status: the status of the job (since 1.3)
#
+# @ready: true if the job may be completed (since 2.2)
+#
# Since: 1.1
##
{ 'type': 'BlockJobInfo',
'data': {'type': 'str', 'device': 'str', 'len': 'int',
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
- 'io-status': 'BlockDeviceIoStatus'} }
+ 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} }
##
# @query-block-jobs:
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 55aec6b..9567774 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -22,9 +22,9 @@ STEXI
ETEXI
DEF("commit", img_commit,
- "commit [-q] [-f fmt] [-t cache] filename")
+ "commit [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename")
STEXI
-@item commit [-q] [-f @var{fmt}] [-t @var{cache}] @var{filename}
+@item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename}
ETEXI
DEF("compare", img_compare,
@@ -70,8 +70,8 @@ STEXI
ETEXI
DEF("amend", img_amend,
- "amend [-q] [-f fmt] [-t cache] -o options filename")
+ "amend [-p] [-q] [-f fmt] [-t cache] -o options filename")
STEXI
-@item amend [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
+@item amend [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
@end table
ETEXI
diff --git a/qemu-img.c b/qemu-img.c
index 731502c..66a7eb4 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -31,6 +31,7 @@
#include "sysemu/sysemu.h"
#include "sysemu/block-backend.h"
#include "block/block_int.h"
+#include "block/blockjob.h"
#include "block/qapi.h"
#include <getopt.h>
@@ -722,18 +723,54 @@ fail:
return ret;
}
+typedef struct CommonBlockJobCBInfo {
+ BlockDriverState *bs;
+ Error **errp;
+} CommonBlockJobCBInfo;
+
+static void common_block_job_cb(void *opaque, int ret)
+{
+ CommonBlockJobCBInfo *cbi = opaque;
+
+ if (ret < 0) {
+ error_setg_errno(cbi->errp, -ret, "Block job failed");
+ }
+
+ /* Drop this block job's reference */
+ bdrv_unref(cbi->bs);
+}
+
+static void run_block_job(BlockJob *job, Error **errp)
+{
+ AioContext *aio_context = bdrv_get_aio_context(job->bs);
+
+ do {
+ aio_poll(aio_context, true);
+ qemu_progress_print((float)job->offset / job->len * 100.f, 0);
+ } while (!job->ready);
+
+ block_job_complete_sync(job, errp);
+
+ /* A block job may finish instantaneously without publishing any progress,
+ * so just signal completion here */
+ qemu_progress_print(100.f, 0);
+}
+
static int img_commit(int argc, char **argv)
{
int c, ret, flags;
- const char *filename, *fmt, *cache;
+ const char *filename, *fmt, *cache, *base;
BlockBackend *blk;
- BlockDriverState *bs;
- bool quiet = false;
+ BlockDriverState *bs, *base_bs;
+ bool progress = false, quiet = false, drop = false;
+ Error *local_err = NULL;
+ CommonBlockJobCBInfo cbi;
fmt = NULL;
cache = BDRV_DEFAULT_CACHE;
+ base = NULL;
for(;;) {
- c = getopt(argc, argv, "f:ht:q");
+ c = getopt(argc, argv, "f:ht:b:dpq");
if (c == -1) {
break;
}
@@ -748,17 +785,34 @@ static int img_commit(int argc, char **argv)
case 't':
cache = optarg;
break;
+ case 'b':
+ base = optarg;
+ /* -b implies -d */
+ drop = true;
+ break;
+ case 'd':
+ drop = true;
+ break;
+ case 'p':
+ progress = true;
+ break;
case 'q':
quiet = true;
break;
}
}
+
+ /* Progress is not shown in Quiet mode */
+ if (quiet) {
+ progress = false;
+ }
+
if (optind != argc - 1) {
error_exit("Expecting one image file name");
}
filename = argv[optind++];
- flags = BDRV_O_RDWR;
+ flags = BDRV_O_RDWR | BDRV_O_UNMAP;
ret = bdrv_parse_cache_flags(cache, &flags);
if (ret < 0) {
error_report("Invalid cache option: %s", cache);
@@ -771,29 +825,76 @@ static int img_commit(int argc, char **argv)
}
bs = blk_bs(blk);
- ret = bdrv_commit(bs);
- switch(ret) {
- case 0:
- qprintf(quiet, "Image committed.\n");
- break;
- case -ENOENT:
- error_report("No disk inserted");
- break;
- case -EACCES:
- error_report("Image is read-only");
- break;
- case -ENOTSUP:
- error_report("Image is already committed");
- break;
- default:
- error_report("Error while committing image");
- break;
+ qemu_progress_init(progress, 1.f);
+ qemu_progress_print(0.f, 100);
+
+ if (base) {
+ base_bs = bdrv_find_backing_image(bs, base);
+ if (!base_bs) {
+ error_set(&local_err, QERR_BASE_NOT_FOUND, base);
+ goto done;
+ }
+ } else {
+ /* This is different from QMP, which by default uses the deepest file in
+ * the backing chain (i.e., the very base); however, the traditional
+ * behavior of qemu-img commit is using the immediate backing file. */
+ base_bs = bs->backing_hd;
+ if (!base_bs) {
+ error_setg(&local_err, "Image does not have a backing file");
+ goto done;
+ }
}
+ cbi = (CommonBlockJobCBInfo){
+ .errp = &local_err,
+ .bs = bs,
+ };
+
+ commit_active_start(bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT,
+ common_block_job_cb, &cbi, &local_err);
+ if (local_err) {
+ goto done;
+ }
+
+ /* The block job will swap base_bs and bs (which is not what we really want
+ * here, but okay) and unref base_bs (after the swap, i.e., the old top
+ * image). In order to still be able to empty that top image afterwards,
+ * increment the reference counter here preemptively. */
+ if (!drop) {
+ bdrv_ref(base_bs);
+ }
+
+ run_block_job(bs->job, &local_err);
+ if (local_err) {
+ goto unref_backing;
+ }
+
+ if (!drop && base_bs->drv->bdrv_make_empty) {
+ ret = base_bs->drv->bdrv_make_empty(base_bs);
+ if (ret) {
+ error_setg_errno(&local_err, -ret, "Could not empty %s",
+ filename);
+ goto unref_backing;
+ }
+ }
+
+unref_backing:
+ if (!drop) {
+ bdrv_unref(base_bs);
+ }
+
+done:
+ qemu_progress_end();
+
blk_unref(blk);
- if (ret) {
+
+ if (local_err) {
+ qerror_report_err(local_err);
+ error_free(local_err);
return 1;
}
+
+ qprintf(quiet, "Image committed.\n");
return 0;
}
@@ -2770,6 +2871,12 @@ out:
return 0;
}
+static void amend_status_cb(BlockDriverState *bs,
+ int64_t offset, int64_t total_work_size)
+{
+ qemu_progress_print(100.f * offset / total_work_size, 0);
+}
+
static int img_amend(int argc, char **argv)
{
int c, ret = 0;
@@ -2778,13 +2885,13 @@ static int img_amend(int argc, char **argv)
QemuOpts *opts = NULL;
const char *fmt = NULL, *filename, *cache;
int flags;
- bool quiet = false;
+ bool quiet = false, progress = false;
BlockBackend *blk = NULL;
BlockDriverState *bs = NULL;
cache = BDRV_DEFAULT_CACHE;
for (;;) {
- c = getopt(argc, argv, "ho:f:t:q");
+ c = getopt(argc, argv, "ho:f:t:pq");
if (c == -1) {
break;
}
@@ -2814,6 +2921,9 @@ static int img_amend(int argc, char **argv)
case 't':
cache = optarg;
break;
+ case 'p':
+ progress = true;
+ break;
case 'q':
quiet = true;
break;
@@ -2824,6 +2934,11 @@ static int img_amend(int argc, char **argv)
error_exit("Must specify options (-o)");
}
+ if (quiet) {
+ progress = false;
+ }
+ qemu_progress_init(progress, 1.0);
+
filename = (optind == argc - 1) ? argv[argc - 1] : NULL;
if (fmt && has_help_option(options)) {
/* If a format is explicitly specified (and possibly no filename is
@@ -2833,7 +2948,9 @@ static int img_amend(int argc, char **argv)
}
if (optind != argc - 1) {
- error_exit("Expecting one image file name");
+ error_report("Expecting one image file name");
+ ret = -1;
+ goto out;
}
flags = BDRV_O_FLAGS | BDRV_O_RDWR;
@@ -2867,13 +2984,18 @@ static int img_amend(int argc, char **argv)
goto out;
}
- ret = bdrv_amend_options(bs, opts);
+ /* In case the driver does not call amend_status_cb() */
+ qemu_progress_print(0.f, 0);
+ ret = bdrv_amend_options(bs, opts, &amend_status_cb);
+ qemu_progress_print(100.f, 0);
if (ret < 0) {
error_report("Error while amending options: %s", strerror(-ret));
goto out;
}
out:
+ qemu_progress_end();
+
blk_unref(blk);
qemu_opts_del(opts);
qemu_opts_free(create_opts);
diff --git a/qemu-img.texi b/qemu-img.texi
index e3ef4a0..0a1ab35 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -167,7 +167,7 @@ this case. @var{backing_file} will never be modified unless you use the
The size can also be specified using the @var{size} option with @code{-o},
it doesn't need to be specified separately in this case.
-@item commit [-f @var{fmt}] [-t @var{cache}] @var{filename}
+@item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename}
Commit the changes recorded in @var{filename} in its base image or backing file.
If the backing file is smaller than the snapshot, then the backing file will be
@@ -176,6 +176,20 @@ the backing file, the backing file will not be truncated. If you want the
backing file to match the size of the smaller snapshot, you can safely truncate
it yourself once the commit operation successfully completes.
+The image @var{filename} is emptied after the operation has succeeded. If you do
+not need @var{filename} afterwards and intend to drop it, you may skip emptying
+@var{filename} by specifying the @code{-d} flag.
+
+If the backing chain of the given image file @var{filename} has more than one
+layer, the backing file into which the changes will be committed may be
+specified as @var{base} (which has to be part of @var{filename}'s backing
+chain). If @var{base} is not specified, the immediate backing file of the top
+image (which is @var{filename}) will be used. For reasons of consistency,
+explicitly specifying @var{base} will always imply @code{-d} (since emptying an
+image after committing to an indirect backing file would lead to different data
+being read from the image due to content in the intermediate backing chain
+overruling the commit target).
+
@item compare [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-s] [-q] @var{filename1} @var{filename2}
Check if two images have the same content. You can compare images with
@@ -398,7 +412,7 @@ After using this command to grow a disk image, you must use file system and
partitioning tools inside the VM to actually begin using the new space on the
device.
-@item amend [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
+@item amend [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
Amends the image format specific @var{options} for the image file
@var{filename}. Not all file formats support this operation.
diff --git a/qemu-options.hx b/qemu-options.hx
index 1e7d5b8..da9851d 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -33,6 +33,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
" property accel=accel1[:accel2[:...]] selects accelerator\n"
" supported accelerators are kvm, xen, tcg (default: tcg)\n"
" kernel_irqchip=on|off controls accelerated irqchip support\n"
+ " vmport=on|off controls emulation of vmport (default: on)\n"
" kvm_shadow_mem=size of KVM shadow MMU\n"
" dump-guest-core=on|off include guest memory in a core dump (default=on)\n"
" mem-merge=on|off controls memory merge support (default: on)\n"
@@ -51,6 +52,8 @@ than one accelerator specified, the next one is used if the previous one fails
to initialize.
@item kernel_irqchip=on|off
Enables in-kernel irqchip support for the chosen accelerator when available.
+@item vmport=on|off
+Enables emulation of VMWare IO port, for vmmouse etc. (enabled by default)
@item kvm_shadow_mem=size
Defines the size of the KVM shadow MMU.
@item dump-guest-core=on|off
diff --git a/rules.mak b/rules.mak
index cf76b88..f500fef 100644
--- a/rules.mak
+++ b/rules.mak
@@ -362,4 +362,9 @@ define unnest-vars
# Include all the .d files
$(eval -include $(addsuffix *.d, $(sort $(dir $($v)))))
$(eval $v := $(filter-out %/,$($v))))
+
+ # For all %.mo objects that are directly added into -y, expand them to %.mo-objs
+ $(foreach v,$2,
+ $(eval $v := $(foreach o,$($v),$(if $($o-objs),$($o-objs),$o))))
+
endef
diff --git a/savevm.c b/savevm.c
index 2d8eb96..08ec678 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1246,7 +1246,7 @@ int load_vmstate(const char *name)
void do_delvm(Monitor *mon, const QDict *qdict)
{
BlockDriverState *bs;
- Error *err = NULL;
+ Error *err;
const char *name = qdict_get_str(qdict, "name");
if (!find_vmstate_bs()) {
@@ -1257,6 +1257,7 @@ void do_delvm(Monitor *mon, const QDict *qdict)
bs = NULL;
while ((bs = bdrv_next(bs))) {
if (bdrv_can_snapshot(bs)) {
+ err = NULL;
bdrv_snapshot_delete_by_id_or_name(bs, name, &err);
if (err) {
monitor_printf(mon,
diff --git a/scripts/kvm/kvm_stat b/scripts/kvm/kvm_stat
index d7e97e7..7b1437c 100755
--- a/scripts/kvm/kvm_stat
+++ b/scripts/kvm/kvm_stat
@@ -12,7 +12,7 @@
# the COPYING file in the top-level directory.
import curses
-import sys, os, time, optparse
+import sys, os, time, optparse, ctypes
class DebugfsProvider(object):
def __init__(self):
@@ -141,61 +141,97 @@ svm_exit_reasons = {
0x400: 'NPF',
}
-s390_exit_reasons = {
- 0x000: 'UNKNOWN',
- 0x001: 'EXCEPTION',
- 0x002: 'IO',
- 0x003: 'HYPERCALL',
- 0x004: 'DEBUG',
- 0x005: 'HLT',
- 0x006: 'MMIO',
- 0x007: 'IRQ_WINDOW_OPEN',
- 0x008: 'SHUTDOWN',
- 0x009: 'FAIL_ENTRY',
- 0x010: 'INTR',
- 0x011: 'SET_TPR',
- 0x012: 'TPR_ACCESS',
- 0x013: 'S390_SIEIC',
- 0x014: 'S390_RESET',
- 0x015: 'DCR',
- 0x016: 'NMI',
- 0x017: 'INTERNAL_ERROR',
- 0x018: 'OSI',
- 0x019: 'PAPR_HCALL',
+# From include/uapi/linux/kvm.h, KVM_EXIT_xxx
+userspace_exit_reasons = {
+ 0: 'UNKNOWN',
+ 1: 'EXCEPTION',
+ 2: 'IO',
+ 3: 'HYPERCALL',
+ 4: 'DEBUG',
+ 5: 'HLT',
+ 6: 'MMIO',
+ 7: 'IRQ_WINDOW_OPEN',
+ 8: 'SHUTDOWN',
+ 9: 'FAIL_ENTRY',
+ 10: 'INTR',
+ 11: 'SET_TPR',
+ 12: 'TPR_ACCESS',
+ 13: 'S390_SIEIC',
+ 14: 'S390_RESET',
+ 15: 'DCR',
+ 16: 'NMI',
+ 17: 'INTERNAL_ERROR',
+ 18: 'OSI',
+ 19: 'PAPR_HCALL',
+ 20: 'S390_UCONTROL',
+ 21: 'WATCHDOG',
+ 22: 'S390_TSCH',
+ 23: 'EPR',
}
-vendor_exit_reasons = {
+x86_exit_reasons = {
'vmx': vmx_exit_reasons,
'svm': svm_exit_reasons,
- 'IBM/S390': s390_exit_reasons,
}
-syscall_numbers = {
- 'IBM/S390': 331,
-}
-
-sc_perf_evt_open = 298
-
+sc_perf_evt_open = None
exit_reasons = None
-for line in file('/proc/cpuinfo').readlines():
- if line.startswith('flags') or line.startswith('vendor_id'):
- for flag in line.split():
- if flag in vendor_exit_reasons:
- exit_reasons = vendor_exit_reasons[flag]
- if flag in syscall_numbers:
- sc_perf_evt_open = syscall_numbers[flag]
-filters = {
- 'kvm_exit': ('exit_reason', exit_reasons)
+ioctl_numbers = {
+ 'SET_FILTER' : 0x40082406,
+ 'ENABLE' : 0x00002400,
+ 'DISABLE' : 0x00002401,
}
+def x86_init(flag):
+ globals().update({
+ 'sc_perf_evt_open' : 298,
+ 'exit_reasons' : x86_exit_reasons[flag],
+ })
+
+def s390_init():
+ globals().update({
+ 'sc_perf_evt_open' : 331
+ })
+
+def ppc_init():
+ globals().update({
+ 'sc_perf_evt_open' : 319,
+ 'ioctl_numbers' : {
+ 'SET_FILTER' : 0x80002406 | (ctypes.sizeof(ctypes.c_char_p) << 16),
+ 'ENABLE' : 0x20002400,
+ 'DISABLE' : 0x20002401,
+ }
+ })
+
+def detect_platform():
+ if os.uname()[4].startswith('ppc'):
+ ppc_init()
+ return
+
+ for line in file('/proc/cpuinfo').readlines():
+ if line.startswith('flags'):
+ for flag in line.split():
+ if flag in x86_exit_reasons:
+ x86_init(flag)
+ return
+ elif line.startswith('vendor_id'):
+ for flag in line.split():
+ if flag == 'IBM/S390':
+ s390_init()
+ return
+
+detect_platform()
+
def invert(d):
return dict((x[1], x[0]) for x in d.iteritems())
-for f in filters:
- filters[f] = (filters[f][0], invert(filters[f][1]))
+filters = {}
+filters['kvm_userspace_exit'] = ('reason', invert(userspace_exit_reasons))
+if exit_reasons:
+ filters['kvm_exit'] = ('exit_reason', invert(exit_reasons))
-import ctypes, struct, array
+import struct, array
libc = ctypes.CDLL('libc.so.6')
syscall = libc.syscall
@@ -285,14 +321,14 @@ class Event(object):
raise Exception('perf_event_open failed')
if filter:
import fcntl
- fcntl.ioctl(fd, 0x40082406, filter)
+ fcntl.ioctl(fd, ioctl_numbers['SET_FILTER'], filter)
self.fd = fd
def enable(self):
import fcntl
- fcntl.ioctl(self.fd, 0x00002400, 0)
+ fcntl.ioctl(self.fd, ioctl_numbers['ENABLE'], 0)
def disable(self):
import fcntl
- fcntl.ioctl(self.fd, 0x00002401, 0)
+ fcntl.ioctl(self.fd, ioctl_numbers['DISABLE'], 0)
class TracepointProvider(object):
def __init__(self):
@@ -311,18 +347,30 @@ class TracepointProvider(object):
self.select(fields)
def fields(self):
return self._fields
+
+ def _online_cpus(self):
+ l = []
+ pattern = r'cpu([0-9]+)'
+ basedir = '/sys/devices/system/cpu'
+ for entry in os.listdir(basedir):
+ match = re.match(pattern, entry)
+ if not match:
+ continue
+ path = os.path.join(basedir, entry, 'online')
+ if os.path.exists(path) and open(path).read().strip() != '1':
+ continue
+ l.append(int(match.group(1)))
+ return l
+
def _setup(self, _fields):
self._fields = _fields
- cpure = r'cpu([0-9]+)'
- self.cpus = [int(re.match(cpure, x).group(1))
- for x in os.listdir('/sys/devices/system/cpu')
- if re.match(cpure, x)]
+ cpus = self._online_cpus()
import resource
- nfiles = len(self.cpus) * 1000
+ nfiles = len(cpus) * 1000
resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
events = []
self.group_leaders = []
- for cpu in self.cpus:
+ for cpu in cpus:
group = Group(cpu)
for name in _fields:
tracepoint = name
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 3a3c48a..80d2c07 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -748,7 +748,6 @@ static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size)
} else {
TCGv_i64 tcg_hiaddr = tcg_temp_new_i64();
tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TEQ);
- tcg_gen_qemu_st64(tmp, tcg_addr, get_mem_index(s));
tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(s, srcidx));
tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8);
tcg_gen_qemu_st_i64(tmp, tcg_hiaddr, get_mem_index(s), MO_TEQ);
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index e194601..e4ccee1 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -442,14 +442,6 @@ const char *get_register_name_32(unsigned int reg)
return x86_reg_info_32[reg].name;
}
-/* collects per-function cpuid data
- */
-typedef struct model_features_t {
- uint32_t *guest_feat;
- uint32_t *host_feat;
- FeatureWord feat_word;
-} model_features_t;
-
/* KVM-specific features that are automatically added to all CPU models
* when KVM is enabled.
*/
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 418173e..782f7d2 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -7987,7 +7987,7 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu,
if (bp->pc == pc_ptr &&
!((bp->flags & BP_CPU) && (tb->flags & HF_RF_MASK))) {
gen_debug(dc, pc_ptr - dc->cs_base);
- break;
+ goto done_generating;
}
}
}
@@ -8038,6 +8038,7 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu,
}
if (tb->cflags & CF_LAST_IO)
gen_io_end();
+done_generating:
gen_tb_end(tb, num_insns);
*tcg_ctx.gen_opc_ptr = INDEX_op_end;
/* we don't forget to fill the last values */
diff --git a/target-xtensa/core-dc232b.c b/target-xtensa/core-dc232b.c
index c51e11e..a3b914b 100644
--- a/target-xtensa/core-dc232b.c
+++ b/target-xtensa/core-dc232b.c
@@ -33,7 +33,7 @@
#include "core-dc232b/core-isa.h"
#include "overlay_tool.h"
-static const XtensaConfig dc232b = {
+static const XtensaConfig dc232b __attribute__((unused)) = {
.name = "dc232b",
.gdb_regmap = {
.num_regs = 120,
diff --git a/target-xtensa/core-dc233c.c b/target-xtensa/core-dc233c.c
index 42dd64f..ac745d1 100644
--- a/target-xtensa/core-dc233c.c
+++ b/target-xtensa/core-dc233c.c
@@ -34,7 +34,7 @@
#include "core-dc233c/core-isa.h"
#include "overlay_tool.h"
-static const XtensaConfig dc233c = {
+static const XtensaConfig dc233c __attribute__((unused)) = {
.name = "dc233c",
.gdb_regmap = {
.num_regs = 121,
diff --git a/target-xtensa/core-fsf.c b/target-xtensa/core-fsf.c
index 6859bee..cfcc840 100644
--- a/target-xtensa/core-fsf.c
+++ b/target-xtensa/core-fsf.c
@@ -33,7 +33,7 @@
#include "core-fsf/core-isa.h"
#include "overlay_tool.h"
-static const XtensaConfig fsf = {
+static const XtensaConfig fsf __attribute__((unused)) = {
.name = "fsf",
/* GDB for this core is not supported currently */
.clock_freq_khz = 10000,
diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
index 9cf5275..beb5486 100644
--- a/target-xtensa/cpu.h
+++ b/target-xtensa/cpu.h
@@ -266,6 +266,7 @@ typedef enum {
INTTYPE_TIMER,
INTTYPE_DEBUG,
INTTYPE_WRITE_ERR,
+ INTTYPE_PROFILING,
INTTYPE_MAX
} interrupt_type;
diff --git a/target-xtensa/import_core.sh b/target-xtensa/import_core.sh
new file mode 100755
index 0000000..73791ec
--- /dev/null
+++ b/target-xtensa/import_core.sh
@@ -0,0 +1,53 @@
+#! /bin/bash -e
+
+OVERLAY="$1"
+NAME="$2"
+FREQ=40000
+BASE=$(dirname "$0")
+TARGET="$BASE"/core-$NAME
+
+[ $# -ge 2 -a -f "$OVERLAY" ] || { cat <<EOF
+Usage: $0 overlay-archive-to-import core-name [frequency-in-KHz]
+ overlay-archive-to-import: file name of xtensa-config-overlay.tar.gz
+ to import configuration from.
+ core-name: QEMU name of the imported core. Must be valid
+ C identifier.
+ frequency-in-KHz: core frequency (40MHz if not specified).
+EOF
+exit
+}
+
+[ $# -ge 3 ] && FREQ="$3"
+mkdir -p "$TARGET"
+tar -xf "$OVERLAY" -C "$TARGET" --strip-components=1 \
+ --xform='s/core/core-isa/' config/core.h
+tar -xf "$OVERLAY" -O gdb/xtensa-config.c | \
+ sed -n '1,/*\//p;/pc/,/a15/p' > "$TARGET"/gdb-config.c
+NUM_REGS=$(grep XTREG "$TARGET"/gdb-config.c | wc -l)
+
+cat <<EOF > "${TARGET}.c"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/gdbstub.h"
+#include "qemu/host-utils.h"
+
+#include "core-$NAME/core-isa.h"
+#include "overlay_tool.h"
+
+static const XtensaConfig $NAME __attribute__((unused)) = {
+ .name = "$NAME",
+ .gdb_regmap = {
+ .num_regs = $NUM_REGS,
+ .reg = {
+#include "core-$NAME/gdb-config.c"
+ }
+ },
+ .clock_freq_khz = $FREQ,
+ DEFAULT_SECTIONS
+};
+
+REGISTER_CORE($NAME)
+EOF
+
+grep -q core-${NAME}.o "$BASE"/Makefile.objs || \
+ echo "obj-y += core-${NAME}.o" >> "$BASE"/Makefile.objs
diff --git a/target-xtensa/overlay_tool.h b/target-xtensa/overlay_tool.h
index 4c0de7f..6105d4c 100644
--- a/target-xtensa/overlay_tool.h
+++ b/target-xtensa/overlay_tool.h
@@ -108,20 +108,27 @@
#define XCHAL_WINDOW_UF12_VECOFS 0x00000140
#endif
+#if XCHAL_HAVE_WINDOWED
+#define WINDOW_VECTORS \
+ [EXC_WINDOW_OVERFLOW4] = XCHAL_WINDOW_OF4_VECOFS + \
+ XCHAL_WINDOW_VECTORS_VADDR, \
+ [EXC_WINDOW_UNDERFLOW4] = XCHAL_WINDOW_UF4_VECOFS + \
+ XCHAL_WINDOW_VECTORS_VADDR, \
+ [EXC_WINDOW_OVERFLOW8] = XCHAL_WINDOW_OF8_VECOFS + \
+ XCHAL_WINDOW_VECTORS_VADDR, \
+ [EXC_WINDOW_UNDERFLOW8] = XCHAL_WINDOW_UF8_VECOFS + \
+ XCHAL_WINDOW_VECTORS_VADDR, \
+ [EXC_WINDOW_OVERFLOW12] = XCHAL_WINDOW_OF12_VECOFS + \
+ XCHAL_WINDOW_VECTORS_VADDR, \
+ [EXC_WINDOW_UNDERFLOW12] = XCHAL_WINDOW_UF12_VECOFS + \
+ XCHAL_WINDOW_VECTORS_VADDR,
+#else
+#define WINDOW_VECTORS
+#endif
+
#define EXCEPTION_VECTORS { \
[EXC_RESET] = XCHAL_RESET_VECTOR_VADDR, \
- [EXC_WINDOW_OVERFLOW4] = XCHAL_WINDOW_OF4_VECOFS + \
- XCHAL_WINDOW_VECTORS_VADDR, \
- [EXC_WINDOW_UNDERFLOW4] = XCHAL_WINDOW_UF4_VECOFS + \
- XCHAL_WINDOW_VECTORS_VADDR, \
- [EXC_WINDOW_OVERFLOW8] = XCHAL_WINDOW_OF8_VECOFS + \
- XCHAL_WINDOW_VECTORS_VADDR, \
- [EXC_WINDOW_UNDERFLOW8] = XCHAL_WINDOW_UF8_VECOFS + \
- XCHAL_WINDOW_VECTORS_VADDR, \
- [EXC_WINDOW_OVERFLOW12] = XCHAL_WINDOW_OF12_VECOFS + \
- XCHAL_WINDOW_VECTORS_VADDR, \
- [EXC_WINDOW_UNDERFLOW12] = XCHAL_WINDOW_UF12_VECOFS + \
- XCHAL_WINDOW_VECTORS_VADDR, \
+ WINDOW_VECTORS \
[EXC_KERNEL] = XCHAL_KERNEL_VECTOR_VADDR, \
[EXC_USER] = XCHAL_USER_VECTOR_VADDR, \
[EXC_DOUBLE] = XCHAL_DOUBLEEXC_VECTOR_VADDR, \
@@ -163,6 +170,7 @@
#define XTHAL_INTTYPE_TBD1 INTTYPE_DEBUG
#define XTHAL_INTTYPE_TBD2 INTTYPE_WRITE_ERR
#define XTHAL_INTTYPE_WRITE_ERROR INTTYPE_WRITE_ERR
+#define XTHAL_INTTYPE_PROFILING INTTYPE_PROFILING
#define INTERRUPT(i) { \
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index f1e16c1..2b432ad 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -43,8 +43,7 @@ class ImageCommitTestCase(iotests.QMPTestCase):
if event['event'] == 'BLOCK_JOB_COMPLETED':
self.assert_qmp(event, 'data/type', 'commit')
self.assert_qmp(event, 'data/device', 'drive0')
- self.assert_qmp(event, 'data/offset', self.image_len)
- self.assert_qmp(event, 'data/len', self.image_len)
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
if need_ready:
self.assertTrue(ready, "Expecting BLOCK_JOB_COMPLETED event")
completed = True
@@ -52,7 +51,6 @@ class ImageCommitTestCase(iotests.QMPTestCase):
ready = True
self.assert_qmp(event, 'data/type', 'commit')
self.assert_qmp(event, 'data/device', 'drive0')
- self.assert_qmp(event, 'data/len', self.image_len)
self.vm.qmp('block-job-complete', device='drive0')
self.assert_no_active_block_jobs()
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index 5dbd4ee..59a8f73 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -52,8 +52,7 @@ class ImageMirroringTestCase(iotests.QMPTestCase):
event = self.cancel_and_wait(drive=drive)
self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
self.assert_qmp(event, 'data/type', 'mirror')
- self.assert_qmp(event, 'data/offset', self.image_len)
- self.assert_qmp(event, 'data/len', self.image_len)
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
def complete_and_wait(self, drive='drive0', wait_ready=True):
'''Complete a block job and wait for it to finish'''
@@ -417,7 +416,6 @@ new_state = "1"
self.assert_qmp(event, 'data/type', 'mirror')
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/error', 'Input/output error')
- self.assert_qmp(event, 'data/len', self.image_len)
completed = True
self.assert_no_active_block_jobs()
@@ -568,7 +566,6 @@ new_state = "1"
self.assert_qmp(event, 'data/type', 'mirror')
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/error', 'Input/output error')
- self.assert_qmp(event, 'data/len', self.image_len)
completed = True
self.assert_no_active_block_jobs()
diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061
index ab98def..8d37f8a 100755
--- a/tests/qemu-iotests/061
+++ b/tests/qemu-iotests/061
@@ -209,6 +209,31 @@ $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 64M" "$TEST_IMG" | _filter_qemu_io
+echo
+echo "=== Testing progress report without snapshot ==="
+echo
+IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 4G
+IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 4G
+$QEMU_IO -c "write -z 0 64k" \
+ -c "write -z 1G 64k" \
+ -c "write -z 2G 64k" \
+ -c "write -z 3G 64k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+
+echo
+echo "=== Testing progress report with snapshot ==="
+echo
+IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 4G
+IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 4G
+$QEMU_IO -c "write -z 0 64k" \
+ -c "write -z 1G 64k" \
+ -c "write -z 2G 64k" \
+ -c "write -z 3G 64k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+$QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
index 4ae6472..9045544 100644
--- a/tests/qemu-iotests/061.out
+++ b/tests/qemu-iotests/061.out
@@ -390,4 +390,34 @@ wrote 67108864/67108864 bytes at offset 0
No errors were found on the image.
read 67108864/67108864 bytes at offset 0
64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing progress report without snapshot ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 1073741824
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 2147483648
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 3221225472
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ (0.00/100%) (12.50/100%) (25.00/100%) (37.50/100%) (50.00/100%) (62.50/100%) (75.00/100%) (87.50/100%) (100.00/100%) (100.00/100%)
+No errors were found on the image.
+
+=== Testing progress report with snapshot ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 1073741824
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 2147483648
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 3221225472
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ (0.00/100%) (6.25/100%) (12.50/100%) (18.75/100%) (25.00/100%) (31.25/100%) (37.50/100%) (43.75/100%) (50.00/100%) (56.25/100%) (62.50/100%) (68.75/100%) (75.00/100%) (81.25/100%) (87.50/100%) (93.75/100%) (100.00/100%) (100.00/100%)
+No errors were found on the image.
*** done
diff --git a/tests/qemu-iotests/076 b/tests/qemu-iotests/076
index bc47457..ed2be35 100755
--- a/tests/qemu-iotests/076
+++ b/tests/qemu-iotests/076
@@ -47,29 +47,34 @@ catalog_entries_offset=$((0x20))
nb_sectors_offset=$((0x24))
echo
-echo "== Read from a valid (enough) image =="
-_use_sample_img fake.parallels.bz2
+echo "== Read from a valid v1 image =="
+_use_sample_img parallels-v1.bz2
{ $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Negative catalog size =="
-_use_sample_img fake.parallels.bz2
+_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$catalog_entries_offset" "\xff\xff\xff\xff"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Overflow in catalog allocation =="
-_use_sample_img fake.parallels.bz2
+_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$nb_sectors_offset" "\xff\xff\xff\xff"
poke_file "$TEST_IMG" "$catalog_entries_offset" "\x01\x00\x00\x40"
{ $QEMU_IO -c "read 64M 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Zero sectors per track =="
-_use_sample_img fake.parallels.bz2
+_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$tracks_offset" "\x00\x00\x00\x00"
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+echo
+echo "== Read from a valid v2 image =="
+_use_sample_img parallels-v2.bz2
+{ $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/076.out b/tests/qemu-iotests/076.out
index f7745d8..32ade08 100644
--- a/tests/qemu-iotests/076.out
+++ b/tests/qemu-iotests/076.out
@@ -1,18 +1,22 @@
QA output created by 076
-== Read from a valid (enough) image ==
+== Read from a valid v1 image ==
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== Negative catalog size ==
-qemu-io: can't open device TEST_DIR/fake.parallels: Catalog too large
+qemu-io: can't open device TEST_DIR/parallels-v1: Catalog too large
no file open, try 'help open'
== Overflow in catalog allocation ==
-qemu-io: can't open device TEST_DIR/fake.parallels: Catalog too large
+qemu-io: can't open device TEST_DIR/parallels-v1: Catalog too large
no file open, try 'help open'
== Zero sectors per track ==
-qemu-io: can't open device TEST_DIR/fake.parallels: Invalid image: Zero sectors per track
+qemu-io: can't open device TEST_DIR/parallels-v1: Invalid image: Zero sectors per track
no file open, try 'help open'
+
+== Read from a valid v2 image ==
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
diff --git a/tests/qemu-iotests/097 b/tests/qemu-iotests/097
new file mode 100755
index 0000000..c7a613b
--- /dev/null
+++ b/tests/qemu-iotests/097
@@ -0,0 +1,122 @@
+#!/bin/bash
+#
+# Commit changes into backing chains and empty the top image if the
+# backing image is not explicitly specified
+#
+# Copyright (C) 2014 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=mreitz@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
+ _rm_test_img "$TEST_IMG.itmd"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+. ./common.pattern
+
+# Any format supporting backing files and bdrv_make_empty
+_supported_fmt qcow qcow2
+_supported_proto file
+_supported_os Linux
+
+
+# Four passes:
+# 0: Two-layer backing chain, commit to upper backing file (implicitly)
+# (in this case, the top image will be emptied)
+# 1: Two-layer backing chain, commit to upper backing file (explicitly)
+# (in this case, the top image will implicitly stay unchanged)
+# 2: Two-layer backing chain, commit to upper backing file (implicitly with -d)
+# (in this case, the top image will explicitly stay unchanged)
+# 3: Two-layer backing chain, commit to lower backing file
+# (in this case, the top image will implicitly stay unchanged)
+#
+# 020 already tests committing, so this only tests whether image chains are
+# working properly and that all images above the base are emptied; therefore,
+# no complicated patterns are necessary
+for i in 0 1 2 3; do
+
+echo
+echo "=== Test pass $i ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" 64M
+_make_test_img -b "$TEST_IMG.itmd" 64M
+
+$QEMU_IO -c 'write -P 1 0 192k' "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c 'write -P 2 64k 128k' "$TEST_IMG.itmd" | _filter_qemu_io
+$QEMU_IO -c 'write -P 3 128k 64k' "$TEST_IMG" | _filter_qemu_io
+
+if [ $i -lt 3 ]; then
+ if [ $i == 0 ]; then
+ # -b "$TEST_IMG.itmd" should be the default (that is, committing to the
+ # first backing file in the chain)
+ $QEMU_IMG commit "$TEST_IMG"
+ elif [ $i == 1 ]; then
+ # explicitly specify the commit target (this should imply -d)
+ $QEMU_IMG commit -b "$TEST_IMG.itmd" "$TEST_IMG"
+ else
+ # do not explicitly specify the commit target, but use -d to leave the
+ # top image unchanged
+ $QEMU_IMG commit -d "$TEST_IMG"
+ fi
+
+ # Bottom should be unchanged
+ $QEMU_IO -c 'read -P 1 0 192k' "$TEST_IMG.base" | _filter_qemu_io
+
+ # Intermediate should contain changes from top
+ $QEMU_IO -c 'read -P 1 0 64k' "$TEST_IMG.itmd" | _filter_qemu_io
+ $QEMU_IO -c 'read -P 2 64k 64k' "$TEST_IMG.itmd" | _filter_qemu_io
+ $QEMU_IO -c 'read -P 3 128k 64k' "$TEST_IMG.itmd" | _filter_qemu_io
+
+ # And in pass 0, the top image should be empty, whereas in both other passes
+ # it should be unchanged (which is both checked by qemu-img map)
+else
+ $QEMU_IMG commit -b "$TEST_IMG.base" "$TEST_IMG"
+
+ # Bottom should contain all changes
+ $QEMU_IO -c 'read -P 1 0 64k' "$TEST_IMG.base" | _filter_qemu_io
+ $QEMU_IO -c 'read -P 2 64k 64k' "$TEST_IMG.base" | _filter_qemu_io
+ $QEMU_IO -c 'read -P 3 128k 64k' "$TEST_IMG.base" | _filter_qemu_io
+
+ # Both top and intermediate should be unchanged
+fi
+
+$QEMU_IMG map "$TEST_IMG.base" | _filter_qemu_img_map
+$QEMU_IMG map "$TEST_IMG.itmd" | _filter_qemu_img_map
+$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map
+
+done
+
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/097.out b/tests/qemu-iotests/097.out
new file mode 100644
index 0000000..1cb7764
--- /dev/null
+++ b/tests/qemu-iotests/097.out
@@ -0,0 +1,119 @@
+QA output created by 097
+
+=== Test pass 0 ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd'
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image committed.
+read 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x30000 TEST_DIR/t.IMGFMT.base
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+
+=== Test pass 1 ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd'
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image committed.
+read 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x30000 TEST_DIR/t.IMGFMT.base
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd
+0x20000 0x10000 TEST_DIR/t.IMGFMT
+
+=== Test pass 2 ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd'
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image committed.
+read 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x30000 TEST_DIR/t.IMGFMT.base
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd
+0x20000 0x10000 TEST_DIR/t.IMGFMT
+
+=== Test pass 3 ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd'
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 65536
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image committed.
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 131072
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x30000 TEST_DIR/t.IMGFMT.base
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT.base
+0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd
+0x20000 0x10000 TEST_DIR/t.IMGFMT
+*** done
diff --git a/tests/qemu-iotests/098 b/tests/qemu-iotests/098
new file mode 100755
index 0000000..e2230ad
--- /dev/null
+++ b/tests/qemu-iotests/098
@@ -0,0 +1,82 @@
+#!/bin/bash
+#
+# Test qcow2's bdrv_make_empty for images without internal snapshots
+#
+# Copyright (C) 2014 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=mreitz@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
+ rm -f "$TEST_DIR/blkdebug.conf"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+. ./common.pattern
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+IMGOPTS="compat=1.1"
+
+for event in l1_update empty_image_prepare reftable_update refblock_alloc; do
+
+echo
+echo "=== $event ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+_make_test_img -b "$TEST_IMG.base" 64M
+
+# Some data that can be leaked when emptying the top image
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+cat > "$TEST_DIR/blkdebug.conf" <<EOF
+[inject-error]
+event = "$event"
+EOF
+
+$QEMU_IMG commit "blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" 2>&1 \
+ | _filter_testdir | _filter_imgfmt
+
+# There may be errors, but they should be fixed by opening the image
+$QEMU_IO -c close "$TEST_IMG"
+
+_check_test_img
+
+done
+
+
+rm -f "$TEST_DIR/blkdebug.conf"
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/098.out b/tests/qemu-iotests/098.out
new file mode 100644
index 0000000..586dfc8
--- /dev/null
+++ b/tests/qemu-iotests/098.out
@@ -0,0 +1,52 @@
+QA output created by 098
+
+=== l1_update ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+No errors were found on the image.
+
+=== empty_image_prepare ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+Leaked cluster 4 refcount=1 reference=0
+Leaked cluster 5 refcount=1 reference=0
+Repairing cluster 4 refcount=1 reference=0
+Repairing cluster 5 refcount=1 reference=0
+No errors were found on the image.
+
+=== reftable_update ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+ERROR cluster 0 refcount=0 reference=1
+ERROR cluster 1 refcount=0 reference=1
+ERROR cluster 3 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+No errors were found on the image.
+
+=== refblock_alloc ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
+ERROR cluster 0 refcount=0 reference=1
+ERROR cluster 1 refcount=0 reference=1
+ERROR cluster 3 refcount=0 reference=1
+Rebuilding refcount structure
+Repairing cluster 1 refcount=1 reference=0
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/102 b/tests/qemu-iotests/102
index 34b363f..161b197 100755
--- a/tests/qemu-iotests/102
+++ b/tests/qemu-iotests/102
@@ -34,9 +34,10 @@ _cleanup()
}
trap "_cleanup; exit \$status" 0 1 2 3 15
-# get standard environment, filters and checks
+# get standard environment, filters and qemu instance handling
. ./common.rc
. ./common.filter
+. ./common.qemu
_supported_fmt qcow2
_supported_proto file
@@ -53,11 +54,27 @@ _make_test_img $IMG_SIZE
$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
# Remove data cluster from image (first cluster: image header, second: reftable,
# third: refblock, fourth: L1 table, fifth: L2 table)
-truncate -s $((5 * 64 * 1024)) "$TEST_IMG"
+$QEMU_IMG resize -f raw "$TEST_IMG" $((5 * 64 * 1024))
$QEMU_IO -c map "$TEST_IMG"
$QEMU_IMG map "$TEST_IMG"
+echo
+echo '=== Testing map on an image file truncated outside of qemu ==='
+echo
+
+# Same as above, only now we concurrently truncate and map the image
+_make_test_img $IMG_SIZE
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+qemu_comm_method=monitor _launch_qemu -drive if=none,file="$TEST_IMG",id=drv0
+
+$QEMU_IMG resize -f raw "$TEST_IMG" $((5 * 64 * 1024))
+
+_send_qemu_cmd $QEMU_HANDLE 'qemu-io drv0 map' 'allocated' \
+ | sed -e 's/^(qemu).*qemu-io drv0 map...$/(qemu) qemu-io drv0 map/'
+_send_qemu_cmd $QEMU_HANDLE 'quit' ''
+
# success, all done
echo '*** done'
rm -f $seq.full
diff --git a/tests/qemu-iotests/102.out b/tests/qemu-iotests/102.out
index e0e9cdc..eecde16 100644
--- a/tests/qemu-iotests/102.out
+++ b/tests/qemu-iotests/102.out
@@ -5,6 +5,17 @@ QA output created by 102
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image resized.
[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
Offset Length Mapped to File
+
+=== Testing map on an image file truncated outside of qemu ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Image resized.
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) qemu-io drv0 map
+[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
*** done
diff --git a/tests/qemu-iotests/107 b/tests/qemu-iotests/107
index cad1cf9..9862030 100755
--- a/tests/qemu-iotests/107
+++ b/tests/qemu-iotests/107
@@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
_supported_fmt qcow2
-_supported_proto file
+_supported_proto file nfs
_supported_os Linux
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index f69cb6b..3acdb30 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -213,5 +213,12 @@ _filter_img_info()
-e "s/archipelago:a/TEST_DIR\//g"
}
+# filter out offsets and file names from qemu-img map
+_filter_qemu_img_map()
+{
+ sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \
+ -e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt
+}
+
# make sure this script returns success
/bin/true
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 9bbd5d3..7b2c666 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -67,7 +67,7 @@
058 rw auto quick
059 rw auto quick
060 rw auto quick
-061 rw auto quick
+061 rw auto
062 rw auto quick
063 rw auto quick
064 rw auto quick
@@ -100,6 +100,8 @@
091 rw auto quick
092 rw auto quick
095 rw auto quick
+097 rw auto backing
+098 rw auto backing quick
099 rw auto quick
100 rw auto quick
101 rw auto quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 39a4cfc..f57f154 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -267,8 +267,7 @@ class QMPTestCase(unittest.TestCase):
self.assert_qmp(event, 'data/device', drive)
self.assert_qmp_absent(event, 'data/error')
if check_offset:
- self.assert_qmp(event, 'data/offset', self.image_len)
- self.assert_qmp(event, 'data/len', self.image_len)
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
completed = True
self.assert_no_active_block_jobs()
diff --git a/tests/qemu-iotests/sample_images/fake.parallels.bz2 b/tests/qemu-iotests/sample_images/fake.parallels.bz2
deleted file mode 100644
index ffb5f13..0000000
--- a/tests/qemu-iotests/sample_images/fake.parallels.bz2
+++ /dev/null
Binary files differ
diff --git a/tests/qemu-iotests/sample_images/parallels-v1.bz2 b/tests/qemu-iotests/sample_images/parallels-v1.bz2
new file mode 100644
index 0000000..d1ef142
--- /dev/null
+++ b/tests/qemu-iotests/sample_images/parallels-v1.bz2
Binary files differ
diff --git a/tests/qemu-iotests/sample_images/parallels-v2.bz2 b/tests/qemu-iotests/sample_images/parallels-v2.bz2
new file mode 100644
index 0000000..fd8614d
--- /dev/null
+++ b/tests/qemu-iotests/sample_images/parallels-v2.bz2
Binary files differ
diff --git a/tests/tcg/xtensa/Makefile b/tests/tcg/xtensa/Makefile
index a70c92b..522a63e 100644
--- a/tests/tcg/xtensa/Makefile
+++ b/tests/tcg/xtensa/Makefile
@@ -13,6 +13,7 @@ SIMFLAGS = --xtensa-core=DC_B_232L --exit_with_target_code $(EXTFLAGS)
SIMDEBUG = --gdbserve=0
endif
+HOST_CC = gcc
CC = $(CROSS)gcc
AS = $(CROSS)gcc -x assembler-with-cpp
LD = $(CROSS)ld
@@ -21,7 +22,7 @@ XTENSA_SRC_PATH = $(SRC_PATH)/tests/tcg/xtensa
INCLUDE_DIRS = $(XTENSA_SRC_PATH) $(SRC_PATH)/target-xtensa/core-$(CORE)
XTENSA_INC = $(addprefix -I,$(INCLUDE_DIRS))
-LDFLAGS = -T$(XTENSA_SRC_PATH)/linker.ld
+LDFLAGS = -Tlinker.ld
CRT = crt.o vectors.o
@@ -59,13 +60,16 @@ TESTCASES += test_windowed.tst
all: build
+linker.ld: $(XTENSA_SRC_PATH)/linker.ld.S
+ $(HOST_CC) $(XTENSA_INC) -E -P $< -o $@
+
%.o: $(XTENSA_SRC_PATH)/%.c
$(CC) $(XTENSA_INC) $(CFLAGS) -c $< -o $@
%.o: $(XTENSA_SRC_PATH)/%.S
$(CC) $(XTENSA_INC) $(ASFLAGS) -c $< -o $@
-%.tst: %.o $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile
+%.tst: %.o linker.ld $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile
$(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@
build: $(TESTCASES)
@@ -85,4 +89,4 @@ host-debug-%.tst: %.tst
gdb --args $(SIM) $(SIMFLAGS) ./$<
clean:
- $(RM) -fr $(TESTCASES) $(CRT)
+ $(RM) -fr $(TESTCASES) $(CRT) linker.ld
diff --git a/tests/tcg/xtensa/linker.ld b/tests/tcg/xtensa/linker.ld
deleted file mode 100644
index 4d0b307..0000000
--- a/tests/tcg/xtensa/linker.ld
+++ /dev/null
@@ -1,112 +0,0 @@
-OUTPUT_FORMAT("elf32-xtensa-le")
-ENTRY(_start)
-
-__DYNAMIC = 0;
-
-MEMORY {
- ram : ORIGIN = 0xd0000000, LENGTH = 0x08000000 /* 128M */
- rom : ORIGIN = 0xfe000000, LENGTH = 0x00001000 /* 4k */
-}
-
-SECTIONS
-{
- .init :
- {
- *(.init)
- *(.init.*)
- } > rom
-
- .vector :
- {
- . = 0x00000000;
- *(.vector.window_overflow_4)
- *(.vector.window_overflow_4.*)
- . = 0x00000040;
- *(.vector.window_underflow_4)
- *(.vector.window_underflow_4.*)
- . = 0x00000080;
- *(.vector.window_overflow_8)
- *(.vector.window_overflow_8.*)
- . = 0x000000c0;
- *(.vector.window_underflow_8)
- *(.vector.window_underflow_8.*)
- . = 0x00000100;
- *(.vector.window_overflow_12)
- *(.vector.window_overflow_12.*)
- . = 0x00000140;
- *(.vector.window_underflow_12)
- *(.vector.window_underflow_12.*)
-
- . = 0x00000180;
- *(.vector.level2)
- *(.vector.level2.*)
- . = 0x000001c0;
- *(.vector.level3)
- *(.vector.level3.*)
- . = 0x00000200;
- *(.vector.level4)
- *(.vector.level4.*)
- . = 0x00000240;
- *(.vector.level5)
- *(.vector.level5.*)
- . = 0x00000280;
- *(.vector.level6)
- *(.vector.level6.*)
- . = 0x000002c0;
- *(.vector.level7)
- *(.vector.level7.*)
-
- . = 0x00000300;
- *(.vector.kernel)
- *(.vector.kernel.*)
- . = 0x00000340;
- *(.vector.user)
- *(.vector.user.*)
- . = 0x000003c0;
- *(.vector.double)
- *(.vector.double.*)
- } > ram
-
- .text :
- {
- _ftext = .;
- *(.text .stub .text.* .gnu.linkonce.t.* .literal .literal.*)
- _etext = .;
- } > ram
-
- .rodata :
- {
- . = ALIGN(4);
- _frodata = .;
- *(.rodata .rodata.* .gnu.linkonce.r.*)
- *(.rodata1)
- _erodata = .;
- } > ram
-
- .data :
- {
- . = ALIGN(4);
- _fdata = .;
- *(.data .data.* .gnu.linkonce.d.*)
- *(.data1)
- _gp = ALIGN(16);
- *(.sdata .sdata.* .gnu.linkonce.s.*)
- _edata = .;
- } > ram
-
- .bss :
- {
- . = ALIGN(4);
- _fbss = .;
- *(.dynsbss)
- *(.sbss .sbss.* .gnu.linkonce.sb.*)
- *(.scommon)
- *(.dynbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- _ebss = .;
- _end = .;
- } > ram
-}
-
-PROVIDE(_fstack = ORIGIN(ram) + LENGTH(ram) - 4);
diff --git a/tests/tcg/xtensa/linker.ld.S b/tests/tcg/xtensa/linker.ld.S
new file mode 100644
index 0000000..f1e7fa9
--- /dev/null
+++ b/tests/tcg/xtensa/linker.ld.S
@@ -0,0 +1,130 @@
+#include <core-isa.h>
+
+#if XTENSA_HAVE_BE
+OUTPUT_FORMAT("elf32-xtensa-be")
+#else
+OUTPUT_FORMAT("elf32-xtensa-le")
+#endif
+ENTRY(_start)
+
+__DYNAMIC = 0;
+
+MEMORY {
+ ram : ORIGIN = XCHAL_VECBASE_RESET_VADDR, LENGTH = 0x08000000 /* 128M */
+ rom : ORIGIN = XCHAL_RESET_VECTOR_VADDR, LENGTH = 0x00001000 /* 4k */
+}
+
+SECTIONS
+{
+ .init :
+ {
+ *(.init)
+ *(.init.*)
+ } > rom
+
+ .vector :
+ {
+ . = XCHAL_WINDOW_OF4_VECOFS;
+ *(.vector.window_overflow_4)
+ *(.vector.window_overflow_4.*)
+ . = XCHAL_WINDOW_UF4_VECOFS;
+ *(.vector.window_underflow_4)
+ *(.vector.window_underflow_4.*)
+ . = XCHAL_WINDOW_OF8_VECOFS;
+ *(.vector.window_overflow_8)
+ *(.vector.window_overflow_8.*)
+ . = XCHAL_WINDOW_UF8_VECOFS;
+ *(.vector.window_underflow_8)
+ *(.vector.window_underflow_8.*)
+ . = XCHAL_WINDOW_OF12_VECOFS;
+ *(.vector.window_overflow_12)
+ *(.vector.window_overflow_12.*)
+ . = XCHAL_WINDOW_UF12_VECOFS;
+ *(.vector.window_underflow_12)
+ *(.vector.window_underflow_12.*)
+
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 2
+ . = XCHAL_INTLEVEL2_VECOFS;
+ *(.vector.level2)
+ *(.vector.level2.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 3
+ . = XCHAL_INTLEVEL3_VECOFS;
+ *(.vector.level3)
+ *(.vector.level3.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 4
+ . = XCHAL_INTLEVEL4_VECOFS;
+ *(.vector.level4)
+ *(.vector.level4.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 5
+ . = XCHAL_INTLEVEL5_VECOFS;
+ *(.vector.level5)
+ *(.vector.level5.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 6
+ . = XCHAL_INTLEVEL6_VECOFS;
+ *(.vector.level6)
+ *(.vector.level6.*)
+#endif
+#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 7
+ . = XCHAL_INTLEVEL7_VECOFS;
+ *(.vector.level7)
+ *(.vector.level7.*)
+#endif
+
+ . = XCHAL_KERNEL_VECOFS;
+ *(.vector.kernel)
+ *(.vector.kernel.*)
+ . = XCHAL_USER_VECOFS;
+ *(.vector.user)
+ *(.vector.user.*)
+ . = XCHAL_DOUBLEEXC_VECOFS;
+ *(.vector.double)
+ *(.vector.double.*)
+ } > ram
+
+ .text :
+ {
+ _ftext = .;
+ *(.text .stub .text.* .gnu.linkonce.t.* .literal .literal.*)
+ _etext = .;
+ } > ram
+
+ .rodata :
+ {
+ . = ALIGN(4);
+ _frodata = .;
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ *(.rodata1)
+ _erodata = .;
+ } > ram
+
+ .data :
+ {
+ . = ALIGN(4);
+ _fdata = .;
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.data1)
+ _gp = ALIGN(16);
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ _edata = .;
+ } > ram
+
+ .bss :
+ {
+ . = ALIGN(4);
+ _fbss = .;
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ _ebss = .;
+ _end = .;
+ } > ram
+}
+
+PROVIDE(_fstack = (ORIGIN(ram) & 0xf0000000) + LENGTH(ram) - 16);
diff --git a/tests/test-bitops.c b/tests/test-bitops.c
index 8238eb5..47b5d3e 100644
--- a/tests/test-bitops.c
+++ b/tests/test-bitops.c
@@ -8,6 +8,7 @@
#include <glib.h>
#include <stdint.h>
+#include "qemu/osdep.h"
#include "qemu/bitops.h"
typedef struct {
diff --git a/trace-events b/trace-events
index 6c3a400..b5722ea 100644
--- a/trace-events
+++ b/trace-events
@@ -697,35 +697,36 @@ lm32_uart_irq_state(int level) "irq state %d"
megasas_init_firmware(uint64_t pa) "pa %" PRIx64 " "
megasas_init_queue(uint64_t queue_pa, int queue_len, uint64_t head, uint64_t tail, uint32_t flags) "queue at %" PRIx64 " len %d head %" PRIx64 " tail %" PRIx64 " flags %x"
megasas_initq_map_failed(int frame) "scmd %d: failed to map queue"
+megasas_initq_mapped(uint64_t pa) "queue already mapped at %" PRIx64 ""
megasas_initq_mismatch(int queue_len, int fw_cmds) "queue size %d max fw cmds %d"
-megasas_qf_found(unsigned int index, uint64_t pa) "found mapped frame %x pa %" PRIx64 ""
-megasas_qf_new(unsigned int index, void *cmd) "return new frame %x cmd %p"
-megasas_qf_failed(unsigned long pa) "all frames busy for frame %lx"
-megasas_qf_enqueue(unsigned int index, unsigned int count, uint64_t context, unsigned int tail, int busy) "enqueue frame %x count %d context %" PRIx64 " tail %x busy %d"
-megasas_qf_update(unsigned int head, unsigned int busy) "update reply queue head %x busy %d"
+megasas_qf_mapped(unsigned int index) "skip mapped frame %x"
+megasas_qf_new(unsigned int index, uint64_t frame) "frame %x addr %" PRIx64 ""
+megasas_qf_busy(unsigned long pa) "all frames busy for frame %lx"
+megasas_qf_enqueue(unsigned int index, unsigned int count, uint64_t context, unsigned int head, unsigned int tail, int busy) "frame %x count %d context %" PRIx64 " head %x tail %x busy %d"
+megasas_qf_update(unsigned int head, unsigned int tail, unsigned int busy) "head %x tail %x busy %d"
megasas_qf_map_failed(int cmd, unsigned long frame) "scmd %d: frame %lu"
megasas_qf_complete_noirq(uint64_t context) "context %" PRIx64 " "
-megasas_qf_complete(uint64_t context, unsigned int tail, unsigned int offset, int busy, unsigned int doorbell) "context %" PRIx64 " tail %x offset %d busy %d doorbell %x"
+megasas_qf_complete(uint64_t context, unsigned int head, unsigned int tail, int busy) "context %" PRIx64 " head %x tail %x busy %d"
megasas_frame_busy(uint64_t addr) "frame %" PRIx64 " busy"
-megasas_unhandled_frame_cmd(int cmd, uint8_t frame_cmd) "scmd %d: Unhandled MFI cmd %x"
+megasas_unhandled_frame_cmd(int cmd, uint8_t frame_cmd) "scmd %d: MFI cmd %x"
megasas_handle_scsi(const char *frame, int bus, int dev, int lun, void *sdev, unsigned long size) "%s dev %x/%x/%x sdev %p xfer %lu"
-megasas_scsi_target_not_present(const char *frame, int bus, int dev, int lun) "%s dev %x/%x/%x target not present"
+megasas_scsi_target_not_present(const char *frame, int bus, int dev, int lun) "%s dev %x/%x/%x"
megasas_scsi_invalid_cdb_len(const char *frame, int bus, int dev, int lun, int len) "%s dev %x/%x/%x invalid cdb len %d"
megasas_iov_read_overflow(int cmd, int bytes, int len) "scmd %d: %d/%d bytes"
megasas_iov_write_overflow(int cmd, int bytes, int len) "scmd %d: %d/%d bytes"
megasas_iov_read_underflow(int cmd, int bytes, int len) "scmd %d: %d/%d bytes"
megasas_iov_write_underflow(int cmd, int bytes, int len) "scmd %d: %d/%d bytes"
-megasas_scsi_req_alloc_failed(const char *frame, int dev, int lun) "%s dev %x/%x req allocation failed"
+megasas_scsi_req_alloc_failed(const char *frame, int dev, int lun) "%s dev %x/%x"
megasas_scsi_read_start(int cmd, int len) "scmd %d: transfer %d bytes of data"
megasas_scsi_write_start(int cmd, int len) "scmd %d: transfer %d bytes of data"
megasas_scsi_nodata(int cmd) "scmd %d: no data to be transferred"
-megasas_scsi_complete(int cmd, uint32_t status, int len, int xfer) "scmd %d: finished with status %x, len %u/%u"
-megasas_command_complete(int cmd, uint32_t status, uint32_t resid) "scmd %d: command completed, status %x, residual %d"
+megasas_scsi_complete(int cmd, uint32_t status, int len, int xfer) "scmd %d: status %x, len %u/%u"
+megasas_command_complete(int cmd, uint32_t status, uint32_t resid) "scmd %d: status %x, residual %d"
megasas_handle_io(int cmd, const char *frame, int dev, int lun, unsigned long lba, unsigned long count) "scmd %d: %s dev %x/%x lba %lx count %lu"
megasas_io_target_not_present(int cmd, const char *frame, int dev, int lun) "scmd %d: %s dev 1/%x/%x LUN not present"
megasas_io_read_start(int cmd, unsigned long lba, unsigned long count, unsigned long len) "scmd %d: start LBA %lx %lu blocks (%lu bytes)"
megasas_io_write_start(int cmd, unsigned long lba, unsigned long count, unsigned long len) "scmd %d: start LBA %lx %lu blocks (%lu bytes)"
-megasas_io_complete(int cmd, uint32_t len) "scmd %d: %d bytes completed"
+megasas_io_complete(int cmd, uint32_t len) "scmd %d: %d bytes"
megasas_iovec_sgl_overflow(int cmd, int index, int limit) "scmd %d: iovec count %d limit %d"
megasas_iovec_sgl_underflow(int cmd, int index) "scmd %d: iovec count %d"
megasas_iovec_sgl_invalid(int cmd, int index, uint64_t pa, uint32_t len) "scmd %d: element %d pa %" PRIx64 " len %u"
@@ -733,28 +734,29 @@ megasas_iovec_overflow(int cmd, int len, int limit) "scmd %d: len %d limit %d"
megasas_iovec_underflow(int cmd, int len, int limit) "scmd %d: len %d limit %d"
megasas_handle_dcmd(int cmd, int opcode) "scmd %d: MFI DCMD opcode %x"
megasas_finish_dcmd(int cmd, int size) "scmd %d: MFI DCMD wrote %d bytes"
-megasas_dcmd_req_alloc_failed(int cmd, const char *desc) "scmd %d: %s alloc failed"
+megasas_dcmd_req_alloc_failed(int cmd, const char *desc) "scmd %d: %s"
megasas_dcmd_internal_submit(int cmd, const char *desc, int dev) "scmd %d: %s to dev %d"
-megasas_dcmd_internal_finish(int cmd, int opcode, int lun) "scmd %d: DCMD finish internal cmd %x lun %d"
-megasas_dcmd_internal_invalid(int cmd, int opcode) "scmd %d: Invalid internal DCMD %x"
+megasas_dcmd_internal_finish(int cmd, int opcode, int lun) "scmd %d: cmd %x lun %d"
+megasas_dcmd_internal_invalid(int cmd, int opcode) "scmd %d: DCMD %x"
megasas_dcmd_unhandled(int cmd, int opcode, int len) "scmd %d: opcode %x, len %d"
megasas_dcmd_zero_sge(int cmd) "scmd %d: zero DCMD sge count"
-megasas_dcmd_invalid_sge(int cmd, int count) "scmd %d: invalid DCMD sge count %d"
-megasas_dcmd_invalid_xfer_len(int cmd, unsigned long size, unsigned long max) "scmd %d: invalid xfer len %ld, max %ld"
+megasas_dcmd_invalid_sge(int cmd, int count) "scmd %d: DCMD sge count %d"
+megasas_dcmd_invalid_xfer_len(int cmd, unsigned long size, unsigned long max) "scmd %d: xfer len %ld, max %ld"
megasas_dcmd_enter(int cmd, const char *dcmd, int len) "scmd %d: DCMD %s len %d"
-megasas_dcmd_dummy(int cmd, unsigned long size) "scmd %d: DCMD dummy xfer len %ld"
+megasas_dcmd_dummy(int cmd, unsigned long size) "scmd %d: xfer len %ld"
megasas_dcmd_set_fw_time(int cmd, unsigned long time) "scmd %d: Set FW time %lx"
megasas_dcmd_pd_get_list(int cmd, int num, int max, int offset) "scmd %d: DCMD PD get list: %d / %d PDs, size %d"
megasas_dcmd_ld_get_list(int cmd, int num, int max) "scmd %d: DCMD LD get list: found %d / %d LDs"
-megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: DCMD LD get info for dev %d"
-megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d"
-megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x"
-megasas_dcmd_ld_list_query(int cmd, int flags) "scmd %d: DCMD LD list query flags %x"
+megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: dev %d"
+megasas_dcmd_ld_list_query(int cmd, int flags) "scmd %d: query flags %x"
+megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: dev %d"
+megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: query flags %x"
+megasas_dcmd_reset_ld(int cmd, int target_id) "scmd %d: dev %d"
megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld"
-megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: aborting frame %x"
+megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: frame %x"
megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 ""
megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x"
-megasas_reset(void) "Reset"
+megasas_reset(int fw_state) "firmware state %x"
megasas_init(int sges, int cmds, const char *mode) "Using %d sges, %d cmds, %s mode"
megasas_msix_raise(int vector) "vector %d"
megasas_msi_raise(int vector) "vector %d"
@@ -764,9 +766,9 @@ megasas_intr_enabled(void) "Interrupts enabled"
megasas_intr_disabled(void) "Interrupts disabled"
megasas_msix_enabled(int vector) "vector %d"
megasas_msi_enabled(int vector) "vector %d"
-megasas_mmio_readl(unsigned long addr, uint32_t val) "addr 0x%lx: 0x%x"
+megasas_mmio_readl(const char *reg, uint32_t val) "reg %s: 0x%x"
megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx"
-megasas_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x"
+megasas_mmio_writel(const char *reg, uint32_t val) "reg %s: 0x%x"
megasas_mmio_invalid_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x"
# hw/audio/milkymist-ac97.c
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 6afb52a..801cba2 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -10,12 +10,13 @@ vnc-obj-y += vnc-jobs.o
common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
common-obj-y += input.o input-keymap.o input-legacy.o
common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
-common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o sdl2.o
+common-obj-$(CONFIG_SDL) += sdl.mo x_keymap.o
common-obj-$(CONFIG_COCOA) += cocoa.o
common-obj-$(CONFIG_CURSES) += curses.o
common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o
-$(obj)/sdl.o $(obj)/sdl_zoom.o $(obj)/sdl2.o: QEMU_CFLAGS += $(SDL_CFLAGS)
+sdl.mo-objs := sdl.o sdl_zoom.o sdl2.o
+sdl.mo-cflags := $(SDL_CFLAGS)
gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS)
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 016a047..8c9d80e 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -390,7 +390,8 @@ void os_mem_prealloc(int fd, char *area, size_t memory)
pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
if (sigsetjmp(sigjump, 1)) {
- fprintf(stderr, "os_mem_prealloc: failed to preallocate pages\n");
+ fprintf(stderr, "os_mem_prealloc: Insufficient free host memory "
+ "pages available to allocate guest RAM\n");
exit(1);
} else {
int i;
diff --git a/vl.c b/vl.c
index f6b3546..409cc47 100644
--- a/vl.c
+++ b/vl.c
@@ -376,6 +376,10 @@ static QemuOptsList qemu_machine_opts = {
.name = PC_MACHINE_MAX_RAM_BELOW_4G,
.type = QEMU_OPT_SIZE,
.help = "maximum ram below the 4G boundary (32bit boundary)",
+ }, {
+ .name = PC_MACHINE_VMPORT,
+ .type = QEMU_OPT_BOOL,
+ .help = "Enable vmport (pc & q35)",
},{
.name = "iommu",
.type = QEMU_OPT_BOOL,
@@ -3746,6 +3750,11 @@ int main(int argc, char **argv, char **envp)
configure_msg(opts);
break;
case QEMU_OPTION_dump_vmstate:
+ if (vmstate_dump_file) {
+ fprintf(stderr, "qemu: only one '-dump-vmstate' "
+ "option may be given\n");
+ exit(1);
+ }
vmstate_dump_file = fopen(optarg, "w");
if (vmstate_dump_file == NULL) {
fprintf(stderr, "open %s: %s\n", optarg, strerror(errno));
@@ -3999,7 +4008,7 @@ int main(int argc, char **argv, char **envp)
#endif
if (pid_file && qemu_create_pidfile(pid_file) != 0) {
- os_pidfile_error();
+ fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno));
exit(1);
}
OpenPOWER on IntegriCloud