diff options
46 files changed, 1055 insertions, 278 deletions
diff --git a/block/backup.c b/block/backup.c index 7b5d8a3757..ea38733849 100644 --- a/block/backup.c +++ b/block/backup.c @@ -242,6 +242,14 @@ static void backup_abort(BlockJob *job) } } +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 void backup_drain(BlockJob *job) } } -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 @@ typedef struct { 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 @@ static void coroutine_fn backup_run(void *opaque) 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 @@ void backup_start(const char *job_id, BlockDriverState *bs, 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 @@ void backup_start(const char *job_id, BlockDriverState *bs, 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/commit.c b/block/commit.c index e1eda8908b..c284e8535d 100644 --- a/block/commit.c +++ b/block/commit.c @@ -205,6 +205,7 @@ static const BlockJobDriver commit_job_driver = { .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 @@ void commit_start(const char *job_id, BlockDriverState *bs, 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 e5eaa7ba0a..0404c1b5fa 100644 --- a/block/curl.c +++ b/block/curl.c @@ -68,12 +68,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, #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 @@ typedef struct CURLAIOCB { 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 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, { 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 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) 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 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) 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 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, { 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 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, // 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 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, 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_check_completion(BDRVCURLState *s) static void curl_multi_do(void *arg) { CURLState *s = (CURLState *)arg; + CURLSocket *socket, *next_socket; int running; int r; @@ -359,10 +397,13 @@ static void curl_multi_do(void *arg) 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 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s) #endif } + QLIST_INIT(&state->sockets); state->s = s; return state; @@ -475,6 +517,14 @@ static void curl_clean_state(CURLState *s) { 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 @@ static void curl_readv_bh_cb(void *p) 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 @@ static void curl_readv_bh_cb(void *p) } 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 @@ static void curl_readv_bh_cb(void *p) 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 @@ static BlockDriver bdrv_ftps = { .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 b2c1fb855b..301ba9219a 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -615,6 +615,20 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) 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 @@ static void coroutine_fn mirror_run(void *opaque) 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 @@ static void coroutine_fn mirror_run(void *opaque) 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 @@ static const BlockJobDriver mirror_job_driver = { .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 @@ static const BlockJobDriver commit_active_job_driver = { .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 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, } } - 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/replication.c b/block/replication.c index d5e2b0f497..729dd12499 100644 --- a/block/replication.c +++ b/block/replication.c @@ -421,6 +421,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, 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 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, 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/stream.c b/block/stream.c index b05856bd65..1523ba7dfb 100644 --- a/block/stream.c +++ b/block/stream.c @@ -218,6 +218,7 @@ static const BlockJobDriver stream_job_driver = { .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 @@ void stream_start(const char *job_id, BlockDriverState *bs, 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 882c9034c2..cfc05f2478 100644 --- a/block/trace-events +++ b/block/trace-events @@ -19,14 +19,14 @@ bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t c # 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 102ca9fe01..245e1e1d17 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1811,7 +1811,7 @@ typedef struct DriveBackupState { 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 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) 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 @@ typedef struct BlockdevBackupState { 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 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) 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 @@ static const BlkActionOps actions[] = { [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 @@ out: 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 @@ static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp) bs = qmp_get_root_bs(backup->device, errp); if (!bs) { - return; + return NULL; } aio_context = bdrv_get_aio_context(bs); @@ -3213,10 +3223,10 @@ static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp) } } - 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 @@ static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp) 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 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) 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 @@ void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error **errp) bs = qmp_get_root_bs(backup->device, errp); if (!bs) { - return; + return NULL; } aio_context = bdrv_get_aio_context(bs); @@ -3284,20 +3302,25 @@ void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error **errp) 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 4aa14a4974..513620c199 100644 --- a/blockjob.c +++ b/blockjob.c @@ -174,7 +174,9 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, 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 @@ bool block_job_is_internal(BlockJob *job) 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 @@ static void block_job_completed_single(BlockJob *job) 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 @@ void block_job_complete(BlockJob *job, Error **errp) { /* 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 @@ bool block_job_user_paused(BlockJob *job) 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_enter(BlockJob *job) 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 052cebe851..bd05623a79 100644 --- a/disas/ppc.c +++ b/disas/ppc.c @@ -2286,6 +2286,10 @@ const struct powerpc_opcode powerpc_opcodes[] = { { "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/qmp-commands.txt b/docs/qmp-commands.txt index 6afa87298d..abf210a596 100644 --- a/docs/qmp-commands.txt +++ b/docs/qmp-commands.txt @@ -1803,7 +1803,7 @@ Each json-object contain the following: "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) @@ -318,6 +318,7 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) 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); diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 31822fed9a..06b9b3917f 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 @@ static void vty_receive(void *opaque, const uint8_t *buf, int size) 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/ppc/pnv.c b/hw/ppc/pnv.c index 82276e0857..9df7b25315 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -110,7 +110,7 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) 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 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) _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 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) 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 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) 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 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) 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 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) 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 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) 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 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) 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 2acda9637d..76ce854b0c 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 00dbd8b07b..0e2117f0f5 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 5aaa264bd7..8da271872f 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 @@ static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width) 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 @@ static void xscom_write(void *opaque, hwaddr addr, uint64_t val, 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 7cde30ee09..f9661b7d1a 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1658,19 +1658,25 @@ static int spapr_pci_post_load(void *opaque, int version_id) 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 b02abbd618..83a423c580 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -748,7 +748,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, 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 @@ void mirror_start(const char *job_id, BlockDriverState *bs, * @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 356cacf004..1acb256223 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -189,6 +189,15 @@ void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs); 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 40275e4437..82238229c6 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -47,6 +47,9 @@ struct BlockJobDriver { /** 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 @@ struct BlockJobDriver { 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 02ac1c5f42..df98a72006 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 @@ typedef struct PnvChipClass { 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 c0a2fbb9f6..0faa1847bf 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 @@ typedef struct PnvXScomInterfaceClass { } 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 @@ typedef struct PnvXScomInterfaceClass { * 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 98fb005aba..1881284cb5 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -218,7 +218,7 @@ static inline unsigned long hweight_long(unsigned long w) */ 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 rol8(uint8_t word, unsigned int shift) */ 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 uint8_t ror8(uint8_t word, unsigned int shift) */ 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 rol16(uint16_t word, unsigned int shift) */ 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 uint16_t ror16(uint16_t word, unsigned int shift) */ 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 rol32(uint32_t word, unsigned int shift) */ 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 uint32_t ror32(uint32_t word, unsigned int shift) */ 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 rol64(uint64_t word, unsigned int shift) */ 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 e331f28382..f498ab84f2 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -593,6 +593,7 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) 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 fb9252d722..a1c8089010 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1987,7 +1987,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) 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 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) 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 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) return ret; } - return pages_sent; + return done; } /* Called with iothread lock */ diff --git a/qapi/block-core.json b/qapi/block-core.json index bcd3b9effe..c29bef7ee1 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 4536e18ac0..4a5b29f349 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2606,8 +2606,8 @@ qemu-system-x86_64 --drive file=gluster://192.0.2.1/testvol/a.img 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 @@ Syntax using a single filename: 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 280050a21f..6c18971368 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -66,6 +66,23 @@ void 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-ppc/cpu-models.c b/target-ppc/cpu-models.c index 901cf40033..506dee1ee8 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 7d9e6a2ca0..aafbbd7d5d 100644 --- a/target-ppc/cpu-models.h +++ b/target-ppc/cpu-models.h @@ -549,8 +549,6 @@ enum { 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 808760bf53..93369d4fe5 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -427,6 +427,9 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) 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 3916b2eddc..da00f0ab49 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -223,6 +223,9 @@ DEF_HELPER_3(vsro, void, avr, avr, avr) 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(vmaxfp, void, env, avr, avr, avr) 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(vpermxor, void, avr, avr, avr, avr) 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 dca479838d..9ac204a393 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 @@ void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) } } +/* 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 @@ void helper_vrsqrtefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) } } +#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 @@ void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) #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 void bcd_put_digit(ppc_avr_t *bcd, uint8_t digit, int n) } } +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 @@ uint32_t helper_bcdsub(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) 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 0000000000..1ff4896c45 --- /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 54f35e9904..59e9552d2b 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 @@ EXTRACT_HELPER(DCM, 10, 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 fc612d9f37..7143eb3a39 100644 --- a/target-ppc/translate/vmx-impl.inc.c +++ b/target-ppc/translate/vmx-impl.inc.c @@ -442,6 +442,9 @@ GEN_VXFORM(vmulesw, 4, 14); 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_DUAL(vsubeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ 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(vrfim, 5, 11); 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 @@ static void gen_##op(DisasContext *ctx) \ 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 @@ GEN_VXFORM_DUAL(vsldoi, PPC_ALTIVEC, PPC_NONE, #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 cc7ed7eeed..f02b3bed50 100644 --- a/target-ppc/translate/vmx-ops.inc.c +++ b/target-ppc/translate/vmx-ops.inc.c @@ -107,7 +107,7 @@ GEN_VXFORM(vmulesh, 4, 13), 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_300(vslv, 2, 29), 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_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), 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_207(vsubcuq, 0, 21), 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 de516341fd..e98d3b6bb3 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -270,6 +270,7 @@ gcov-files-ppc64-y = ppc64-softmmu/hw/ppc/spapr_pci.c 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/e1000-test$(EXESUF): tests/e1000-test.o 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/boot-serial-test.c b/tests/boot-serial-test.c index d98c564a35..44c82e5110 100644 --- a/tests/boot-serial-test.c +++ b/tests/boot-serial-test.c @@ -29,6 +29,7 @@ static testdef_t tests[] = { { "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/pnv-xscom-test.c b/tests/pnv-xscom-test.c new file mode 100644 index 0000000000..5951da16cd --- /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 280ed27aa9..927151a285 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -62,6 +62,9 @@ function run_qemu() "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 f06938eeb7..d0d2c2bfb0 100644 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -395,19 +395,7 @@ class TestIncrementalBackup(TestIncrementalBackupBase): 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 @@ class TestIncrementalBackup(TestIncrementalBackupBase): 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 @@ class TestIncrementalBackup(TestIncrementalBackupBase): 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 @@ class TestIncrementalBackup(TestIncrementalBackupBase): 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 @@ class TestIncrementalBackup(TestIncrementalBackupBase): 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 36376bed87..e56cae021b 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/test-blockjob-txn.c b/tests/test-blockjob-txn.c index f9afc3be41..b132e39097 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -24,10 +24,6 @@ typedef struct { 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 @@ static void test_block_job_cb(void *opaque, int ret) 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 @@ static BlockJob *test_block_job_start(unsigned int iterations, 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-vmstate.c b/tests/test-vmstate.c index d8da26f974..d2f529b831 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -83,6 +83,13 @@ static void save_vmstate(const VMStateDescription *desc, void *obj) 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 const VMStateDescription vmstate_versioned = { 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_v1(void) 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 @@ static void test_load_v2(void) 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_save_skip(void) 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 @@ static void test_load_noskip(void) 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_noskip(void) 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 @@ static void test_load_skip(void) 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 @@ static void test_load_skip(void) 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 @@ int main(int argc, char **argv) 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); |