Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
virtio, vhost, pc, pci: documentation, fixes and cleanups
Lots of fixes all over the place.
Unfortunately, this does not yet fix a regression with vhost
introduced by the last pull, the issue is typically this error:
kvm_mem_ioeventfd_add: error adding ioeventfd: File exists
followed by QEMU aborting.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
* remotes/mst/tags/for_upstream: (28 commits)
docs: add PCIe devices placement guidelines
virtio: drop virtio_queue_get_ring_{size,addr}()
vhost: drop legacy vring layout bits
vhost: adapt vhost_verify_ring_mappings() to virtio 1 ring layout
nvdimm acpi: introduce NVDIMM_DSM_MEMORY_SIZE
nvdimm acpi: use aml_name_decl to define named object
nvdimm acpi: rename nvdimm_dsm_reserved_root
nvdimm acpi: fix two comments
nvdimm acpi: define DSM return codes
nvdimm acpi: rename nvdimm_acpi_hotplug
nvdimm acpi: cleanup nvdimm_build_fit
nvdimm acpi: rename nvdimm_plugged_device_list
docs: improve the doc of Read FIT method
nvdimm acpi: clean up nvdimm_build_acpi
pc: memhp: stop handling nvdimm hotplug in pc_dimm_unplug
pc: memhp: move nvdimm hotplug out of memory hotplug
nvdimm acpi: drop the lock of fit buffer
qdev: hotplug: drop HotplugHandler.post_plug callback
vhost: migration blocker only if shared log is used
virtio-net: mark VIRTIO_NET_F_GSO as legacy
...
Message-id: 1479237527-11846-1-git-send-email-mst@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
diff --git a/block.c b/block.c
index c19c6c6..39ddea3 100644
--- a/block.c
+++ b/block.c
@@ -2796,7 +2796,7 @@
static int qsort_strcmp(const void *a, const void *b)
{
- return strcmp(a, b);
+ return strcmp(*(char *const *)a, *(char *const *)b);
}
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
@@ -2822,6 +2822,24 @@
}
}
+ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); i++) {
+ const char *format_name = block_driver_modules[i].format_name;
+
+ if (format_name) {
+ bool found = false;
+ int j = count;
+
+ while (formats && j && !found) {
+ found = !strcmp(formats[--j], format_name);
+ }
+
+ if (!found) {
+ formats = g_renew(const char *, formats, count + 1);
+ formats[count++] = format_name;
+ }
+ }
+ }
+
qsort(formats, count, sizeof(formats[0]), qsort_strcmp);
for (i = 0; i < count; i++) {
diff --git a/block/backup.c b/block/backup.c
index 7b5d8a3..ea38733 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -242,6 +242,14 @@
}
}
+static void backup_clean(BlockJob *job)
+{
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+ assert(s->target);
+ blk_unref(s->target);
+ s->target = NULL;
+}
+
static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
@@ -315,16 +323,6 @@
}
}
-static const BlockJobDriver backup_job_driver = {
- .instance_size = sizeof(BackupBlockJob),
- .job_type = BLOCK_JOB_TYPE_BACKUP,
- .set_speed = backup_set_speed,
- .commit = backup_commit,
- .abort = backup_abort,
- .attached_aio_context = backup_attached_aio_context,
- .drain = backup_drain,
-};
-
static BlockErrorAction backup_error_action(BackupBlockJob *job,
bool read, int error)
{
@@ -343,12 +341,8 @@
static void backup_complete(BlockJob *job, void *opaque)
{
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
BackupCompleteData *data = opaque;
- blk_unref(s->target);
- s->target = NULL;
-
block_job_completed(job, data->ret);
g_free(data);
}
@@ -537,7 +531,19 @@
block_job_defer_to_main_loop(&job->common, backup_complete, data);
}
-void backup_start(const char *job_id, BlockDriverState *bs,
+static const BlockJobDriver backup_job_driver = {
+ .instance_size = sizeof(BackupBlockJob),
+ .job_type = BLOCK_JOB_TYPE_BACKUP,
+ .start = backup_run,
+ .set_speed = backup_set_speed,
+ .commit = backup_commit,
+ .abort = backup_abort,
+ .clean = backup_clean,
+ .attached_aio_context = backup_attached_aio_context,
+ .drain = backup_drain,
+};
+
+BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
bool compress,
@@ -557,52 +563,52 @@
if (bs == target) {
error_setg(errp, "Source and target cannot be the same");
- return;
+ return NULL;
}
if (!bdrv_is_inserted(bs)) {
error_setg(errp, "Device is not inserted: %s",
bdrv_get_device_name(bs));
- return;
+ return NULL;
}
if (!bdrv_is_inserted(target)) {
error_setg(errp, "Device is not inserted: %s",
bdrv_get_device_name(target));
- return;
+ return NULL;
}
if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
error_setg(errp, "Compression is not supported for this drive %s",
bdrv_get_device_name(target));
- return;
+ return NULL;
}
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
- return;
+ return NULL;
}
if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
- return;
+ return NULL;
}
if (sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
if (!sync_bitmap) {
error_setg(errp, "must provide a valid bitmap name for "
"\"incremental\" sync mode");
- return;
+ return NULL;
}
/* Create a new bitmap, and freeze/disable this one. */
if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) {
- return;
+ return NULL;
}
} else if (sync_bitmap) {
error_setg(errp,
"a sync_bitmap was provided to backup_run, "
"but received an incompatible sync_mode (%s)",
MirrorSyncMode_lookup[sync_mode]);
- return;
+ return NULL;
}
len = bdrv_getlength(bs);
@@ -648,17 +654,18 @@
block_job_add_bdrv(&job->common, target);
job->common.len = len;
- job->common.co = qemu_coroutine_create(backup_run, job);
block_job_txn_add_job(txn, &job->common);
- qemu_coroutine_enter(job->common.co);
- return;
+
+ return &job->common;
error:
if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
}
if (job) {
- blk_unref(job->target);
+ backup_clean(&job->common);
block_job_unref(&job->common);
}
+
+ return NULL;
}
diff --git a/block/block-backend.c b/block/block-backend.c
index 27a7f6f..efbf398 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1393,13 +1393,14 @@
if (bs) {
bdrv_eject(bs, eject_flag);
-
- id = blk_get_attached_dev_id(blk);
- qapi_event_send_device_tray_moved(blk_name(blk), id,
- eject_flag, &error_abort);
- g_free(id);
-
}
+
+ /* Whether or not we ejected on the backend,
+ * the frontend experienced a tray event. */
+ id = blk_get_attached_dev_id(blk);
+ qapi_event_send_device_tray_moved(blk_name(blk), id,
+ eject_flag, &error_abort);
+ g_free(id);
}
int blk_get_flags(BlockBackend *blk)
diff --git a/block/commit.c b/block/commit.c
index e1eda89..c284e85 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -205,6 +205,7 @@
.instance_size = sizeof(CommitBlockJob),
.job_type = BLOCK_JOB_TYPE_COMMIT,
.set_speed = commit_set_speed,
+ .start = commit_run,
};
void commit_start(const char *job_id, BlockDriverState *bs,
@@ -288,10 +289,9 @@
s->backing_file_str = g_strdup(backing_file_str);
s->on_error = on_error;
- s->common.co = qemu_coroutine_create(commit_run, s);
- trace_commit_start(bs, base, top, s, s->common.co);
- qemu_coroutine_enter(s->common.co);
+ trace_commit_start(bs, base, top, s);
+ block_job_start(&s->common);
}
diff --git a/block/curl.c b/block/curl.c
index e5eaa7b..0404c1b 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -68,12 +68,10 @@
#endif
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
- CURLPROTO_FTP | CURLPROTO_FTPS | \
- CURLPROTO_TFTP)
+ CURLPROTO_FTP | CURLPROTO_FTPS)
#define CURL_NUM_STATES 8
#define CURL_NUM_ACB 8
-#define SECTOR_SIZE 512
#define READ_AHEAD_DEFAULT (256 * 1024)
#define CURL_TIMEOUT_DEFAULT 5
#define CURL_TIMEOUT_MAX 10000
@@ -105,12 +103,17 @@
size_t end;
} CURLAIOCB;
+typedef struct CURLSocket {
+ int fd;
+ QLIST_ENTRY(CURLSocket) next;
+} CURLSocket;
+
typedef struct CURLState
{
struct BDRVCURLState *s;
CURLAIOCB *acb[CURL_NUM_ACB];
CURL *curl;
- curl_socket_t sock_fd;
+ QLIST_HEAD(, CURLSocket) sockets;
char *orig_buf;
size_t buf_start;
size_t buf_off;
@@ -164,10 +167,27 @@
{
BDRVCURLState *s;
CURLState *state = NULL;
+ CURLSocket *socket;
+
curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
- state->sock_fd = fd;
s = state->s;
+ QLIST_FOREACH(socket, &state->sockets, next) {
+ if (socket->fd == fd) {
+ if (action == CURL_POLL_REMOVE) {
+ QLIST_REMOVE(socket, next);
+ g_free(socket);
+ }
+ break;
+ }
+ }
+ if (!socket) {
+ socket = g_new0(CURLSocket, 1);
+ socket->fd = fd;
+ QLIST_INSERT_HEAD(&state->sockets, socket, next);
+ }
+ socket = NULL;
+
DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd);
switch (action) {
case CURL_POLL_IN:
@@ -213,12 +233,13 @@
DPRINTF("CURL: Just reading %zd bytes\n", realsize);
- if (!s || !s->orig_buf)
- return 0;
+ if (!s || !s->orig_buf) {
+ goto read_end;
+ }
if (s->buf_off >= s->buf_len) {
/* buffer full, read nothing */
- return 0;
+ goto read_end;
}
realsize = MIN(realsize, s->buf_len - s->buf_off);
memcpy(s->orig_buf + s->buf_off, ptr, realsize);
@@ -231,15 +252,26 @@
continue;
if ((s->buf_off >= acb->end)) {
+ size_t request_length = acb->nb_sectors * BDRV_SECTOR_SIZE;
+
qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
acb->end - acb->start);
+
+ if (acb->end - acb->start < request_length) {
+ size_t offset = acb->end - acb->start;
+ qemu_iovec_memset(acb->qiov, offset, 0,
+ request_length - offset);
+ }
+
acb->common.cb(acb->common.opaque, 0);
qemu_aio_unref(acb);
s->acb[i] = NULL;
}
}
- return realsize;
+read_end:
+ /* curl will error out if we do not return this value */
+ return size * nmemb;
}
static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
@@ -247,6 +279,8 @@
{
int i;
size_t end = start + len;
+ size_t clamped_end = MIN(end, s->len);
+ size_t clamped_len = clamped_end - start;
for (i=0; i<CURL_NUM_STATES; i++) {
CURLState *state = &s->states[i];
@@ -261,12 +295,15 @@
// Does the existing buffer cover our section?
if ((start >= state->buf_start) &&
(start <= buf_end) &&
- (end >= state->buf_start) &&
- (end <= buf_end))
+ (clamped_end >= state->buf_start) &&
+ (clamped_end <= buf_end))
{
char *buf = state->orig_buf + (start - state->buf_start);
- qemu_iovec_from_buf(acb->qiov, 0, buf, len);
+ qemu_iovec_from_buf(acb->qiov, 0, buf, clamped_len);
+ if (clamped_len < len) {
+ qemu_iovec_memset(acb->qiov, clamped_len, 0, len - clamped_len);
+ }
acb->common.cb(acb->common.opaque, 0);
return FIND_RET_OK;
@@ -276,13 +313,13 @@
if (state->in_use &&
(start >= state->buf_start) &&
(start <= buf_fend) &&
- (end >= state->buf_start) &&
- (end <= buf_fend))
+ (clamped_end >= state->buf_start) &&
+ (clamped_end <= buf_fend))
{
int j;
acb->start = start - state->buf_start;
- acb->end = acb->start + len;
+ acb->end = acb->start + clamped_len;
for (j=0; j<CURL_NUM_ACB; j++) {
if (!state->acb[j]) {
@@ -352,6 +389,7 @@
static void curl_multi_do(void *arg)
{
CURLState *s = (CURLState *)arg;
+ CURLSocket *socket, *next_socket;
int running;
int r;
@@ -359,10 +397,13 @@
return;
}
- do {
- r = curl_multi_socket_action(s->s->multi, s->sock_fd, 0, &running);
- } while(r == CURLM_CALL_MULTI_PERFORM);
-
+ /* Need to use _SAFE because curl_multi_socket_action() may trigger
+ * curl_sock_cb() which might modify this list */
+ QLIST_FOREACH_SAFE(socket, &s->sockets, next, next_socket) {
+ do {
+ r = curl_multi_socket_action(s->s->multi, socket->fd, 0, &running);
+ } while (r == CURLM_CALL_MULTI_PERFORM);
+ }
}
static void curl_multi_read(void *arg)
@@ -466,6 +507,7 @@
#endif
}
+ QLIST_INIT(&state->sockets);
state->s = s;
return state;
@@ -475,6 +517,14 @@
{
if (s->s->multi)
curl_multi_remove_handle(s->s->multi, s->curl);
+
+ while (!QLIST_EMPTY(&s->sockets)) {
+ CURLSocket *socket = QLIST_FIRST(&s->sockets);
+
+ QLIST_REMOVE(socket, next);
+ g_free(socket);
+ }
+
s->in_use = 0;
}
@@ -738,12 +788,12 @@
CURLAIOCB *acb = p;
BDRVCURLState *s = acb->common.bs->opaque;
- size_t start = acb->sector_num * SECTOR_SIZE;
+ size_t start = acb->sector_num * BDRV_SECTOR_SIZE;
size_t end;
// In case we have the requested data already (e.g. read-ahead),
// we can just call the callback and be done.
- switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) {
+ switch (curl_find_buf(s, start, acb->nb_sectors * BDRV_SECTOR_SIZE, acb)) {
case FIND_RET_OK:
qemu_aio_unref(acb);
// fall through
@@ -762,13 +812,13 @@
}
acb->start = 0;
- acb->end = (acb->nb_sectors * SECTOR_SIZE);
+ acb->end = MIN(acb->nb_sectors * BDRV_SECTOR_SIZE, s->len - start);
state->buf_off = 0;
g_free(state->orig_buf);
state->buf_start = start;
- state->buf_len = acb->end + s->readahead_size;
- end = MIN(start + state->buf_len, s->len) - 1;
+ state->buf_len = MIN(acb->end + s->readahead_size, s->len - start);
+ end = start + state->buf_len - 1;
state->orig_buf = g_try_malloc(state->buf_len);
if (state->buf_len && state->orig_buf == NULL) {
curl_clean_state(state);
@@ -779,8 +829,8 @@
state->acb[0] = acb;
snprintf(state->range, 127, "%zd-%zd", start, end);
- DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
- (acb->nb_sectors * SECTOR_SIZE), start, state->range);
+ DPRINTF("CURL (AIO): Reading %llu at %zd (%s)\n",
+ (acb->nb_sectors * BDRV_SECTOR_SIZE), start, state->range);
curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
curl_multi_add_handle(s->multi, state->curl);
@@ -886,29 +936,12 @@
.bdrv_attach_aio_context = curl_attach_aio_context,
};
-static BlockDriver bdrv_tftp = {
- .format_name = "tftp",
- .protocol_name = "tftp",
-
- .instance_size = sizeof(BDRVCURLState),
- .bdrv_parse_filename = curl_parse_filename,
- .bdrv_file_open = curl_open,
- .bdrv_close = curl_close,
- .bdrv_getlength = curl_getlength,
-
- .bdrv_aio_readv = curl_aio_readv,
-
- .bdrv_detach_aio_context = curl_detach_aio_context,
- .bdrv_attach_aio_context = curl_attach_aio_context,
-};
-
static void curl_block_init(void)
{
bdrv_register(&bdrv_http);
bdrv_register(&bdrv_https);
bdrv_register(&bdrv_ftp);
bdrv_register(&bdrv_ftps);
- bdrv_register(&bdrv_tftp);
}
block_init(curl_block_init);
diff --git a/block/mirror.c b/block/mirror.c
index b2c1fb8..301ba92 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -615,6 +615,20 @@
return 0;
}
+/* Called when going out of the streaming phase to flush the bulk of the
+ * data to the medium, or just before completing.
+ */
+static int mirror_flush(MirrorBlockJob *s)
+{
+ int ret = blk_flush(s->target);
+ if (ret < 0) {
+ if (mirror_error_action(s, false, -ret) == BLOCK_ERROR_ACTION_REPORT) {
+ s->ret = ret;
+ }
+ }
+ return ret;
+}
+
static void coroutine_fn mirror_run(void *opaque)
{
MirrorBlockJob *s = opaque;
@@ -727,27 +741,23 @@
should_complete = false;
if (s->in_flight == 0 && cnt == 0) {
trace_mirror_before_flush(s);
- ret = blk_flush(s->target);
- if (ret < 0) {
- if (mirror_error_action(s, false, -ret) ==
- BLOCK_ERROR_ACTION_REPORT) {
- goto immediate_exit;
+ if (!s->synced) {
+ if (mirror_flush(s) < 0) {
+ /* Go check s->ret. */
+ continue;
}
- } else {
/* We're out of the streaming phase. From now on, if the job
* is cancelled we will actually complete all pending I/O and
* report completion. This way, block-job-cancel will leave
* the target in a consistent state.
*/
- if (!s->synced) {
- block_job_event_ready(&s->common);
- s->synced = true;
- }
-
- should_complete = s->should_complete ||
- block_job_is_cancelled(&s->common);
- cnt = bdrv_get_dirty_count(s->dirty_bitmap);
+ block_job_event_ready(&s->common);
+ s->synced = true;
}
+
+ should_complete = s->should_complete ||
+ block_job_is_cancelled(&s->common);
+ cnt = bdrv_get_dirty_count(s->dirty_bitmap);
}
if (cnt == 0 && should_complete) {
@@ -765,7 +775,7 @@
bdrv_drained_begin(bs);
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
- if (cnt > 0) {
+ if (cnt > 0 || mirror_flush(s) < 0) {
bdrv_drained_end(bs);
continue;
}
@@ -920,6 +930,7 @@
.instance_size = sizeof(MirrorBlockJob),
.job_type = BLOCK_JOB_TYPE_MIRROR,
.set_speed = mirror_set_speed,
+ .start = mirror_run,
.complete = mirror_complete,
.pause = mirror_pause,
.attached_aio_context = mirror_attached_aio_context,
@@ -930,6 +941,7 @@
.instance_size = sizeof(MirrorBlockJob),
.job_type = BLOCK_JOB_TYPE_COMMIT,
.set_speed = mirror_set_speed,
+ .start = mirror_run,
.complete = mirror_complete,
.pause = mirror_pause,
.attached_aio_context = mirror_attached_aio_context,
@@ -1007,9 +1019,8 @@
}
}
- s->common.co = qemu_coroutine_create(mirror_run, s);
- trace_mirror_start(bs, s, s->common.co, opaque);
- qemu_coroutine_enter(s->common.co);
+ trace_mirror_start(bs, s, opaque);
+ block_job_start(&s->common);
}
void mirror_start(const char *job_id, BlockDriverState *bs,
diff --git a/block/nbd.c b/block/nbd.c
index 9cff839..35f24be 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -536,6 +536,7 @@
ov = qobject_output_visitor_new(&saddr_qdict);
visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort);
visit_complete(ov, &saddr_qdict);
+ visit_free(ov);
assert(qobject_type(saddr_qdict) == QTYPE_QDICT);
qdict_put_obj(opts, "server", saddr_qdict);
diff --git a/block/nfs.c b/block/nfs.c
index 55c4e0b..d082783 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -676,6 +676,7 @@
ret = nfs_ftruncate(client->context, client->fh, total_size);
nfs_client_close(client);
out:
+ QDECREF(options);
g_free(client);
return ret;
}
diff --git a/block/qcow2.h b/block/qcow2.h
index 92203a8..1823414 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -473,8 +473,6 @@
return r1 > r2 ? r1 - r2 : r2 - r1;
}
-// FIXME Need qcow2_ prefix to global functions
-
/* qcow2.c functions */
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t sector_num, int nb_sectors);
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 247e47b..28b47d9 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -542,7 +542,7 @@
BlockReopenQueue *queue, Error **errp)
{
BDRVRawState *s;
- BDRVRawReopenState *raw_s;
+ BDRVRawReopenState *rs;
int ret = 0;
Error *local_err = NULL;
@@ -552,15 +552,15 @@
s = state->bs->opaque;
state->opaque = g_new0(BDRVRawReopenState, 1);
- raw_s = state->opaque;
+ rs = state->opaque;
if (s->type == FTYPE_CD) {
- raw_s->open_flags |= O_NONBLOCK;
+ rs->open_flags |= O_NONBLOCK;
}
- raw_parse_flags(state->flags, &raw_s->open_flags);
+ raw_parse_flags(state->flags, &rs->open_flags);
- raw_s->fd = -1;
+ rs->fd = -1;
int fcntl_flags = O_APPEND | O_NONBLOCK;
#ifdef O_NOATIME
@@ -569,35 +569,35 @@
#ifdef O_ASYNC
/* Not all operating systems have O_ASYNC, and those that don't
- * will not let us track the state into raw_s->open_flags (typically
+ * will not let us track the state into rs->open_flags (typically
* you achieve the same effect with an ioctl, for example I_SETSIG
* on Solaris). But we do not use O_ASYNC, so that's fine.
*/
assert((s->open_flags & O_ASYNC) == 0);
#endif
- if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
+ if ((rs->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
/* dup the original fd */
- raw_s->fd = qemu_dup(s->fd);
- if (raw_s->fd >= 0) {
- ret = fcntl_setfl(raw_s->fd, raw_s->open_flags);
+ rs->fd = qemu_dup(s->fd);
+ if (rs->fd >= 0) {
+ ret = fcntl_setfl(rs->fd, rs->open_flags);
if (ret) {
- qemu_close(raw_s->fd);
- raw_s->fd = -1;
+ qemu_close(rs->fd);
+ rs->fd = -1;
}
}
}
/* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
- if (raw_s->fd == -1) {
+ if (rs->fd == -1) {
const char *normalized_filename = state->bs->filename;
ret = raw_normalize_devicepath(&normalized_filename);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not normalize device path");
} else {
- assert(!(raw_s->open_flags & O_CREAT));
- raw_s->fd = qemu_open(normalized_filename, raw_s->open_flags);
- if (raw_s->fd == -1) {
+ assert(!(rs->open_flags & O_CREAT));
+ rs->fd = qemu_open(normalized_filename, rs->open_flags);
+ if (rs->fd == -1) {
error_setg_errno(errp, errno, "Could not reopen file");
ret = -1;
}
@@ -606,11 +606,11 @@
/* Fail already reopen_prepare() if we can't get a working O_DIRECT
* alignment with the new fd. */
- if (raw_s->fd != -1) {
- raw_probe_alignment(state->bs, raw_s->fd, &local_err);
+ if (rs->fd != -1) {
+ raw_probe_alignment(state->bs, rs->fd, &local_err);
if (local_err) {
- qemu_close(raw_s->fd);
- raw_s->fd = -1;
+ qemu_close(rs->fd);
+ rs->fd = -1;
error_propagate(errp, local_err);
ret = -EINVAL;
}
@@ -621,13 +621,13 @@
static void raw_reopen_commit(BDRVReopenState *state)
{
- BDRVRawReopenState *raw_s = state->opaque;
+ BDRVRawReopenState *rs = state->opaque;
BDRVRawState *s = state->bs->opaque;
- s->open_flags = raw_s->open_flags;
+ s->open_flags = rs->open_flags;
qemu_close(s->fd);
- s->fd = raw_s->fd;
+ s->fd = rs->fd;
g_free(state->opaque);
state->opaque = NULL;
@@ -636,16 +636,16 @@
static void raw_reopen_abort(BDRVReopenState *state)
{
- BDRVRawReopenState *raw_s = state->opaque;
+ BDRVRawReopenState *rs = state->opaque;
/* nothing to do if NULL, we didn't get far enough */
- if (raw_s == NULL) {
+ if (rs == NULL) {
return;
}
- if (raw_s->fd >= 0) {
- qemu_close(raw_s->fd);
- raw_s->fd = -1;
+ if (rs->fd >= 0) {
+ qemu_close(rs->fd);
+ rs->fd = -1;
}
g_free(state->opaque);
state->opaque = NULL;
diff --git a/block/raw_bsd.c b/block/raw_bsd.c
index 7c9bebb..8a5b9b0 100644
--- a/block/raw_bsd.c
+++ b/block/raw_bsd.c
@@ -91,6 +91,14 @@
}
s->offset = qemu_opt_get_size(opts, "offset", 0);
+ if (s->offset > real_size) {
+ error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than "
+ "size of the containing file (%" PRId64 ")",
+ s->offset, real_size);
+ ret = -EINVAL;
+ goto end;
+ }
+
if (qemu_opt_find(opts, "size") != NULL) {
s->size = qemu_opt_get_size(opts, "size", 0);
s->has_size = true;
@@ -100,7 +108,7 @@
}
/* Check size and offset */
- if (real_size < s->offset || (real_size - s->offset) < s->size) {
+ if ((real_size - s->offset) < s->size) {
error_setg(errp, "The sum of offset (%" PRIu64 ") and size "
"(%" PRIu64 ") has to be smaller or equal to the "
" actual size of the containing file (%" PRId64 ")",
@@ -111,7 +119,7 @@
/* Make sure size is multiple of BDRV_SECTOR_SIZE to prevent rounding
* up and leaking out of the specified area. */
- if (!QEMU_IS_ALIGNED(s->size, BDRV_SECTOR_SIZE)) {
+ if (s->has_size && !QEMU_IS_ALIGNED(s->size, BDRV_SECTOR_SIZE)) {
error_setg(errp, "Specified size is not multiple of %llu",
BDRV_SECTOR_SIZE);
ret = -EINVAL;
diff --git a/block/replication.c b/block/replication.c
index d5e2b0f..729dd12 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -421,6 +421,7 @@
int64_t active_length, hidden_length, disk_length;
AioContext *aio_context;
Error *local_err = NULL;
+ BlockJob *job;
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
@@ -508,17 +509,18 @@
bdrv_op_block_all(top_bs, s->blocker);
bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker);
- backup_start(NULL, s->secondary_disk->bs, s->hidden_disk->bs, 0,
- MIRROR_SYNC_MODE_NONE, NULL, false,
- BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
- BLOCK_JOB_INTERNAL, backup_job_completed, bs,
- NULL, &local_err);
+ job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs,
+ 0, MIRROR_SYNC_MODE_NONE, NULL, false,
+ BLOCKDEV_ON_ERROR_REPORT,
+ BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
+ backup_job_completed, bs, NULL, &local_err);
if (local_err) {
error_propagate(errp, local_err);
backup_job_cleanup(bs);
aio_context_release(aio_context);
return;
}
+ block_job_start(job);
break;
default:
aio_context_release(aio_context);
diff --git a/block/ssh.c b/block/ssh.c
index ca071c5..15ed281 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -582,8 +582,7 @@
return true;
}
-static InetSocketAddress *ssh_config(BDRVSSHState *s, QDict *options,
- Error **errp)
+static InetSocketAddress *ssh_config(QDict *options, Error **errp)
{
InetSocketAddress *inet = NULL;
QDict *addr = NULL;
@@ -661,7 +660,7 @@
}
/* Pop the config into our state object, Exit if invalid */
- s->inet = ssh_config(s, options, errp);
+ s->inet = ssh_config(options, errp);
if (!s->inet) {
ret = -EINVAL;
goto err;
diff --git a/block/stream.c b/block/stream.c
index b05856b..1523ba7 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -218,6 +218,7 @@
.instance_size = sizeof(StreamBlockJob),
.job_type = BLOCK_JOB_TYPE_STREAM,
.set_speed = stream_set_speed,
+ .start = stream_run,
};
void stream_start(const char *job_id, BlockDriverState *bs,
@@ -254,7 +255,6 @@
s->bs_flags = orig_bs_flags;
s->on_error = on_error;
- s->common.co = qemu_coroutine_create(stream_run, s);
- trace_stream_start(bs, base, s, s->common.co);
- qemu_coroutine_enter(s->common.co);
+ trace_stream_start(bs, base, s);
+ block_job_start(&s->common);
}
diff --git a/block/trace-events b/block/trace-events
index 882c903..cfc05f2 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -19,14 +19,14 @@
# block/stream.c
stream_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
-stream_start(void *bs, void *base, void *s, void *co) "bs %p base %p s %p co %p"
+stream_start(void *bs, void *base, void *s) "bs %p base %p s %p"
# block/commit.c
commit_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
-commit_start(void *bs, void *base, void *top, void *s, void *co) "bs %p base %p top %p s %p co %p"
+commit_start(void *bs, void *base, void *top, void *s) "bs %p base %p top %p s %p"
# block/mirror.c
-mirror_start(void *bs, void *s, void *co, void *opaque) "bs %p s %p co %p opaque %p"
+mirror_start(void *bs, void *s, void *opaque) "bs %p s %p opaque %p"
mirror_restart_iter(void *s, int64_t cnt) "s %p dirty count %"PRId64
mirror_before_flush(void *s) "s %p"
mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64
diff --git a/blockdev.c b/blockdev.c
index 102ca9f..245e1e1 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1811,7 +1811,7 @@
BlockJob *job;
} DriveBackupState;
-static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
+static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
Error **errp);
static void drive_backup_prepare(BlkActionState *common, Error **errp)
@@ -1835,23 +1835,26 @@
bdrv_drained_begin(bs);
state->bs = bs;
- do_drive_backup(backup, common->block_job_txn, &local_err);
+ state->job = do_drive_backup(backup, common->block_job_txn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
+}
- state->job = state->bs->job;
+static void drive_backup_commit(BlkActionState *common)
+{
+ DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
+ assert(state->job);
+ block_job_start(state->job);
}
static void drive_backup_abort(BlkActionState *common)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
- BlockDriverState *bs = state->bs;
- /* Only cancel if it's the job we started */
- if (bs && bs->job && bs->job == state->job) {
- block_job_cancel_sync(bs->job);
+ if (state->job) {
+ block_job_cancel_sync(state->job);
}
}
@@ -1872,8 +1875,8 @@
AioContext *aio_context;
} BlockdevBackupState;
-static void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
- Error **errp);
+static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
+ Error **errp);
static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
{
@@ -1906,23 +1909,26 @@
state->bs = bs;
bdrv_drained_begin(state->bs);
- do_blockdev_backup(backup, common->block_job_txn, &local_err);
+ state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
+}
- state->job = state->bs->job;
+static void blockdev_backup_commit(BlkActionState *common)
+{
+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ assert(state->job);
+ block_job_start(state->job);
}
static void blockdev_backup_abort(BlkActionState *common)
{
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
- BlockDriverState *bs = state->bs;
- /* Only cancel if it's the job we started */
- if (bs && bs->job && bs->job == state->job) {
- block_job_cancel_sync(bs->job);
+ if (state->job) {
+ block_job_cancel_sync(state->job);
}
}
@@ -2072,12 +2078,14 @@
[TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
.instance_size = sizeof(DriveBackupState),
.prepare = drive_backup_prepare,
+ .commit = drive_backup_commit,
.abort = drive_backup_abort,
.clean = drive_backup_clean,
},
[TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
.instance_size = sizeof(BlockdevBackupState),
.prepare = blockdev_backup_prepare,
+ .commit = blockdev_backup_commit,
.abort = blockdev_backup_abort,
.clean = blockdev_backup_clean,
},
@@ -3106,11 +3114,13 @@
aio_context_release(aio_context);
}
-static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp)
+static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
+ Error **errp)
{
BlockDriverState *bs;
BlockDriverState *target_bs;
BlockDriverState *source = NULL;
+ BlockJob *job = NULL;
BdrvDirtyBitmap *bmap = NULL;
AioContext *aio_context;
QDict *options = NULL;
@@ -3139,7 +3149,7 @@
bs = qmp_get_root_bs(backup->device, errp);
if (!bs) {
- return;
+ return NULL;
}
aio_context = bdrv_get_aio_context(bs);
@@ -3213,10 +3223,10 @@
}
}
- backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
- bmap, backup->compress, backup->on_source_error,
- backup->on_target_error, BLOCK_JOB_DEFAULT,
- NULL, NULL, txn, &local_err);
+ job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
+ backup->sync, bmap, backup->compress,
+ backup->on_source_error, backup->on_target_error,
+ BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
bdrv_unref(target_bs);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -3225,11 +3235,17 @@
out:
aio_context_release(aio_context);
+ return job;
}
void qmp_drive_backup(DriveBackup *arg, Error **errp)
{
- return do_drive_backup(arg, NULL, errp);
+
+ BlockJob *job;
+ job = do_drive_backup(arg, NULL, errp);
+ if (job) {
+ block_job_start(job);
+ }
}
BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
@@ -3237,12 +3253,14 @@
return bdrv_named_nodes_list(errp);
}
-void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error **errp)
+BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
+ Error **errp)
{
BlockDriverState *bs;
BlockDriverState *target_bs;
Error *local_err = NULL;
AioContext *aio_context;
+ BlockJob *job = NULL;
if (!backup->has_speed) {
backup->speed = 0;
@@ -3262,7 +3280,7 @@
bs = qmp_get_root_bs(backup->device, errp);
if (!bs) {
- return;
+ return NULL;
}
aio_context = bdrv_get_aio_context(bs);
@@ -3284,20 +3302,25 @@
goto out;
}
}
- backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
- NULL, backup->compress, backup->on_source_error,
- backup->on_target_error, BLOCK_JOB_DEFAULT,
- NULL, NULL, txn, &local_err);
+ job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
+ backup->sync, NULL, backup->compress,
+ backup->on_source_error, backup->on_target_error,
+ BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
}
out:
aio_context_release(aio_context);
+ return job;
}
void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
{
- do_blockdev_backup(arg, NULL, errp);
+ BlockJob *job;
+ job = do_blockdev_backup(arg, NULL, errp);
+ if (job) {
+ block_job_start(job);
+ }
}
/* Parameter check and block job starting for drive mirroring.
diff --git a/blockjob.c b/blockjob.c
index 4aa14a4..513620c 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -174,7 +174,9 @@
job->blk = blk;
job->cb = cb;
job->opaque = opaque;
- job->busy = true;
+ job->busy = false;
+ job->paused = true;
+ job->pause_count = 1;
job->refcnt = 1;
bs->job = job;
@@ -202,6 +204,23 @@
return (job->id == NULL);
}
+static bool block_job_started(BlockJob *job)
+{
+ return job->co;
+}
+
+void block_job_start(BlockJob *job)
+{
+ assert(job && !block_job_started(job) && job->paused &&
+ !job->busy && job->driver->start);
+ job->co = qemu_coroutine_create(job->driver->start, job);
+ if (--job->pause_count == 0) {
+ job->paused = false;
+ job->busy = true;
+ qemu_coroutine_enter(job->co);
+ }
+}
+
void block_job_ref(BlockJob *job)
{
++job->refcnt;
@@ -241,21 +260,29 @@
job->driver->abort(job);
}
}
+ if (job->driver->clean) {
+ job->driver->clean(job);
+ }
if (job->cb) {
job->cb(job->opaque, job->ret);
}
- if (block_job_is_cancelled(job)) {
- block_job_event_cancelled(job);
- } else {
- const char *msg = NULL;
- if (job->ret < 0) {
- msg = strerror(-job->ret);
+
+ /* Emit events only if we actually started */
+ if (block_job_started(job)) {
+ if (block_job_is_cancelled(job)) {
+ block_job_event_cancelled(job);
+ } else {
+ const char *msg = NULL;
+ if (job->ret < 0) {
+ msg = strerror(-job->ret);
+ }
+ block_job_event_completed(job, msg);
}
- block_job_event_completed(job, msg);
}
if (job->txn) {
+ QLIST_REMOVE(job, txn_list);
block_job_txn_unref(job->txn);
}
block_job_unref(job);
@@ -359,7 +386,8 @@
{
/* Should not be reachable via external interface for internal jobs */
assert(job->id);
- if (job->pause_count || job->cancelled || !job->driver->complete) {
+ if (job->pause_count || job->cancelled ||
+ !block_job_started(job) || !job->driver->complete) {
error_setg(errp, "The active block job '%s' cannot be completed",
job->id);
return;
@@ -391,6 +419,8 @@
void coroutine_fn block_job_pause_point(BlockJob *job)
{
+ assert(job && block_job_started(job));
+
if (!block_job_should_pause(job)) {
return;
}
@@ -442,9 +472,13 @@
void block_job_cancel(BlockJob *job)
{
- job->cancelled = true;
- block_job_iostatus_reset(job);
- block_job_enter(job);
+ if (block_job_started(job)) {
+ job->cancelled = true;
+ block_job_iostatus_reset(job);
+ block_job_enter(job);
+ } else {
+ block_job_completed(job, -ECANCELED);
+ }
}
bool block_job_is_cancelled(BlockJob *job)
diff --git a/disas/ppc.c b/disas/ppc.c
index 052cebe..bd05623 100644
--- a/disas/ppc.c
+++ b/disas/ppc.c
@@ -2286,6 +2286,10 @@
{ "vrlh", VX(4, 68), VX_MASK, PPCVEC, { VD, VA, VB } },
{ "vrlw", VX(4, 132), VX_MASK, PPCVEC, { VD, VA, VB } },
{ "vrsqrtefp", VX(4, 330), VX_MASK, PPCVEC, { VD, VB } },
+{ "vrldmi", VX(4, 197), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vrldnm", VX(4, 453), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vrlwmi", VX(4, 133), VX_MASK, PPCVEC, { VD, VA, VB} },
+{ "vrlwnm", VX(4, 389), VX_MASK, PPCVEC, { VD, VA, VB } },
{ "vsel", VXA(4, 42), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
{ "vsl", VX(4, 452), VX_MASK, PPCVEC, { VD, VA, VB } },
{ "vslb", VX(4, 260), VX_MASK, PPCVEC, { VD, VA, VB } },
diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt
index 6282938..e289be2 100644
--- a/docs/COLO-FT.txt
+++ b/docs/COLO-FT.txt
@@ -41,41 +41,43 @@
are detected between the PVM and SVM, COLO withholds transmission of the
outbound packets until it has successfully synchronized the PVM state to the SVM.
- Primary Node Secondary Node
- +------------+ +-----------------------+ +------------------------+ +------------+
- | | | HeartBeat |<----->| HeartBeat | | |
- | Primary VM | +-----------|-----------+ +-----------|------------+ |Secondary VM|
- | | | | | |
- | | +-----------|-----------+ +-----------|------------+ | |
- | | |QEMU +---v----+ | |QEMU +----v---+ | | |
- | | | |Failover| | | |Failover| | | |
- | | | +--------+ | | +--------+ | | |
- | | | +---------------+ | | +---------------+ | | |
- | | | | VM Checkpoint |-------------->| VM Checkpoint | | | |
- | | | +---------------+ | | +---------------+ | | |
- | | | | | | | |
- |Requests<---------------------------^------------------------------------------>Requests|
- |Responses----------------------\ /--|--------------\ /------------------------Responses|
- | | | | | | | | | | | | |
- | | | +-----------+ | | | | | | | +------------+ | | |
- | | | | COLO disk | | | | | | | | | COLO disk | | | |
- | | | | Manager |-|-|--|--------------|--|->| Manager | | | |
- | | | +|----------+ | | | | | | | +-----------|+ | | |
- | | | | | | | | | | | | | | |
- +------------+ +--|------------|-|--|--+ +---|--|--------------|--+ +------------+
- | | | | | | |
- +-------------+ | +----------v-v--|--+ +---|--v-----------+ | +-------------+
- | VM Monitor | | | COLO Proxy | | COLO Proxy | | | VM Monitor |
- | | | |(compare packet) | | (adjust sequence)| | | |
- +-------------+ | +----------|----^--+ +------------------+ | +-------------+
- | | | |
- +------------------|------------|----|--+ +---------------------|------------------+
- | Kernel | | | | | Kernel | |
- +------------------|------------|----|--+ +---------------------|------------------+
- | | | |
- +--------------v+ +--------v----|--+ +------------------+ +v-------------+
- | Storage | |External Network| | External Network | | Storage |
- +---------------+ +----------------+ +------------------+ +--------------+
+ Primary Node Secondary Node
++------------+ +-----------------------+ +------------------------+ +------------+
+| | | HeartBeat +<----->+ HeartBeat | | |
+| Primary VM | +-----------+-----------+ +-----------+------------+ |Secondary VM|
+| | | | | |
+| | +-----------|-----------+ +-----------|------------+ | |
+| | |QEMU +---v----+ | |QEMU +----v---+ | | |
+| | | |Failover| | | |Failover| | | |
+| | | +--------+ | | +--------+ | | |
+| | | +---------------+ | | +---------------+ | | |
+| | | | VM Checkpoint +-------------->+ VM Checkpoint | | | |
+| | | +---------------+ | | +---------------+ | | |
+|Requests<--------------------------\ /-----------------\ /--------------------->Requests|
+| | | ^ ^ | | | | | | |
+|Responses+---------------------\ /-|-|------------\ /-------------------------+Responses|
+| | | | | | | | | | | | | | | |
+| | | +-----------+ | | | | | | | | | | +----------+ | | |
+| | | | COLO disk | | | | | | | | | | | | COLO disk| | | |
+| | | | Manager +---------------------------->| Manager | | | |
+| | | ++----------+ v v | | | | | v v | +---------++ | | |
+| | | |+-----------+-+-+-++| | ++-+--+-+---------+ | | | |
+| | | || COLO Proxy || | | COLO Proxy | | | | |
+| | | || (compare packet || | |(adjust sequence | | | | |
+| | | ||and mirror packet)|| | | and ACK) | | | | |
+| | | |+------------+---+-+| | +-----------------+ | | | |
++------------+ +-----------------------+ +------------------------+ +------------+
++------------+ | | | | +------------+
+| VM Monitor | | | | | | VM Monitor |
++------------+ | | | | +------------+
++---------------------------------------+ +----------------------------------------+
+| Kernel | | | | | Kernel | |
++---------------------------------------+ +----------------------------------------+
+ | | | |
+ +--------------v+ +---------v---+--+ +------------------+ +v-------------+
+ | Storage | |External Network| | External Network | | Storage |
+ +---------------+ +----------------+ +------------------+ +--------------+
+
== Components introduction ==
diff --git a/docs/qmp-commands.txt b/docs/qmp-commands.txt
index 6afa872..abf210a 100644
--- a/docs/qmp-commands.txt
+++ b/docs/qmp-commands.txt
@@ -1803,7 +1803,7 @@
"file", "file", "ftp", "ftps", "host_cdrom",
"host_device", "http", "https",
"nbd", "parallels", "qcow", "qcow2", "raw",
- "tftp", "vdi", "vmdk", "vpc", "vvfat"
+ "vdi", "vmdk", "vpc", "vvfat"
- "backing_file": backing file name (json-string, optional)
- "backing_file_depth": number of files in the backing file chain (json-int)
- "encrypted": true if encrypted, false otherwise (json-bool)
diff --git a/hmp.c b/hmp.c
index b5e3f54..b869617 100644
--- a/hmp.c
+++ b/hmp.c
@@ -318,6 +318,7 @@
monitor_printf(mon, " %s: %" PRId64 " milliseconds",
MigrationParameter_lookup[MIGRATION_PARAMETER_DOWNTIME_LIMIT],
params->downtime_limit);
+ assert(params->has_x_checkpoint_delay);
monitor_printf(mon, " %s: %" PRId64,
MigrationParameter_lookup[MIGRATION_PARAMETER_X_CHECKPOINT_DELAY],
params->x_checkpoint_delay);
@@ -1570,7 +1571,7 @@
const char *base = qdict_get_try_str(qdict, "base");
int64_t speed = qdict_get_try_int(qdict, "speed", 0);
- qmp_block_stream(false, NULL, device, base != NULL, base, false, NULL,
+ qmp_block_stream(true, device, device, base != NULL, base, false, NULL,
false, NULL, qdict_haskey(qdict, "speed"), speed,
true, BLOCKDEV_ON_ERROR_REPORT, &error);
diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c
index 31822fe..06b9b39 100644
--- a/hw/char/spapr_vty.c
+++ b/hw/char/spapr_vty.c
@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qemu-common.h"
#include "cpu.h"
@@ -37,7 +38,15 @@
qemu_irq_pulse(spapr_vio_qirq(&dev->sdev));
}
for (i = 0; i < size; i++) {
- assert((dev->in - dev->out) < VTERM_BUFSIZE);
+ if (dev->in - dev->out >= VTERM_BUFSIZE) {
+ static bool reported;
+ if (!reported) {
+ error_report("VTY input buffer exhausted - characters dropped."
+ " (input size = %i)", size);
+ reported = true;
+ }
+ break;
+ }
dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i];
}
}
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index c35f0f5..1b7ea50 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -200,18 +200,14 @@
}
s = qemu_chr_find(str);
- g_free(str);
if (s == NULL) {
error_setg(errp, "Property '%s.%s' can't find value '%s'",
object_get_typename(obj), prop->name, str);
- return;
- }
-
- if (!qemu_chr_fe_init(be, s, errp)) {
+ } else if (!qemu_chr_fe_init(be, s, errp)) {
error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
object_get_typename(obj), prop->name, str);
- return;
}
+ g_free(str);
}
static void release_chr(Object *obj, const char *name, void *opaque)
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 311af6d..2a82768 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -705,13 +705,19 @@
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop);
- char buffer[] = "xxxx:xx:xx.x";
+ char buffer[] = "ffff:ff:ff.f";
char *p = buffer;
int rc = 0;
- rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%d",
- addr->domain, addr->bus, addr->slot, addr->function);
- assert(rc == sizeof(buffer) - 1);
+ /*
+ * Catch "invalid" device reference from vfio-pci and allow the
+ * default buffer representing the non-existant device to be used.
+ */
+ if (~addr->domain || ~addr->bus || ~addr->slot || ~addr->function) {
+ rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%0d",
+ addr->domain, addr->bus, addr->slot, addr->function);
+ assert(rc == sizeof(buffer) - 1);
+ }
visit_type_str(v, name, &p, errp);
}
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 6189675..fc1d19c 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -637,6 +637,23 @@
return 8; /* We wrote to 4 extra bytes from the header */
}
+/*
+ * Before transferring data or otherwise signalling acceptance of a command
+ * marked CONDDATA, we must check the validity of the byte_count_limit.
+ */
+static bool validate_bcl(IDEState *s)
+{
+ /* TODO: Check IDENTIFY data word 125 for defacult BCL (currently 0) */
+ if (s->atapi_dma || atapi_byte_count_limit(s)) {
+ return true;
+ }
+
+ /* TODO: Move abort back into core.c and introduce proper error flow between
+ * ATAPI layer and IDE core layer */
+ ide_abort_command(s);
+ return false;
+}
+
static void cmd_get_event_status_notification(IDEState *s,
uint8_t *buf)
{
@@ -1028,12 +1045,19 @@
return;
}
- transfer_request = buf[9];
- switch(transfer_request & 0xf8) {
- case 0x00:
+ transfer_request = buf[9] & 0xf8;
+ if (transfer_request == 0x00) {
/* nothing */
ide_atapi_cmd_ok(s);
- break;
+ return;
+ }
+
+ /* Check validity of BCL before transferring data */
+ if (!validate_bcl(s)) {
+ return;
+ }
+
+ switch (transfer_request) {
case 0x10:
/* normal read */
ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
@@ -1266,6 +1290,14 @@
* See ATA8-ACS3 "7.21.5 Byte Count Limit"
*/
NONDATA = 0x04,
+
+ /*
+ * CONDDATA implies a command that transfers data only conditionally based
+ * on the presence of suboptions. It should be exempt from the BCL check at
+ * command validation time, but it needs to be checked at the command
+ * handler level instead.
+ */
+ CONDDATA = 0x08,
};
static const struct AtapiCmd {
@@ -1289,7 +1321,7 @@
[ 0xad ] = { cmd_read_dvd_structure, CHECK_READY },
[ 0xbb ] = { cmd_set_speed, NONDATA },
[ 0xbd ] = { cmd_mechanism_status, 0 },
- [ 0xbe ] = { cmd_read_cd, CHECK_READY },
+ [ 0xbe ] = { cmd_read_cd, CHECK_READY | CONDDATA },
/* [1] handler detects and reports not ready condition itself */
};
@@ -1348,15 +1380,12 @@
return;
}
- /* Nondata commands permit the byte_count_limit to be 0.
+ /* Commands that don't transfer DATA permit the byte_count_limit to be 0.
* If this is a data-transferring PIO command and BCL is 0,
* we abort at the /ATA/ level, not the ATAPI level.
* See ATA8 ACS3 section 7.17.6.49 and 7.21.5 */
- if (cmd->handler && !(cmd->flags & NONDATA)) {
- /* TODO: Check IDENTIFY data word 125 for default BCL (currently 0) */
- if (!(atapi_byte_count_limit(s) || s->atapi_dma)) {
- /* TODO: Move abort back into core.c and make static inline again */
- ide_abort_command(s);
+ if (cmd->handler && !(cmd->flags & (NONDATA | CONDDATA))) {
+ if (!validate_bcl(s)) {
return;
}
}
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index b68c69d..5009533 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -508,6 +508,10 @@
int i;
int r;
+ if (n->nic->peer_deleted) {
+ return;
+ }
+
for (i = 0; i < n->max_queues; i++) {
if (i < n->curr_queues) {
r = peer_attach(n, i);
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 82276e0..9df7b25 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -110,7 +110,7 @@
CPUState *cs = CPU(DEVICE(pc->threads));
DeviceClass *dc = DEVICE_GET_CLASS(cs);
PowerPCCPU *cpu = POWERPC_CPU(cs);
- int smt_threads = ppc_get_compat_smt_threads(cpu);
+ int smt_threads = CPU_CORE(pc)->nr_threads;
CPUPPCState *env = &cpu->env;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
uint32_t servers_prop[smt_threads];
@@ -206,10 +206,6 @@
_FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
pa_features, sizeof(pa_features))));
- if (cpu->cpu_version) {
- _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version)));
- }
-
/* Build interrupt servers properties */
for (i = 0; i < smt_threads; i++) {
servers_prop[i] = cpu_to_be32(pc->pir + i);
@@ -525,6 +521,7 @@
k->cores_mask = POWER8E_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
k->xscom_base = 0x003fc0000000000ull;
+ k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8E";
}
@@ -546,6 +543,7 @@
k->cores_mask = POWER8_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
k->xscom_base = 0x003fc0000000000ull;
+ k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8";
}
@@ -567,6 +565,7 @@
k->cores_mask = POWER8_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p8;
k->xscom_base = 0x003fc0000000000ull;
+ k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8NVL";
}
@@ -588,6 +587,7 @@
k->cores_mask = POWER9_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p9;
k->xscom_base = 0x00603fc00000000ull;
+ k->xscom_core_base = 0x0ull;
dc->desc = "PowerNV Chip POWER9";
}
@@ -620,7 +620,7 @@
chip->cores_mask &= pcc->cores_mask;
/* now that we have a sane layout, let check the number of cores */
- cores_max = hweight_long(chip->cores_mask);
+ cores_max = ctpop64(chip->cores_mask);
if (chip->nr_cores > cores_max) {
error_setg(errp, "warning: too many cores for chip ! Limit is %d",
cores_max);
@@ -695,7 +695,9 @@
object_unref(OBJECT(pnv_core));
/* Each core has an XSCOM MMIO region */
- pnv_xscom_add_subregion(chip, PNV_XSCOM_EX_CORE_BASE(core_hwid),
+ pnv_xscom_add_subregion(chip,
+ PNV_XSCOM_EX_CORE_BASE(pcc->xscom_core_base,
+ core_hwid),
&PNV_CORE(pnv_core)->xscom_regs);
i++;
}
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index 2acda96..76ce854 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -24,6 +24,7 @@
#include "hw/ppc/ppc.h"
#include "hw/ppc/pnv.h"
#include "hw/ppc/pnv_core.h"
+#include "hw/ppc/pnv_xscom.h"
static void powernv_cpu_reset(void *opaque)
{
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
index 00dbd8b..0e2117f 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -23,8 +23,9 @@
#include "qapi/error.h"
#include "qemu/log.h"
-#include "hw/ppc/pnv_lpc.h"
#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_lpc.h"
+#include "hw/ppc/pnv_xscom.h"
#include "hw/ppc/fdt.h"
#include <libfdt.h>
diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c
index 5aaa264..8da2718 100644
--- a/hw/ppc/pnv_xscom.c
+++ b/hw/ppc/pnv_xscom.c
@@ -25,8 +25,8 @@
#include "hw/sysbus.h"
#include "hw/ppc/fdt.h"
-#include "hw/ppc/pnv_xscom.h"
#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_xscom.h"
#include <libfdt.h>
@@ -124,8 +124,8 @@
goto complete;
}
- val = address_space_ldq(&chip->xscom_as, pcba << 3, MEMTXATTRS_UNSPECIFIED,
- &result);
+ val = address_space_ldq(&chip->xscom_as, (uint64_t) pcba << 3,
+ MEMTXATTRS_UNSPECIFIED, &result);
if (result != MEMTX_OK) {
qemu_log_mask(LOG_GUEST_ERROR, "XSCOM read failed at @0x%"
HWADDR_PRIx " pcba=0x%08x\n", addr, pcba);
@@ -150,8 +150,8 @@
goto complete;
}
- address_space_stq(&chip->xscom_as, pcba << 3, val, MEMTXATTRS_UNSPECIFIED,
- &result);
+ address_space_stq(&chip->xscom_as, (uint64_t) pcba << 3, val,
+ MEMTXATTRS_UNSPECIFIED, &result);
if (result != MEMTX_OK) {
qemu_log_mask(LOG_GUEST_ERROR, "XSCOM write failed at @0x%"
HWADDR_PRIx " pcba=0x%08x data=0x%" PRIx64 "\n",
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 7cde30e..f9661b7 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -1658,19 +1658,25 @@
return 0;
}
+static bool version_before_3(void *opaque, int version_id)
+{
+ return version_id < 3;
+}
+
static const VMStateDescription vmstate_spapr_pci = {
.name = "spapr_pci",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 2,
.pre_save = spapr_pci_pre_save,
.post_load = spapr_pci_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT64_EQUAL(buid, sPAPRPHBState),
- VMSTATE_UINT32_EQUAL(dma_liobn[0], sPAPRPHBState),
- VMSTATE_UINT64_EQUAL(mem_win_addr, sPAPRPHBState),
- VMSTATE_UINT64_EQUAL(mem_win_size, sPAPRPHBState),
- VMSTATE_UINT64_EQUAL(io_win_addr, sPAPRPHBState),
- VMSTATE_UINT64_EQUAL(io_win_size, sPAPRPHBState),
+ VMSTATE_UNUSED_TEST(version_before_3,
+ sizeof(uint32_t) /* dma_liobn[0] */
+ + sizeof(uint64_t) /* mem_win_addr */
+ + sizeof(uint64_t) /* mem_win_size */
+ + sizeof(uint64_t) /* io_win_addr */
+ + sizeof(uint64_t) /* io_win_size */),
VMSTATE_STRUCT_ARRAY(lsi_table, sPAPRPHBState, PCI_NUM_PINS, 0,
vmstate_spapr_pci_lsi, struct spapr_pci_lsi),
VMSTATE_INT32(msi_devs_num, sPAPRPHBState),
diff --git a/include/block/block_int.h b/include/block/block_int.h
index b02abbd..83a423c 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -748,7 +748,7 @@
bool unmap, Error **errp);
/*
- * backup_start:
+ * backup_job_create:
* @job_id: The id of the newly-created job, or %NULL to use the
* device name of @bs.
* @bs: Block device to operate on.
@@ -764,18 +764,19 @@
* @opaque: Opaque pointer value passed to @cb.
* @txn: Transaction that this job is part of (may be NULL).
*
- * Start a backup operation on @bs. Clusters in @bs are written to @target
+ * Create a backup operation on @bs. Clusters in @bs are written to @target
* until the job is cancelled or manually completed.
*/
-void backup_start(const char *job_id, BlockDriverState *bs,
- BlockDriverState *target, int64_t speed,
- MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
- bool compress,
- BlockdevOnError on_source_error,
- BlockdevOnError on_target_error,
- int creation_flags,
- BlockCompletionFunc *cb, void *opaque,
- BlockJobTxn *txn, Error **errp);
+BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+ BlockDriverState *target, int64_t speed,
+ MirrorSyncMode sync_mode,
+ BdrvDirtyBitmap *sync_bitmap,
+ bool compress,
+ BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
+ int creation_flags,
+ BlockCompletionFunc *cb, void *opaque,
+ BlockJobTxn *txn, Error **errp);
void hmp_drive_add_node(Monitor *mon, const char *optstr);
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 356cacf..1acb256 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -189,6 +189,15 @@
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
/**
+ * block_job_start:
+ * @job: A job that has not yet been started.
+ *
+ * Begins execution of a block job.
+ * Takes ownership of one reference to the job object.
+ */
+void block_job_start(BlockJob *job);
+
+/**
* block_job_cancel:
* @job: The job to be canceled.
*
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
index 40275e4..8223822 100644
--- a/include/block/blockjob_int.h
+++ b/include/block/blockjob_int.h
@@ -47,6 +47,9 @@
/** Optional callback for job types that need to forward I/O status reset */
void (*iostatus_reset)(BlockJob *job);
+ /** Mandatory: Entrypoint for the Coroutine. */
+ CoroutineEntry *start;
+
/**
* Optional callback for job types whose completion must be triggered
* manually.
@@ -74,6 +77,14 @@
void (*abort)(BlockJob *job);
/**
+ * If the callback is not NULL, it will be invoked after a call to either
+ * .commit() or .abort(). Regardless of which callback is invoked after
+ * completion, .clean() will always be called, even if the job does not
+ * belong to a transaction group.
+ */
+ void (*clean)(BlockJob *job);
+
+ /**
* If the callback is not NULL, it will be invoked when the job transitions
* into the paused state. Paused jobs must not perform any asynchronous
* I/O or event loop activity. This callback is used to quiesce jobs.
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 02ac1c5..df98a72 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -21,7 +21,6 @@
#include "hw/boards.h"
#include "hw/sysbus.h"
-#include "hw/ppc/pnv_xscom.h"
#include "hw/ppc/pnv_lpc.h"
#define TYPE_PNV_CHIP "powernv-chip"
@@ -70,6 +69,7 @@
uint64_t cores_mask;
hwaddr xscom_base;
+ hwaddr xscom_core_base;
uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
} PnvChipClass;
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index c0a2fbb..0faa184 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -21,8 +21,6 @@
#include "qom/object.h"
-typedef struct PnvChip PnvChip;
-
typedef struct PnvXScomInterface {
Object parent;
} PnvXScomInterface;
@@ -42,7 +40,7 @@
} PnvXScomInterfaceClass;
/*
- * Layout of the XSCOM PCB addresses of EX core 1
+ * Layout of the XSCOM PCB addresses of EX core 1 (POWER 8)
*
* GPIO 0x1100xxxx
* SCOM 0x1101xxxx
@@ -56,8 +54,7 @@
* PCB SLAVE 0x110Fxxxx
*/
-#define PNV_XSCOM_EX_BASE 0x10000000
-#define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
+#define PNV_XSCOM_EX_CORE_BASE(base, i) (base | (((uint64_t)i) << 24))
#define PNV_XSCOM_EX_CORE_SIZE 0x100000
#define PNV_XSCOM_LPC_BASE 0xb0020
diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index 98fb005..1881284 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -218,7 +218,7 @@
*/
static inline uint8_t rol8(uint8_t word, unsigned int shift)
{
- return (word << shift) | (word >> (8 - shift));
+ return (word << shift) | (word >> ((8 - shift) & 7));
}
/**
@@ -228,7 +228,7 @@
*/
static inline uint8_t ror8(uint8_t word, unsigned int shift)
{
- return (word >> shift) | (word << (8 - shift));
+ return (word >> shift) | (word << ((8 - shift) & 7));
}
/**
@@ -238,7 +238,7 @@
*/
static inline uint16_t rol16(uint16_t word, unsigned int shift)
{
- return (word << shift) | (word >> (16 - shift));
+ return (word << shift) | (word >> ((16 - shift) & 15));
}
/**
@@ -248,7 +248,7 @@
*/
static inline uint16_t ror16(uint16_t word, unsigned int shift)
{
- return (word >> shift) | (word << (16 - shift));
+ return (word >> shift) | (word << ((16 - shift) & 15));
}
/**
@@ -258,7 +258,7 @@
*/
static inline uint32_t rol32(uint32_t word, unsigned int shift)
{
- return (word << shift) | (word >> (32 - shift));
+ return (word << shift) | (word >> ((32 - shift) & 31));
}
/**
@@ -268,7 +268,7 @@
*/
static inline uint32_t ror32(uint32_t word, unsigned int shift)
{
- return (word >> shift) | (word << (32 - shift));
+ return (word >> shift) | (word << ((32 - shift) & 31));
}
/**
@@ -278,7 +278,7 @@
*/
static inline uint64_t rol64(uint64_t word, unsigned int shift)
{
- return (word << shift) | (word >> (64 - shift));
+ return (word << shift) | (word >> ((64 - shift) & 63));
}
/**
@@ -288,7 +288,7 @@
*/
static inline uint64_t ror64(uint64_t word, unsigned int shift)
{
- return (word >> shift) | (word << (64 - shift));
+ return (word >> shift) | (word << ((64 - shift) & 63));
}
/**
diff --git a/migration/migration.c b/migration/migration.c
index e331f28..f498ab8 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -593,6 +593,7 @@
params->max_bandwidth = s->parameters.max_bandwidth;
params->has_downtime_limit = true;
params->downtime_limit = s->parameters.downtime_limit;
+ params->has_x_checkpoint_delay = true;
params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
return params;
diff --git a/migration/ram.c b/migration/ram.c
index fb9252d..a1c8089 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -1987,7 +1987,7 @@
int ret;
int i;
int64_t t0;
- int pages_sent = 0;
+ int done = 0;
rcu_read_lock();
if (ram_list.version != last_version) {
@@ -2007,9 +2007,9 @@
pages = ram_find_and_save_block(f, false, &bytes_transferred);
/* no more pages to sent */
if (pages == 0) {
+ done = 1;
break;
}
- pages_sent += pages;
acct_info.iterations++;
/* we want to check in the 1st loop, just in case it was the 1st time
@@ -2044,7 +2044,7 @@
return ret;
}
- return pages_sent;
+ return done;
}
/* Called with iothread lock */
diff --git a/nbd/client.c b/nbd/client.c
index 7db4301..ffb0743 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -90,20 +90,21 @@
* the amount of bytes consumed. */
static ssize_t drop_sync(QIOChannel *ioc, size_t size)
{
- ssize_t ret, dropped = size;
+ ssize_t ret = 0;
char small[1024];
char *buffer;
buffer = sizeof(small) < size ? small : g_malloc(MIN(65536, size));
while (size > 0) {
- ret = read_sync(ioc, buffer, MIN(65536, size));
- if (ret < 0) {
+ ssize_t count = read_sync(ioc, buffer, MIN(65536, size));
+
+ if (count <= 0) {
goto cleanup;
}
- assert(ret <= size);
- size -= ret;
+ assert(count <= size);
+ size -= count;
+ ret += count;
}
- ret = dropped;
cleanup:
if (buffer != small) {
diff --git a/net/net.c b/net/net.c
index ec984bf..939fe31 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1653,9 +1653,8 @@
if (rs->index >= rs->packet_len) {
rs->index = 0;
rs->state = 0;
- if (rs->finalize) {
- rs->finalize(rs);
- }
+ assert(rs->finalize);
+ rs->finalize(rs);
}
break;
}
diff --git a/net/socket.c b/net/socket.c
index 982c8de..fe3547b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -511,6 +511,7 @@
s->fd = -1;
s->listen_fd = ret;
s->nc.link_down = true;
+ net_socket_rs_init(&s->rs, net_socket_rs_finalize);
qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s);
qapi_free_SocketAddress(saddr);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index bcd3b9e..c29bef7 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -243,12 +243,12 @@
# 0.14.0 this can be: 'blkdebug', 'bochs', 'cloop', 'cow', 'dmg',
# 'file', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device',
# 'http', 'https', 'luks', 'nbd', 'parallels', 'qcow',
-# 'qcow2', 'raw', 'tftp', 'vdi', 'vmdk', 'vpc', 'vvfat'
+# 'qcow2', 'raw', 'vdi', 'vmdk', 'vpc', 'vvfat'
# 2.2: 'archipelago' added, 'cow' dropped
# 2.3: 'host_floppy' deprecated
# 2.5: 'host_floppy' dropped
# 2.6: 'luks' added
-# 2.8: 'replication' added
+# 2.8: 'replication' added, 'tftp' dropped
#
# @backing_file: #optional the name of the backing file (for copy-on-write)
#
@@ -1723,7 +1723,7 @@
'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
'host_device', 'http', 'https', 'luks', 'nbd', 'nfs', 'null-aio',
'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw',
- 'replication', 'ssh', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc',
+ 'replication', 'ssh', 'vdi', 'vhdx', 'vmdk', 'vpc',
'vvfat' ] }
##
@@ -2410,7 +2410,6 @@
'replication':'BlockdevOptionsReplication',
# TODO sheepdog: Wait for structured options
'ssh': 'BlockdevOptionsSsh',
- 'tftp': 'BlockdevOptionsCurl',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
'vmdk': 'BlockdevOptionsGenericCOWFormat',
diff --git a/qemu-options.hx b/qemu-options.hx
index 4536e18..4a5b29f 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2606,8 +2606,8 @@
See also @url{http://www.gluster.org}.
-@item HTTP/HTTPS/FTP/FTPS/TFTP
-QEMU supports read-only access to files accessed over http(s), ftp(s) and tftp.
+@item HTTP/HTTPS/FTP/FTPS
+QEMU supports read-only access to files accessed over http(s) and ftp(s).
Syntax using a single filename:
@example
@@ -2617,7 +2617,7 @@
where:
@table @option
@item protocol
-'http', 'https', 'ftp', 'ftps', or 'tftp'.
+'http', 'https', 'ftp', or 'ftps'.
@item username
Optional username for authentication to the remote server.
diff --git a/slirp/socket.c b/slirp/socket.c
index 280050a..6c18971 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -66,6 +66,23 @@
sofree(struct socket *so)
{
Slirp *slirp = so->slirp;
+ struct mbuf *ifm;
+
+ for (ifm = (struct mbuf *) slirp->if_fastq.qh_link;
+ (struct quehead *) ifm != &slirp->if_fastq;
+ ifm = ifm->ifq_next) {
+ if (ifm->ifq_so == so) {
+ ifm->ifq_so = NULL;
+ }
+ }
+
+ for (ifm = (struct mbuf *) slirp->if_batchq.qh_link;
+ (struct quehead *) ifm != &slirp->if_batchq;
+ ifm = ifm->ifq_next) {
+ if (ifm->ifq_so == so) {
+ ifm->ifq_so = NULL;
+ }
+ }
if (so->so_emu==EMU_RSH && so->extra) {
sofree(so->extra);
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 14c5186..6eec5dc 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -3721,6 +3721,9 @@
cc->write_elf32_qemunote = x86_cpu_write_elf32_qemunote;
cc->vmsd = &vmstate_x86_cpu;
#endif
+ /* CPU_NB_REGS * 2 = general regs + xmm regs
+ * 25 = eip, eflags, 6 seg regs, st[0-7], fctrl,...,fop, mxcsr.
+ */
cc->gdb_num_core_regs = CPU_NB_REGS * 2 + 25;
#ifndef CONFIG_USER_ONLY
cc->debug_excp_handler = breakpoint_handler;
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 1c0864e..f62264a 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -2855,7 +2855,7 @@
if (run->flags & KVM_RUN_X86_SMM) {
env->hflags |= HF_SMM_MASK;
} else {
- env->hflags &= HF_SMM_MASK;
+ env->hflags &= ~HF_SMM_MASK;
}
if (run->if_flag) {
env->eflags |= IF_MASK;
diff --git a/target-i386/machine.c b/target-i386/machine.c
index 48037f1..760f82b 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -709,6 +709,10 @@
X86CPU *cpu = opaque;
CPUX86State *env = &cpu->env;
+ if (!cpu->hyperv_runtime) {
+ return false;
+ }
+
return env->msr_hv_runtime != 0;
}
diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c
index 901cf40..506dee1 100644
--- a/target-ppc/cpu-models.c
+++ b/target-ppc/cpu-models.c
@@ -1130,10 +1130,6 @@
#if defined(TODO)
POWERPC_DEF("POWER6", CPU_POWERPC_POWER6, POWER6,
"POWER6")
- POWERPC_DEF("POWER6_5", CPU_POWERPC_POWER6_5, POWER5,
- "POWER6 running in POWER5 mode")
- POWERPC_DEF("POWER6A", CPU_POWERPC_POWER6A, POWER6,
- "POWER6A")
#endif
POWERPC_DEF("POWER7_v2.3", CPU_POWERPC_POWER7_v23, POWER7,
"POWER7 v2.3")
diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h
index 7d9e6a2..aafbbd7 100644
--- a/target-ppc/cpu-models.h
+++ b/target-ppc/cpu-models.h
@@ -549,8 +549,6 @@
CPU_POWERPC_POWER5 = 0x003A0203,
CPU_POWERPC_POWER5P_v21 = 0x003B0201,
CPU_POWERPC_POWER6 = 0x003E0000,
- CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */
- CPU_POWERPC_POWER6A = 0x0F000002,
CPU_POWERPC_POWER_SERVER_MASK = 0xFFFF0000,
CPU_POWERPC_POWER7_BASE = 0x003F0000,
CPU_POWERPC_POWER7_v23 = 0x003F0203,
diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c
index 808760b..93369d4 100644
--- a/target-ppc/excp_helper.c
+++ b/target-ppc/excp_helper.c
@@ -427,6 +427,9 @@
case POWERPC_EXCP_VPU: /* Vector unavailable exception */
case POWERPC_EXCP_VSXU: /* VSX unavailable exception */
case POWERPC_EXCP_FU: /* Facility unavailable exception */
+#ifdef TARGET_PPC64
+ env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56);
+#endif
break;
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
LOG_EXCP("PIT exception\n");
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 3916b2e..da00f0a 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -223,6 +223,9 @@
DEF_HELPER_3(vsrv, void, avr, avr, avr)
DEF_HELPER_3(vslv, void, avr, avr, avr)
DEF_HELPER_3(vaddcuw, void, avr, avr, avr)
+DEF_HELPER_2(vprtybw, void, avr, avr)
+DEF_HELPER_2(vprtybd, void, avr, avr)
+DEF_HELPER_2(vprtybq, void, avr, avr)
DEF_HELPER_3(vsubcuw, void, avr, avr, avr)
DEF_HELPER_2(lvsl, void, avr, tl)
DEF_HELPER_2(lvsr, void, avr, tl)
@@ -325,6 +328,10 @@
DEF_HELPER_4(vminfp, void, env, avr, avr, avr)
DEF_HELPER_3(vrefp, void, env, avr, avr)
DEF_HELPER_3(vrsqrtefp, void, env, avr, avr)
+DEF_HELPER_3(vrlwmi, void, avr, avr, avr)
+DEF_HELPER_3(vrldmi, void, avr, avr, avr)
+DEF_HELPER_3(vrldnm, void, avr, avr, avr)
+DEF_HELPER_3(vrlwnm, void, avr, avr, avr)
DEF_HELPER_5(vmaddfp, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vnmsubfp, void, env, avr, avr, avr, avr)
DEF_HELPER_3(vexptefp, void, env, avr, avr)
@@ -371,6 +378,10 @@
DEF_HELPER_4(bcdadd, i32, avr, avr, avr, i32)
DEF_HELPER_4(bcdsub, i32, avr, avr, avr, i32)
+DEF_HELPER_3(bcdcfn, i32, avr, avr, i32)
+DEF_HELPER_3(bcdctn, i32, avr, avr, i32)
+DEF_HELPER_3(bcdcfz, i32, avr, avr, i32)
+DEF_HELPER_3(bcdctz, i32, avr, avr, i32)
DEF_HELPER_2(xsadddp, void, env, i32)
DEF_HELPER_2(xssubdp, void, env, i32)
diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c
index dca4798..9ac204a 100644
--- a/target-ppc/int_helper.c
+++ b/target-ppc/int_helper.c
@@ -18,6 +18,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
+#include "internal.h"
#include "exec/exec-all.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
@@ -527,6 +528,40 @@
}
}
+/* vprtybw */
+void helper_vprtybw(ppc_avr_t *r, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
+ uint64_t res = b->u32[i] ^ (b->u32[i] >> 16);
+ res ^= res >> 8;
+ r->u32[i] = res & 1;
+ }
+}
+
+/* vprtybd */
+void helper_vprtybd(ppc_avr_t *r, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->u64); i++) {
+ uint64_t res = b->u64[i] ^ (b->u64[i] >> 32);
+ res ^= res >> 16;
+ res ^= res >> 8;
+ r->u64[i] = res & 1;
+ }
+}
+
+/* vprtybq */
+void helper_vprtybq(ppc_avr_t *r, ppc_avr_t *b)
+{
+ uint64_t res = b->u64[0] ^ b->u64[1];
+ res ^= res >> 32;
+ res ^= res >> 16;
+ res ^= res >> 8;
+ r->u64[LO_IDX] = res & 1;
+ r->u64[HI_IDX] = 0;
+}
+
#define VARITH_DO(name, op, element) \
void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
{ \
@@ -1717,6 +1752,34 @@
}
}
+#define VRLMI(name, size, element, insert) \
+void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+{ \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ uint##size##_t src1 = a->element[i]; \
+ uint##size##_t src2 = b->element[i]; \
+ uint##size##_t src3 = r->element[i]; \
+ uint##size##_t begin, end, shift, mask, rot_val; \
+ \
+ shift = extract##size(src2, 0, 6); \
+ end = extract##size(src2, 8, 6); \
+ begin = extract##size(src2, 16, 6); \
+ rot_val = rol##size(src1, shift); \
+ mask = mask_u##size(begin, end); \
+ if (insert) { \
+ r->element[i] = (rot_val & mask) | (src3 & ~mask); \
+ } else { \
+ r->element[i] = (rot_val & mask); \
+ } \
+ } \
+}
+
+VRLMI(vrldmi, 64, u64, 1);
+VRLMI(vrlwmi, 32, u32, 1);
+VRLMI(vrldnm, 64, u64, 0);
+VRLMI(vrlwnm, 32, u32, 0);
+
void helper_vsel(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b,
ppc_avr_t *c)
{
@@ -2429,6 +2492,8 @@
#define BCD_NEG_PREF 0xD
#define BCD_NEG_ALT 0xB
#define BCD_PLUS_ALT_2 0xE
+#define NATIONAL_PLUS 0x2B
+#define NATIONAL_NEG 0x2D
#if defined(HOST_WORDS_BIGENDIAN)
#define BCD_DIG_BYTE(n) (15 - (n/2))
@@ -2495,6 +2560,33 @@
}
}
+static int bcd_cmp_zero(ppc_avr_t *bcd)
+{
+ if (bcd->u64[HI_IDX] == 0 && (bcd->u64[LO_IDX] >> 4) == 0) {
+ return 1 << CRF_EQ;
+ } else {
+ return (bcd_get_sgn(bcd) == 1) ? 1 << CRF_GT : 1 << CRF_LT;
+ }
+}
+
+static uint16_t get_national_digit(ppc_avr_t *reg, int n)
+{
+#if defined(HOST_WORDS_BIGENDIAN)
+ return reg->u16[8 - n];
+#else
+ return reg->u16[n];
+#endif
+}
+
+static void set_national_digit(ppc_avr_t *reg, uint8_t val, int n)
+{
+#if defined(HOST_WORDS_BIGENDIAN)
+ reg->u16[8 - n] = val;
+#else
+ reg->u16[n] = val;
+#endif
+}
+
static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b)
{
int i;
@@ -2625,6 +2717,163 @@
return helper_bcdadd(r, a, &bcopy, ps);
}
+uint32_t helper_bcdcfn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
+{
+ int i;
+ int cr = 0;
+ uint16_t national = 0;
+ uint16_t sgnb = get_national_digit(b, 0);
+ ppc_avr_t ret = { .u64 = { 0, 0 } };
+ int invalid = (sgnb != NATIONAL_PLUS && sgnb != NATIONAL_NEG);
+
+ for (i = 1; i < 8; i++) {
+ national = get_national_digit(b, i);
+ if (unlikely(national < 0x30 || national > 0x39)) {
+ invalid = 1;
+ break;
+ }
+
+ bcd_put_digit(&ret, national & 0xf, i);
+ }
+
+ if (sgnb == NATIONAL_PLUS) {
+ bcd_put_digit(&ret, (ps == 0) ? BCD_PLUS_PREF_1 : BCD_PLUS_PREF_2, 0);
+ } else {
+ bcd_put_digit(&ret, BCD_NEG_PREF, 0);
+ }
+
+ cr = bcd_cmp_zero(&ret);
+
+ if (unlikely(invalid)) {
+ cr = 1 << CRF_SO;
+ }
+
+ *r = ret;
+
+ return cr;
+}
+
+uint32_t helper_bcdctn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
+{
+ int i;
+ int cr = 0;
+ int sgnb = bcd_get_sgn(b);
+ int invalid = (sgnb == 0);
+ ppc_avr_t ret = { .u64 = { 0, 0 } };
+
+ int ox_flag = (b->u64[HI_IDX] != 0) || ((b->u64[LO_IDX] >> 32) != 0);
+
+ for (i = 1; i < 8; i++) {
+ set_national_digit(&ret, 0x30 + bcd_get_digit(b, i, &invalid), i);
+
+ if (unlikely(invalid)) {
+ break;
+ }
+ }
+ set_national_digit(&ret, (sgnb == -1) ? NATIONAL_NEG : NATIONAL_PLUS, 0);
+
+ cr = bcd_cmp_zero(b);
+
+ if (ox_flag) {
+ cr |= 1 << CRF_SO;
+ }
+
+ if (unlikely(invalid)) {
+ cr = 1 << CRF_SO;
+ }
+
+ *r = ret;
+
+ return cr;
+}
+
+uint32_t helper_bcdcfz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
+{
+ int i;
+ int cr = 0;
+ int invalid = 0;
+ int zone_digit = 0;
+ int zone_lead = ps ? 0xF : 0x3;
+ int digit = 0;
+ ppc_avr_t ret = { .u64 = { 0, 0 } };
+ int sgnb = b->u8[BCD_DIG_BYTE(0)] >> 4;
+
+ if (unlikely((sgnb < 0xA) && ps)) {
+ invalid = 1;
+ }
+
+ for (i = 0; i < 16; i++) {
+ zone_digit = (i * 2) ? b->u8[BCD_DIG_BYTE(i * 2)] >> 4 : zone_lead;
+ digit = b->u8[BCD_DIG_BYTE(i * 2)] & 0xF;
+ if (unlikely(zone_digit != zone_lead || digit > 0x9)) {
+ invalid = 1;
+ break;
+ }
+
+ bcd_put_digit(&ret, digit, i + 1);
+ }
+
+ if ((ps && (sgnb == 0xB || sgnb == 0xD)) ||
+ (!ps && (sgnb & 0x4))) {
+ bcd_put_digit(&ret, BCD_NEG_PREF, 0);
+ } else {
+ bcd_put_digit(&ret, BCD_PLUS_PREF_1, 0);
+ }
+
+ cr = bcd_cmp_zero(&ret);
+
+ if (unlikely(invalid)) {
+ cr = 1 << CRF_SO;
+ }
+
+ *r = ret;
+
+ return cr;
+}
+
+uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
+{
+ int i;
+ int cr = 0;
+ uint8_t digit = 0;
+ int sgnb = bcd_get_sgn(b);
+ int zone_lead = (ps) ? 0xF0 : 0x30;
+ int invalid = (sgnb == 0);
+ ppc_avr_t ret = { .u64 = { 0, 0 } };
+
+ int ox_flag = ((b->u64[HI_IDX] >> 4) != 0);
+
+ for (i = 0; i < 16; i++) {
+ digit = bcd_get_digit(b, i + 1, &invalid);
+
+ if (unlikely(invalid)) {
+ break;
+ }
+
+ ret.u8[BCD_DIG_BYTE(i * 2)] = zone_lead + digit;
+ }
+
+ if (ps) {
+ bcd_put_digit(&ret, (sgnb == 1) ? 0xC : 0xD, 1);
+ } else {
+ bcd_put_digit(&ret, (sgnb == 1) ? 0x3 : 0x7, 1);
+ }
+
+ cr = bcd_cmp_zero(b);
+
+ if (ox_flag) {
+ cr |= 1 << CRF_SO;
+ }
+
+ if (unlikely(invalid)) {
+ cr = 1 << CRF_SO;
+ }
+
+ *r = ret;
+
+ return cr;
+}
+
void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a)
{
int i;
diff --git a/target-ppc/internal.h b/target-ppc/internal.h
new file mode 100644
index 0000000..1ff4896
--- /dev/null
+++ b/target-ppc/internal.h
@@ -0,0 +1,50 @@
+/*
+ * PowerPC interal definitions for qemu.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PPC_INTERNAL_H
+#define PPC_INTERNAL_H
+
+#define FUNC_MASK(name, ret_type, size, max_val) \
+static inline ret_type name(uint##size##_t start, \
+ uint##size##_t end) \
+{ \
+ ret_type ret, max_bit = size - 1; \
+ \
+ if (likely(start == 0)) { \
+ ret = max_val << (max_bit - end); \
+ } else if (likely(end == max_bit)) { \
+ ret = max_val >> start; \
+ } else { \
+ ret = (((uint##size##_t)(-1ULL)) >> (start)) ^ \
+ (((uint##size##_t)(-1ULL) >> (end)) >> 1); \
+ if (unlikely(start > end)) { \
+ return ~ret; \
+ } \
+ } \
+ \
+ return ret; \
+}
+
+#if defined(TARGET_PPC64)
+FUNC_MASK(MASK, target_ulong, 64, UINT64_MAX);
+#else
+FUNC_MASK(MASK, target_ulong, 32, UINT32_MAX);
+#endif
+FUNC_MASK(mask_u32, uint32_t, 32, UINT32_MAX);
+FUNC_MASK(mask_u64, uint64_t, 64, UINT64_MAX);
+
+#endif /* PPC_INTERNAL_H */
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 54f35e9..59e9552 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
+#include "internal.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg-op.h"
@@ -561,34 +562,6 @@
/* DFP Z23-form */
EXTRACT_HELPER(RMC, 9, 2)
-/* Create a mask between <start> and <end> bits */
-static inline target_ulong MASK(uint32_t start, uint32_t end)
-{
- target_ulong ret;
-
-#if defined(TARGET_PPC64)
- if (likely(start == 0)) {
- ret = UINT64_MAX << (63 - end);
- } else if (likely(end == 63)) {
- ret = UINT64_MAX >> start;
- }
-#else
- if (likely(start == 0)) {
- ret = UINT32_MAX << (31 - end);
- } else if (likely(end == 31)) {
- ret = UINT32_MAX >> start;
- }
-#endif
- else {
- ret = (((target_ulong)(-1ULL)) >> (start)) ^
- (((target_ulong)(-1ULL) >> (end)) >> 1);
- if (unlikely(start > end))
- return ~ret;
- }
-
- return ret;
-}
-
EXTRACT_HELPER_SPLIT(xT, 0, 1, 21, 5);
EXTRACT_HELPER_SPLIT(xS, 0, 1, 21, 5);
EXTRACT_HELPER_SPLIT(xA, 2, 1, 16, 5);
diff --git a/target-ppc/translate/vmx-impl.inc.c b/target-ppc/translate/vmx-impl.inc.c
index fc612d9..7143eb3 100644
--- a/target-ppc/translate/vmx-impl.inc.c
+++ b/target-ppc/translate/vmx-impl.inc.c
@@ -442,6 +442,9 @@
GEN_VXFORM(vslb, 2, 4);
GEN_VXFORM(vslh, 2, 5);
GEN_VXFORM(vslw, 2, 6);
+GEN_VXFORM(vrlwnm, 2, 6);
+GEN_VXFORM_DUAL(vslw, PPC_ALTIVEC, PPC_NONE, \
+ vrlwnm, PPC_NONE, PPC2_ISA300)
GEN_VXFORM(vsld, 2, 23);
GEN_VXFORM(vsrb, 2, 8);
GEN_VXFORM(vsrh, 2, 9);
@@ -488,8 +491,17 @@
GEN_VXFORM(vrlb, 2, 0);
GEN_VXFORM(vrlh, 2, 1);
GEN_VXFORM(vrlw, 2, 2);
+GEN_VXFORM(vrlwmi, 2, 2);
+GEN_VXFORM_DUAL(vrlw, PPC_ALTIVEC, PPC_NONE, \
+ vrlwmi, PPC_NONE, PPC2_ISA300)
GEN_VXFORM(vrld, 2, 3);
+GEN_VXFORM(vrldmi, 2, 3);
+GEN_VXFORM_DUAL(vrld, PPC_NONE, PPC2_ALTIVEC_207, \
+ vrldmi, PPC_NONE, PPC2_ISA300)
GEN_VXFORM(vsl, 2, 7);
+GEN_VXFORM(vrldnm, 2, 7);
+GEN_VXFORM_DUAL(vsl, PPC_ALTIVEC, PPC_NONE, \
+ vrldnm, PPC_NONE, PPC2_ISA300)
GEN_VXFORM(vsr, 2, 11);
GEN_VXFORM_ENV(vpkuhum, 7, 0);
GEN_VXFORM_ENV(vpkuwum, 7, 1);
@@ -693,6 +705,9 @@
GEN_VXFORM_NOA_ENV(vrfin, 5, 8);
GEN_VXFORM_NOA_ENV(vrfip, 5, 10);
GEN_VXFORM_NOA_ENV(vrfiz, 5, 9);
+GEN_VXFORM_NOA(vprtybw, 1, 24);
+GEN_VXFORM_NOA(vprtybd, 1, 24);
+GEN_VXFORM_NOA(vprtybq, 1, 24);
#define GEN_VXFORM_SIMM(name, opc2, opc3) \
static void glue(gen_, name)(DisasContext *ctx) \
@@ -945,8 +960,79 @@
tcg_temp_free_i32(ps); \
}
+#define GEN_BCD2(op) \
+static void gen_##op(DisasContext *ctx) \
+{ \
+ TCGv_ptr rd, rb; \
+ TCGv_i32 ps; \
+ \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ \
+ rb = gen_avr_ptr(rB(ctx->opcode)); \
+ rd = gen_avr_ptr(rD(ctx->opcode)); \
+ \
+ ps = tcg_const_i32((ctx->opcode & 0x200) != 0); \
+ \
+ gen_helper_##op(cpu_crf[6], rd, rb, ps); \
+ \
+ tcg_temp_free_ptr(rb); \
+ tcg_temp_free_ptr(rd); \
+ tcg_temp_free_i32(ps); \
+}
+
GEN_BCD(bcdadd)
GEN_BCD(bcdsub)
+GEN_BCD2(bcdcfn)
+GEN_BCD2(bcdctn)
+GEN_BCD2(bcdcfz)
+GEN_BCD2(bcdctz)
+
+static void gen_xpnd04_1(DisasContext *ctx)
+{
+ switch (opc4(ctx->opcode)) {
+ case 4:
+ gen_bcdctz(ctx);
+ break;
+ case 5:
+ gen_bcdctn(ctx);
+ break;
+ case 6:
+ gen_bcdcfz(ctx);
+ break;
+ case 7:
+ gen_bcdcfn(ctx);
+ break;
+ default:
+ gen_invalid(ctx);
+ break;
+ }
+}
+
+static void gen_xpnd04_2(DisasContext *ctx)
+{
+ switch (opc4(ctx->opcode)) {
+ case 4:
+ gen_bcdctz(ctx);
+ break;
+ case 6:
+ gen_bcdcfz(ctx);
+ break;
+ case 7:
+ gen_bcdcfn(ctx);
+ break;
+ default:
+ gen_invalid(ctx);
+ break;
+ }
+}
+
+GEN_VXFORM_DUAL(vsubcuw, PPC_ALTIVEC, PPC_NONE, \
+ xpnd04_1, PPC_NONE, PPC2_ISA300)
+GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \
+ xpnd04_2, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_DUAL(vsububm, PPC_ALTIVEC, PPC_NONE, \
bcdadd, PPC_NONE, PPC2_ALTIVEC_207)
@@ -1023,3 +1109,5 @@
#undef GEN_VXFORM_NOA
#undef GEN_VXFORM_UIMM
#undef GEN_VAFORM_PAIRED
+
+#undef GEN_BCD2
diff --git a/target-ppc/translate/vmx-ops.inc.c b/target-ppc/translate/vmx-ops.inc.c
index cc7ed7e..f02b3be 100644
--- a/target-ppc/translate/vmx-ops.inc.c
+++ b/target-ppc/translate/vmx-ops.inc.c
@@ -107,7 +107,7 @@
GEN_VXFORM_207(vmulesw, 4, 14),
GEN_VXFORM(vslb, 2, 4),
GEN_VXFORM(vslh, 2, 5),
-GEN_VXFORM(vslw, 2, 6),
+GEN_VXFORM_DUAL(vslw, vrlwnm, 2, 6, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM_207(vsld, 2, 23),
GEN_VXFORM(vsrb, 2, 8),
GEN_VXFORM(vsrh, 2, 9),
@@ -122,7 +122,11 @@
GEN_VXFORM(vslo, 6, 16),
GEN_VXFORM(vsro, 6, 17),
GEN_VXFORM(vaddcuw, 0, 6),
-GEN_VXFORM(vsubcuw, 0, 22),
+GEN_HANDLER_E_2(vprtybw, 0x4, 0x1, 0x18, 8, 0, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E_2(vprtybd, 0x4, 0x1, 0x18, 9, 0, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E_2(vprtybq, 0x4, 0x1, 0x18, 10, 0, PPC_NONE, PPC2_ISA300),
+
+GEN_VXFORM_DUAL(vsubcuw, xpnd04_1, 0, 22, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM(vadduws, 0, 10),
@@ -134,7 +138,7 @@
GEN_VXFORM(vsubuws, 0, 26),
GEN_VXFORM(vsubsbs, 0, 28),
GEN_VXFORM(vsubshs, 0, 29),
-GEN_VXFORM(vsubsws, 0, 30),
+GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM_207(vadduqm, 0, 4),
GEN_VXFORM_207(vaddcuq, 0, 5),
GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207),
@@ -143,9 +147,9 @@
GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207),
GEN_VXFORM(vrlb, 2, 0),
GEN_VXFORM(vrlh, 2, 1),
-GEN_VXFORM(vrlw, 2, 2),
-GEN_VXFORM_207(vrld, 2, 3),
-GEN_VXFORM(vsl, 2, 7),
+GEN_VXFORM_DUAL(vrlw, vrlwmi, 2, 2, PPC_ALTIVEC, PPC_NONE),
+GEN_VXFORM_DUAL(vrld, vrldmi, 2, 3, PPC_NONE, PPC2_ALTIVEC_207),
+GEN_VXFORM_DUAL(vsl, vrldnm, 2, 7, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM(vsr, 2, 11),
GEN_VXFORM(vpkuhum, 7, 0),
GEN_VXFORM(vpkuwum, 7, 1),
diff --git a/tests/Makefile.include b/tests/Makefile.include
index de51634..e98d3b6 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -270,6 +270,7 @@
check-qtest-ppc64-y += tests/endianness-test$(EXESUF)
check-qtest-ppc64-y += tests/boot-order-test$(EXESUF)
check-qtest-ppc64-y += tests/prom-env-test$(EXESUF)
+check-qtest-ppc64-y += tests/pnv-xscom-test$(EXESUF)
check-qtest-ppc64-y += tests/drive_del-test$(EXESUF)
check-qtest-ppc64-y += tests/postcopy-test$(EXESUF)
check-qtest-ppc64-y += tests/boot-serial-test$(EXESUF)
@@ -644,6 +645,7 @@
tests/e1000e-test$(EXESUF): tests/e1000e-test.o $(libqos-pc-obj-y)
tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y)
tests/pcnet-test$(EXESUF): tests/pcnet-test.o
+tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o
tests/eepro100-test$(EXESUF): tests/eepro100-test.o
tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
tests/ne2000-test$(EXESUF): tests/ne2000-test.o
diff --git a/tests/ahci-test.c b/tests/ahci-test.c
index 70bcafa..ef17629 100644
--- a/tests/ahci-test.c
+++ b/tests/ahci-test.c
@@ -1473,8 +1473,13 @@
const AHCIOpts *opts)
{
unsigned char *tx = opts->opaque;
- unsigned char *rx = g_malloc0(opts->size);
+ unsigned char *rx;
+ if (!opts->size) {
+ return 0;
+ }
+
+ rx = g_malloc0(opts->size);
bufread(opts->buffer, rx, opts->size);
g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0);
g_free(rx);
@@ -1482,7 +1487,8 @@
return 0;
}
-static void ahci_test_cdrom(int nsectors, bool dma)
+static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd,
+ bool override_bcl, uint16_t bcl)
{
AHCIQState *ahci;
unsigned char *tx;
@@ -1493,10 +1499,13 @@
.atapi = true,
.atapi_dma = dma,
.post_cb = ahci_cb_cmp_buff,
+ .set_bcl = override_bcl,
+ .bcl = bcl,
};
+ uint64_t iso_size = ATAPI_SECTOR_SIZE * (nsectors + 1);
/* Prepare ISO and fill 'tx' buffer */
- fd = prepare_iso(1024 * 1024, &tx, &iso);
+ fd = prepare_iso(iso_size, &tx, &iso);
opts.opaque = tx;
/* Standard startup wonkery, but use ide-cd and our special iso file */
@@ -1505,7 +1514,7 @@
"-device ide-cd,drive=drive0 ", iso);
/* Build & Send AHCI command */
- ahci_exec(ahci, ahci_port_select(ahci), CMD_ATAPI_READ_10, &opts);
+ ahci_exec(ahci, ahci_port_select(ahci), cmd, &opts);
/* Cleanup */
g_free(tx);
@@ -1513,24 +1522,133 @@
remove_iso(fd, iso);
}
+static void ahci_test_cdrom_read10(int nsectors, bool dma)
+{
+ ahci_test_cdrom(nsectors, dma, CMD_ATAPI_READ_10, false, 0);
+}
+
static void test_cdrom_dma(void)
{
- ahci_test_cdrom(1, true);
+ ahci_test_cdrom_read10(1, true);
}
static void test_cdrom_dma_multi(void)
{
- ahci_test_cdrom(3, true);
+ ahci_test_cdrom_read10(3, true);
}
static void test_cdrom_pio(void)
{
- ahci_test_cdrom(1, false);
+ ahci_test_cdrom_read10(1, false);
}
static void test_cdrom_pio_multi(void)
{
- ahci_test_cdrom(3, false);
+ ahci_test_cdrom_read10(3, false);
+}
+
+/* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0
+ * completes as a NOP instead of erroring out. */
+static void test_atapi_bcl(void)
+{
+ ahci_test_cdrom(0, false, CMD_ATAPI_READ_CD, true, 0);
+}
+
+
+static void atapi_wait_tray(bool open)
+{
+ QDict *rsp = qmp_eventwait_ref("DEVICE_TRAY_MOVED");
+ QDict *data = qdict_get_qdict(rsp, "data");
+ if (open) {
+ g_assert(qdict_get_bool(data, "tray-open"));
+ } else {
+ g_assert(!qdict_get_bool(data, "tray-open"));
+ }
+ QDECREF(rsp);
+}
+
+static void test_atapi_tray(void)
+{
+ AHCIQState *ahci;
+ unsigned char *tx;
+ char *iso;
+ int fd;
+ uint8_t port, sense, asc;
+ uint64_t iso_size = ATAPI_SECTOR_SIZE;
+ QDict *rsp;
+
+ fd = prepare_iso(iso_size, &tx, &iso);
+ ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw "
+ "-M q35 "
+ "-device ide-cd,drive=drive0 ", iso);
+ port = ahci_port_select(ahci);
+
+ ahci_atapi_eject(ahci, port);
+ atapi_wait_tray(true);
+
+ ahci_atapi_load(ahci, port);
+ atapi_wait_tray(false);
+
+ /* Remove media */
+ qmp_async("{'execute': 'blockdev-open-tray', "
+ "'arguments': {'device': 'drive0'}}");
+ atapi_wait_tray(true);
+ rsp = qmp_receive();
+ QDECREF(rsp);
+
+ qmp_discard_response("{'execute': 'x-blockdev-remove-medium', "
+ "'arguments': {'device': 'drive0'}}");
+
+ /* Test the tray without a medium */
+ ahci_atapi_load(ahci, port);
+ atapi_wait_tray(false);
+
+ ahci_atapi_eject(ahci, port);
+ atapi_wait_tray(true);
+
+ /* Re-insert media */
+ qmp_discard_response("{'execute': 'blockdev-add', "
+ "'arguments': {'node-name': 'node0', "
+ "'driver': 'raw', "
+ "'file': { 'driver': 'file', "
+ "'filename': %s }}}", iso);
+ qmp_discard_response("{'execute': 'x-blockdev-insert-medium',"
+ "'arguments': { 'device': 'drive0', "
+ "'node-name': 'node0' }}");
+
+ /* Again, the event shows up first */
+ qmp_async("{'execute': 'blockdev-close-tray', "
+ "'arguments': {'device': 'drive0'}}");
+ atapi_wait_tray(false);
+ rsp = qmp_receive();
+ QDECREF(rsp);
+
+ /* Now, to convince ATAPI we understand the media has changed... */
+ ahci_atapi_test_ready(ahci, port, false, SENSE_NOT_READY);
+ ahci_atapi_get_sense(ahci, port, &sense, &asc);
+ g_assert_cmpuint(sense, ==, SENSE_NOT_READY);
+ g_assert_cmpuint(asc, ==, ASC_MEDIUM_NOT_PRESENT);
+
+ ahci_atapi_test_ready(ahci, port, false, SENSE_UNIT_ATTENTION);
+ ahci_atapi_get_sense(ahci, port, &sense, &asc);
+ g_assert_cmpuint(sense, ==, SENSE_UNIT_ATTENTION);
+ g_assert_cmpuint(asc, ==, ASC_MEDIUM_MAY_HAVE_CHANGED);
+
+ ahci_atapi_test_ready(ahci, port, true, SENSE_NO_SENSE);
+ ahci_atapi_get_sense(ahci, port, &sense, &asc);
+ g_assert_cmpuint(sense, ==, SENSE_NO_SENSE);
+
+ /* Final tray test. */
+ ahci_atapi_eject(ahci, port);
+ atapi_wait_tray(true);
+
+ ahci_atapi_load(ahci, port);
+ atapi_wait_tray(false);
+
+ /* Cleanup */
+ g_free(tx);
+ ahci_shutdown(ahci);
+ remove_iso(fd, iso);
}
/******************************************************************************/
@@ -1822,6 +1940,9 @@
qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio);
qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi);
+ qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl);
+ qtest_add_func("/ahci/cdrom/eject", test_atapi_tray);
+
ret = g_test_run();
/* Cleanup */
diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c
index d98c564..44c82e5 100644
--- a/tests/boot-serial-test.c
+++ b/tests/boot-serial-test.c
@@ -29,6 +29,7 @@
{ "ppc64", "ppce500", "", "U-Boot" },
{ "ppc64", "prep", "", "Open Hack'Ware BIOS" },
{ "ppc64", "pseries", "", "Open Firmware" },
+ { "ppc64", "powernv", "-cpu POWER9", "SkiBoot" },
{ "i386", "isapc", "-cpu qemu32 -device sga", "SGABIOS" },
{ "i386", "pc", "-device sga", "SGABIOS" },
{ "i386", "q35", "-device sga", "SGABIOS" },
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c
index 5180d65..1ca7f45 100644
--- a/tests/libqos/ahci.c
+++ b/tests/libqos/ahci.c
@@ -86,6 +86,7 @@
uint8_t name;
uint8_t port;
uint8_t slot;
+ uint8_t errors;
uint32_t interrupts;
uint64_t xbytes;
uint32_t prd_size;
@@ -402,12 +403,14 @@
/**
* Check a port for errors.
*/
-void ahci_port_check_error(AHCIQState *ahci, uint8_t port)
+void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
+ uint32_t imask, uint8_t emask)
{
uint32_t reg;
/* The upper 9 bits of the IS register all indicate errors. */
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
+ reg &= ~imask;
reg >>= 23;
g_assert_cmphex(reg, ==, 0);
@@ -417,8 +420,13 @@
/* The TFD also has two error sections. */
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
- ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
- ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR);
+ if (!emask) {
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
+ } else {
+ ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR);
+ }
+ ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~emask << 8));
+ ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (emask << 8));
}
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
@@ -633,7 +641,8 @@
/* Command creation */
if (opts->atapi) {
- cmd = ahci_atapi_command_create(op);
+ uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE;
+ cmd = ahci_atapi_command_create(op, bcl);
if (opts->atapi_dma) {
ahci_command_enable_atapi_dma(cmd);
}
@@ -864,19 +873,82 @@
return cmd;
}
-AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd)
+AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl)
{
AHCICommand *cmd = ahci_command_create(CMD_PACKET);
cmd->atapi_cmd = g_malloc0(16);
cmd->atapi_cmd[0] = scsi_cmd;
- /* ATAPI needs a PIO transfer chunk size set inside of the LBA registers.
- * The block/sector size is a natural default. */
- cmd->fis.lba_lo[1] = ATAPI_SECTOR_SIZE >> 8 & 0xFF;
- cmd->fis.lba_lo[2] = ATAPI_SECTOR_SIZE & 0xFF;
-
+ stw_le_p(&cmd->fis.lba_lo[1], bcl);
return cmd;
}
+void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port,
+ bool ready, uint8_t expected_sense)
+{
+ AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0);
+ ahci_command_set_size(cmd, 0);
+ if (!ready) {
+ cmd->interrupts |= AHCI_PX_IS_TFES;
+ cmd->errors |= expected_sense << 4;
+ }
+ ahci_command_commit(ahci, cmd, port);
+ ahci_command_issue(ahci, cmd);
+ ahci_command_verify(ahci, cmd);
+ ahci_command_free(cmd);
+}
+
+static int copy_buffer(AHCIQState *ahci, AHCICommand *cmd,
+ const AHCIOpts *opts)
+{
+ unsigned char *rx = opts->opaque;
+ bufread(opts->buffer, rx, opts->size);
+ return 0;
+}
+
+void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
+ uint8_t *sense, uint8_t *asc)
+{
+ unsigned char *rx;
+ AHCIOpts opts = {
+ .size = 18,
+ .atapi = true,
+ .post_cb = copy_buffer,
+ };
+ rx = g_malloc(18);
+ opts.opaque = rx;
+
+ ahci_exec(ahci, port, CMD_ATAPI_REQUEST_SENSE, &opts);
+
+ *sense = rx[2];
+ *asc = rx[12];
+
+ g_free(rx);
+}
+
+void ahci_atapi_eject(AHCIQState *ahci, uint8_t port)
+{
+ AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0);
+ ahci_command_set_size(cmd, 0);
+
+ cmd->atapi_cmd[4] = 0x02; /* loej = true */
+ ahci_command_commit(ahci, cmd, port);
+ ahci_command_issue(ahci, cmd);
+ ahci_command_verify(ahci, cmd);
+ ahci_command_free(cmd);
+}
+
+void ahci_atapi_load(AHCIQState *ahci, uint8_t port)
+{
+ AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0);
+ ahci_command_set_size(cmd, 0);
+
+ cmd->atapi_cmd[4] = 0x03; /* loej,start = true */
+ ahci_command_commit(ahci, cmd, port);
+ ahci_command_issue(ahci, cmd);
+ ahci_command_verify(ahci, cmd);
+ ahci_command_free(cmd);
+}
+
void ahci_command_free(AHCICommand *cmd)
{
g_free(cmd->atapi_cmd);
@@ -901,12 +973,22 @@
switch (cbd[0]) {
case CMD_ATAPI_READ_10:
+ case CMD_ATAPI_READ_CD:
g_assert_cmpuint(lba, <=, UINT32_MAX);
stl_be_p(&cbd[2], lba);
break;
+ case CMD_ATAPI_REQUEST_SENSE:
+ case CMD_ATAPI_TEST_UNIT_READY:
+ case CMD_ATAPI_START_STOP_UNIT:
+ g_assert_cmpuint(lba, ==, 0x00);
+ break;
default:
/* SCSI doesn't have uniform packet formats,
* so you have to add support for it manually. Sorry! */
+ fprintf(stderr, "The Libqos AHCI driver does not support the "
+ "set_offset operation for ATAPI command 0x%02x, "
+ "please add support.\n",
+ cbd[0]);
g_assert_not_reached();
}
}
@@ -951,6 +1033,7 @@
{
unsigned char *cbd = cmd->atapi_cmd;
uint64_t nsectors = xbytes / 2048;
+ uint32_t tmp;
g_assert(cbd);
switch (cbd[0]) {
@@ -958,9 +1041,28 @@
g_assert_cmpuint(nsectors, <=, UINT16_MAX);
stw_be_p(&cbd[7], nsectors);
break;
+ case CMD_ATAPI_READ_CD:
+ /* 24bit BE store */
+ g_assert_cmpuint(nsectors, <, 1ULL << 24);
+ tmp = nsectors;
+ cbd[6] = (tmp & 0xFF0000) >> 16;
+ cbd[7] = (tmp & 0xFF00) >> 8;
+ cbd[8] = (tmp & 0xFF);
+ break;
+ case CMD_ATAPI_REQUEST_SENSE:
+ g_assert_cmpuint(xbytes, <=, UINT8_MAX);
+ cbd[4] = (uint8_t)xbytes;
+ break;
+ case CMD_ATAPI_TEST_UNIT_READY:
+ case CMD_ATAPI_START_STOP_UNIT:
+ g_assert_cmpuint(xbytes, ==, 0);
+ break;
default:
/* SCSI doesn't have uniform packet formats,
* so you have to add support for it manually. Sorry! */
+ fprintf(stderr, "The Libqos AHCI driver does not support the set_size "
+ "operation for ATAPI command 0x%02x, please add support.\n",
+ cbd[0]);
g_assert_not_reached();
}
}
@@ -1105,7 +1207,7 @@
uint8_t slot = cmd->slot;
uint8_t port = cmd->port;
- ahci_port_check_error(ahci, port);
+ ahci_port_check_error(ahci, port, cmd->interrupts, cmd->errors);
ahci_port_check_interrupts(ahci, port, cmd->interrupts);
ahci_port_check_nonbusy(ahci, port, slot);
ahci_port_check_cmd_sanity(ahci, cmd);
diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h
index caaafe3..5f9627b 100644
--- a/tests/libqos/ahci.h
+++ b/tests/libqos/ahci.h
@@ -287,7 +287,22 @@
/* ATAPI Commands */
enum {
- CMD_ATAPI_READ_10 = 0x28,
+ CMD_ATAPI_TEST_UNIT_READY = 0x00,
+ CMD_ATAPI_REQUEST_SENSE = 0x03,
+ CMD_ATAPI_START_STOP_UNIT = 0x1b,
+ CMD_ATAPI_READ_10 = 0x28,
+ CMD_ATAPI_READ_CD = 0xbe,
+};
+
+enum {
+ SENSE_NO_SENSE = 0x00,
+ SENSE_NOT_READY = 0x02,
+ SENSE_UNIT_ATTENTION = 0x06,
+};
+
+enum {
+ ASC_MEDIUM_MAY_HAVE_CHANGED = 0x28,
+ ASC_MEDIUM_NOT_PRESENT = 0x3a,
};
/* AHCI Command Header Flags & Masks*/
@@ -462,12 +477,14 @@
/* Options to ahci_exec */
typedef struct AHCIOpts {
- size_t size;
- unsigned prd_size;
- uint64_t lba;
- uint64_t buffer;
- bool atapi;
- bool atapi_dma;
+ size_t size; /* Size of transfer */
+ unsigned prd_size; /* Size per-each PRD */
+ bool set_bcl; /* Override the default BCL of ATAPI_SECTOR_SIZE */
+ unsigned bcl; /* Byte Count Limit, for ATAPI PIO */
+ uint64_t lba; /* Starting LBA offset */
+ uint64_t buffer; /* Pointer to source or destination guest buffer */
+ bool atapi; /* ATAPI command? */
+ bool atapi_dma; /* Use DMA for ATAPI? */
bool error;
int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
@@ -573,7 +590,8 @@
void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
/* AHCI sanity check routines */
-void ahci_port_check_error(AHCIQState *ahci, uint8_t port);
+void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
+ uint32_t imask, uint8_t emask);
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
uint32_t intr_mask);
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
@@ -596,10 +614,16 @@
void *buffer, size_t bufsize, uint64_t sector);
void ahci_exec(AHCIQState *ahci, uint8_t port,
uint8_t op, const AHCIOpts *opts);
+void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port, bool ready,
+ uint8_t expected_sense);
+void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
+ uint8_t *sense, uint8_t *asc);
+void ahci_atapi_eject(AHCIQState *ahci, uint8_t port);
+void ahci_atapi_load(AHCIQState *ahci, uint8_t port);
/* Command: Fine-grained lifecycle */
AHCICommand *ahci_command_create(uint8_t command_name);
-AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd);
+AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl);
void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port);
void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd);
void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd);
diff --git a/tests/libqtest.c b/tests/libqtest.c
index d4e6bff..6f69752 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -533,7 +533,7 @@
QDECREF(response);
}
-void qtest_qmp_eventwait(QTestState *s, const char *event)
+QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event)
{
QDict *response;
@@ -541,13 +541,20 @@
response = qtest_qmp_receive(s);
if ((qdict_haskey(response, "event")) &&
(strcmp(qdict_get_str(response, "event"), event) == 0)) {
- QDECREF(response);
- break;
+ return response;
}
QDECREF(response);
}
}
+void qtest_qmp_eventwait(QTestState *s, const char *event)
+{
+ QDict *response;
+
+ response = qtest_qmp_eventwait_ref(s, event);
+ QDECREF(response);
+}
+
char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap)
{
char *cmd;
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 0224f06..90f182e 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -114,6 +114,16 @@
void qtest_qmp_eventwait(QTestState *s, const char *event);
/**
+ * qtest_qmp_eventwait_ref:
+ * @s: #QTestState instance to operate on.
+ * @s: #event event to wait for.
+ *
+ * Continuosly polls for QMP responses until it receives the desired event.
+ * Returns a copy of the event for further investigation.
+ */
+QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event);
+
+/**
* qtest_hmpv:
* @s: #QTestState instance to operate on.
* @fmt...: HMP command to send to QEMU
@@ -559,6 +569,18 @@
}
/**
+ * qmp_eventwait_ref:
+ * @s: #event event to wait for.
+ *
+ * Continuosly polls for QMP responses until it receives the desired event.
+ * Returns a copy of the event for further investigation.
+ */
+static inline QDict *qmp_eventwait_ref(const char *event)
+{
+ return qtest_qmp_eventwait_ref(global_qtest, event);
+}
+
+/**
* hmp:
* @fmt...: HMP command to send to QEMU
*
diff --git a/tests/pnv-xscom-test.c b/tests/pnv-xscom-test.c
new file mode 100644
index 0000000..5951da1
--- /dev/null
+++ b/tests/pnv-xscom-test.c
@@ -0,0 +1,140 @@
+/*
+ * QTest testcase for PowerNV XSCOM bus
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+
+#include "libqtest.h"
+
+typedef enum PnvChipType {
+ PNV_CHIP_POWER8E, /* AKA Murano (default) */
+ PNV_CHIP_POWER8, /* AKA Venice */
+ PNV_CHIP_POWER8NVL, /* AKA Naples */
+ PNV_CHIP_POWER9, /* AKA Nimbus */
+} PnvChipType;
+
+typedef struct PnvChip {
+ PnvChipType chip_type;
+ const char *cpu_model;
+ uint64_t xscom_base;
+ uint64_t xscom_core_base;
+ uint64_t cfam_id;
+ uint32_t first_core;
+} PnvChip;
+
+static const PnvChip pnv_chips[] = {
+ {
+ .chip_type = PNV_CHIP_POWER8,
+ .cpu_model = "POWER8",
+ .xscom_base = 0x0003fc0000000000ull,
+ .xscom_core_base = 0x10000000ull,
+ .cfam_id = 0x220ea04980000000ull,
+ .first_core = 0x1,
+ }, {
+ .chip_type = PNV_CHIP_POWER8NVL,
+ .cpu_model = "POWER8NVL",
+ .xscom_base = 0x0003fc0000000000ull,
+ .xscom_core_base = 0x10000000ull,
+ .cfam_id = 0x120d304980000000ull,
+ .first_core = 0x1,
+ }, {
+ .chip_type = PNV_CHIP_POWER9,
+ .cpu_model = "POWER9",
+ .xscom_base = 0x000603fc00000000ull,
+ .xscom_core_base = 0x0ull,
+ .cfam_id = 0x100d104980000000ull,
+ .first_core = 0x20,
+ },
+};
+
+static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba)
+{
+ uint64_t addr = chip->xscom_base;
+
+ if (chip->chip_type == PNV_CHIP_POWER9) {
+ addr |= ((uint64_t) pcba << 3);
+ } else {
+ addr |= (((uint64_t) pcba << 4) & ~0xffull) |
+ (((uint64_t) pcba << 3) & 0x78);
+ }
+ return addr;
+}
+
+static uint64_t pnv_xscom_read(const PnvChip *chip, uint32_t pcba)
+{
+ return readq(pnv_xscom_addr(chip, pcba));
+}
+
+static void test_xscom_cfam_id(const PnvChip *chip)
+{
+ uint64_t f000f = pnv_xscom_read(chip, 0xf000f);
+
+ g_assert_cmphex(f000f, ==, chip->cfam_id);
+}
+
+static void test_cfam_id(const void *data)
+{
+ char *args;
+ const PnvChip *chip = data;
+
+ args = g_strdup_printf("-M powernv,accel=tcg -cpu %s", chip->cpu_model);
+
+ qtest_start(args);
+ test_xscom_cfam_id(chip);
+ qtest_quit(global_qtest);
+
+ g_free(args);
+}
+
+#define PNV_XSCOM_EX_CORE_BASE(chip, i) \
+ ((chip)->xscom_core_base | (((uint64_t)i) << 24))
+#define PNV_XSCOM_EX_DTS_RESULT0 0x50000
+
+static void test_xscom_core(const PnvChip *chip)
+{
+ uint32_t first_core_dts0 =
+ PNV_XSCOM_EX_CORE_BASE(chip, chip->first_core) |
+ PNV_XSCOM_EX_DTS_RESULT0;
+ uint64_t dts0 = pnv_xscom_read(chip, first_core_dts0);
+
+ g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull);
+}
+
+static void test_core(const void *data)
+{
+ char *args;
+ const PnvChip *chip = data;
+
+ args = g_strdup_printf("-M powernv,accel=tcg -cpu %s", chip->cpu_model);
+
+ qtest_start(args);
+ test_xscom_core(chip);
+ qtest_quit(global_qtest);
+
+ g_free(args);
+}
+
+static void add_test(const char *name, void (*test)(const void *data))
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pnv_chips); i++) {
+ char *tname = g_strdup_printf("pnv-xscom/%s/%s", name,
+ pnv_chips[i].cpu_model);
+ qtest_add_data_func(tname, &pnv_chips[i], test);
+ g_free(tname);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ add_test("cfam_id", test_cfam_id);
+ add_test("core", test_core);
+ return g_test_run();
+}
diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109
index 280ed27..927151a 100755
--- a/tests/qemu-iotests/109
+++ b/tests/qemu-iotests/109
@@ -62,6 +62,9 @@
"return"
_send_qemu_cmd $QEMU_HANDLE '' "$qmp_event"
+ if test "$qmp_event" = BLOCK_JOB_ERROR; then
+ _send_qemu_cmd $QEMU_HANDLE '' "BLOCK_JOB_COMPLETED"
+ fi
_send_qemu_cmd $QEMU_HANDLE '{"execute":"query-block-jobs"}' "return"
_cleanup_qemu
}
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index f06938e..d0d2c2b 100644
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -395,19 +395,7 @@
self.check_backups()
- def test_transaction_failure(self):
- '''Test: Verify backups made from a transaction that partially fails.
-
- Add a second drive with its own unique pattern, and add a bitmap to each
- drive. Use blkdebug to interfere with the backup on just one drive and
- attempt to create a coherent incremental backup across both drives.
-
- verify a failure in one but not both, then delete the failed stubs and
- re-run the same transaction.
-
- verify that both incrementals are created successfully.
- '''
-
+ def do_transaction_failure_test(self, race=False):
# Create a second drive, with pattern:
drive1 = self.add_node('drive1')
self.img_create(drive1['file'], drive1['fmt'])
@@ -451,9 +439,10 @@
self.assertFalse(self.vm.get_qmp_events(wait=False))
# Emulate some writes
- self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
- ('0xfe', '16M', '256k'),
- ('0x64', '32736k', '64k')))
+ if not race:
+ self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
+ ('0xfe', '16M', '256k'),
+ ('0x64', '32736k', '64k')))
self.hmp_io_writes(drive1['id'], (('0xba', 0, 512),
('0xef', '16M', '256k'),
('0x46', '32736k', '64k')))
@@ -463,7 +452,8 @@
target1 = self.prepare_backup(dr1bm0)
# Ask for a new incremental backup per-each drive,
- # expecting drive1's backup to fail:
+ # expecting drive1's backup to fail. In the 'race' test,
+ # we expect drive1 to attempt to cancel the empty drive0 job.
transaction = [
transaction_drive_backup(drive0['id'], target0, sync='incremental',
format=drive0['fmt'], mode='existing',
@@ -488,9 +478,15 @@
self.assert_no_active_block_jobs()
# Delete drive0's successful target and eliminate our record of the
- # unsuccessful drive1 target. Then re-run the same transaction.
+ # unsuccessful drive1 target.
dr0bm0.del_target()
dr1bm0.del_target()
+ if race:
+ # Don't re-run the transaction, we only wanted to test the race.
+ self.vm.shutdown()
+ return
+
+ # Re-run the same transaction:
target0 = self.prepare_backup(dr0bm0)
target1 = self.prepare_backup(dr1bm0)
@@ -511,6 +507,27 @@
self.vm.shutdown()
self.check_backups()
+ def test_transaction_failure(self):
+ '''Test: Verify backups made from a transaction that partially fails.
+
+ Add a second drive with its own unique pattern, and add a bitmap to each
+ drive. Use blkdebug to interfere with the backup on just one drive and
+ attempt to create a coherent incremental backup across both drives.
+
+ verify a failure in one but not both, then delete the failed stubs and
+ re-run the same transaction.
+
+ verify that both incrementals are created successfully.
+ '''
+ self.do_transaction_failure_test()
+
+ def test_transaction_failure_race(self):
+ '''Test: Verify that transactions with jobs that have no data to
+ transfer do not cause race conditions in the cancellation of the entire
+ transaction job group.
+ '''
+ self.do_transaction_failure_test(race=True)
+
def test_sync_dirty_bitmap_missing(self):
self.assert_no_active_block_jobs()
diff --git a/tests/qemu-iotests/124.out b/tests/qemu-iotests/124.out
index 36376be..e56cae0 100644
--- a/tests/qemu-iotests/124.out
+++ b/tests/qemu-iotests/124.out
@@ -1,5 +1,5 @@
-..........
+...........
----------------------------------------------------------------------
-Ran 10 tests
+Ran 11 tests
OK
diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162
index f8eecb3..cad2bd7 100755
--- a/tests/qemu-iotests/162
+++ b/tests/qemu-iotests/162
@@ -35,6 +35,9 @@
_supported_fmt generic
_supported_os Linux
+test_ssh=$($QEMU_IMG --help | grep '^Supported formats:.* ssh\( \|$\)')
+[ "$test_ssh" = "" ] && _notrun "ssh support required"
+
echo
echo '=== NBD ==='
# NBD expects all of its arguments to be strings
diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common
index d60ea2c..b6274be 100644
--- a/tests/qemu-iotests/common
+++ b/tests/qemu-iotests/common
@@ -51,7 +51,7 @@
export CACHEMODE="writeback"
export QEMU_IO_OPTIONS=""
export CACHEMODE_IS_DEFAULT=true
-export QEMU_OPTIONS="-nodefaults"
+export QEMU_OPTIONS="-nodefaults -machine accel=qtest"
export VALGRIND_QEMU=
export IMGKEYSECRET=
export IMGOPTSSYNTAX=false
diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu
index 2548a87..e657361 100644
--- a/tests/qemu-iotests/common.qemu
+++ b/tests/qemu-iotests/common.qemu
@@ -155,15 +155,13 @@
if [ -z "$keep_stderr" ]; then
QEMU_NEED_PID='y'\
- ${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \
- >"${fifo_out}" \
- 2>&1 \
- <"${fifo_in}" &
+ ${QEMU} -nographic -serial none ${comm} "${@}" >"${fifo_out}" \
+ 2>&1 \
+ <"${fifo_in}" &
elif [ "$keep_stderr" = "y" ]; then
QEMU_NEED_PID='y'\
- ${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \
- >"${fifo_out}" \
- <"${fifo_in}" &
+ ${QEMU} -nographic -serial none ${comm} "${@}" >"${fifo_out}" \
+ <"${fifo_in}" &
else
exit 1
fi
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
index f9afc3b..b132e39 100644
--- a/tests/test-blockjob-txn.c
+++ b/tests/test-blockjob-txn.c
@@ -24,10 +24,6 @@
int *result;
} TestBlockJob;
-static const BlockJobDriver test_block_job_driver = {
- .instance_size = sizeof(TestBlockJob),
-};
-
static void test_block_job_complete(BlockJob *job, void *opaque)
{
BlockDriverState *bs = blk_bs(job->blk);
@@ -77,6 +73,11 @@
g_free(data);
}
+static const BlockJobDriver test_block_job_driver = {
+ .instance_size = sizeof(TestBlockJob),
+ .start = test_block_job_run,
+};
+
/* Create a block job that completes with a given return code after a given
* number of event loop iterations. The return code is stored in the given
* result pointer.
@@ -104,10 +105,9 @@
s->use_timer = use_timer;
s->rc = rc;
s->result = result;
- s->common.co = qemu_coroutine_create(test_block_job_run, s);
data->job = s;
data->result = result;
- qemu_coroutine_enter(s->common.co);
+ block_job_start(&s->common);
return &s->common;
}
diff --git a/tests/test-replication.c b/tests/test-replication.c
index 0997bd8..fac2da3 100644
--- a/tests/test-replication.c
+++ b/tests/test-replication.c
@@ -85,6 +85,8 @@
}
g_free(pattern_buf);
+ g_free(cmp_buf);
+ qemu_iovec_destroy(&qiov);
}
static void test_blk_write(BlockBackend *blk, long pattern, int64_t offset,
@@ -116,6 +118,7 @@
}
g_free(pattern_buf);
+ qemu_iovec_destroy(&qiov);
}
/*
diff --git a/tests/test-uuid.c b/tests/test-uuid.c
index 77dcdc4..d3a2791 100644
--- a/tests/test-uuid.c
+++ b/tests/test-uuid.c
@@ -161,6 +161,7 @@
}
out = qemu_uuid_unparse_strdup(&uuid_test_data[i].uuid);
g_assert_cmpstr(uuid_test_data[i].uuidstr, ==, out);
+ g_free(out);
}
}
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
index d8da26f..d2f529b 100644
--- a/tests/test-vmstate.c
+++ b/tests/test-vmstate.c
@@ -83,6 +83,13 @@
qemu_fclose(f);
}
+static void save_buffer(const uint8_t *buf, size_t buf_size)
+{
+ QEMUFile *fsave = open_test_file(true);
+ qemu_put_buffer(fsave, buf, buf_size);
+ qemu_fclose(fsave);
+}
+
static void compare_vmstate(uint8_t *wire, size_t size)
{
QEMUFile *f = open_test_file(false);
@@ -309,15 +316,13 @@
static void test_load_v1(void)
{
- QEMUFile *fsave = open_test_file(true);
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 30, /* c */
0, 0, 0, 0, 0, 0, 0, 40, /* d */
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
};
- qemu_put_buffer(fsave, buf, sizeof(buf));
- qemu_fclose(fsave);
+ save_buffer(buf, sizeof(buf));
QEMUFile *loading = open_test_file(false);
TestStruct obj = { .b = 200, .e = 500, .f = 600 };
@@ -334,7 +339,6 @@
static void test_load_v2(void)
{
- QEMUFile *fsave = open_test_file(true);
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 20, /* b */
@@ -344,8 +348,7 @@
0, 0, 0, 0, 0, 0, 0, 60, /* f */
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
};
- qemu_put_buffer(fsave, buf, sizeof(buf));
- qemu_fclose(fsave);
+ save_buffer(buf, sizeof(buf));
QEMUFile *loading = open_test_file(false);
TestStruct obj;
@@ -423,7 +426,6 @@
static void test_load_noskip(void)
{
- QEMUFile *fsave = open_test_file(true);
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 20, /* b */
@@ -433,8 +435,7 @@
0, 0, 0, 0, 0, 0, 0, 60, /* f */
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
};
- qemu_put_buffer(fsave, buf, sizeof(buf));
- qemu_fclose(fsave);
+ save_buffer(buf, sizeof(buf));
QEMUFile *loading = open_test_file(false);
TestStruct obj = { .skip_c_e = false };
@@ -451,7 +452,6 @@
static void test_load_skip(void)
{
- QEMUFile *fsave = open_test_file(true);
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 20, /* b */
@@ -459,8 +459,7 @@
0, 0, 0, 0, 0, 0, 0, 60, /* f */
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
};
- qemu_put_buffer(fsave, buf, sizeof(buf));
- qemu_fclose(fsave);
+ save_buffer(buf, sizeof(buf));
QEMUFile *loading = open_test_file(false);
TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
@@ -475,6 +474,76 @@
qemu_fclose(loading);
}
+
+typedef struct {
+ int32_t i;
+} TestStructTriv;
+
+const VMStateDescription vmsd_tst = {
+ .name = "test/tst",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(i, TestStructTriv),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define AR_SIZE 4
+
+typedef struct {
+ TestStructTriv *ar[AR_SIZE];
+} TestArrayOfPtrToStuct;
+
+const VMStateDescription vmsd_arps = {
+ .name = "test/arps",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct,
+ AR_SIZE, 0, vmsd_tst, TestStructTriv),
+ VMSTATE_END_OF_LIST()
+ }
+};
+static void test_arr_ptr_str_no0_save(void)
+{
+ TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
+ TestArrayOfPtrToStuct sample = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
+ uint8_t wire_sample[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ QEMU_VM_EOF
+ };
+
+ save_vmstate(&vmsd_arps, &sample);
+ compare_vmstate(wire_sample, sizeof(wire_sample));
+}
+
+static void test_arr_ptr_str_no0_load(void)
+{
+ TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
+ TestStructTriv ar[AR_SIZE] = {};
+ TestArrayOfPtrToStuct obj = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
+ int idx;
+ uint8_t wire_sample[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ QEMU_VM_EOF
+ };
+
+ save_buffer(wire_sample, sizeof(wire_sample));
+ SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
+ wire_sample, sizeof(wire_sample)));
+ for (idx = 0; idx < AR_SIZE; ++idx) {
+ /* compare the target array ar with the ground truth array ar_gt */
+ g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
+ }
+}
+
int main(int argc, char **argv)
{
temp_fd = mkstemp(temp_file);
@@ -489,6 +558,10 @@
g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
+ g_test_add_func("/vmstate/array/ptr/str/no0/save",
+ test_arr_ptr_str_no0_save);
+ g_test_add_func("/vmstate/array/ptr/str/no0/load",
+ test_arr_ptr_str_no0_load);
g_test_run();
close(temp_fd);
diff --git a/vl.c b/vl.c
index 319f641..d77dd86 100644
--- a/vl.c
+++ b/vl.c
@@ -4063,6 +4063,11 @@
os_daemonize();
+ if (pid_file && qemu_create_pidfile(pid_file) != 0) {
+ error_report("could not acquire pid file: %s", strerror(errno));
+ exit(1);
+ }
+
if (qemu_init_main_loop(&main_loop_err)) {
error_report_err(main_loop_err);
exit(1);
@@ -4340,11 +4345,6 @@
}
#endif
- if (pid_file && qemu_create_pidfile(pid_file) != 0) {
- error_report("could not acquire pid file: %s", strerror(errno));
- exit(1);
- }
-
if (qemu_opts_foreach(qemu_find_opts("device"),
device_help_func, NULL, NULL)) {
exit(0);