Compare commits
95 Commits
pull-input
...
pull-input
Author | SHA1 | Date | |
---|---|---|---|
|
8977bd111f | ||
|
f85d28316a | ||
|
ee8c0b622c | ||
|
6f5943cf45 | ||
|
8b84286f4c | ||
|
1ff5eedd1d | ||
|
86846bfe64 | ||
|
2386a90730 | ||
|
11c7fa7fa6 | ||
|
f5c0ab1312 | ||
|
178ac111bc | ||
|
6054d883d6 | ||
|
5118dc5975 | ||
|
45e66b7beb | ||
|
65903a8b08 | ||
|
fc13d93726 | ||
|
3478881130 | ||
|
d2e064a73e | ||
|
3894c78764 | ||
|
0aff637e92 | ||
|
5f758366c0 | ||
|
3f9286b721 | ||
|
3b69595068 | ||
|
5a7733b0b7 | ||
|
770a63792b | ||
|
b30f4dfbda | ||
|
8e4e86afa5 | ||
|
76eb98d51c | ||
|
d426d9fba8 | ||
|
7bca3892cb | ||
|
03cf077ac9 | ||
|
7b35d0c44c | ||
|
61c7bbd236 | ||
|
ca8c0fab95 | ||
|
f2564d88fe | ||
|
b1fe60cd35 | ||
|
a2554a334a | ||
|
c5fa6c86d0 | ||
|
5bc8f026dd | ||
|
465bee1da8 | ||
|
6a23082b4e | ||
|
82a402e99f | ||
|
43f35cb5e0 | ||
|
d66e5cee00 | ||
|
46485de0cb | ||
|
42eb58179b | ||
|
7159a45b2b | ||
|
ea54feff58 | ||
|
0a86cb7317 | ||
|
97a3ea5719 | ||
|
e3542c67af | ||
|
9aedd5a5d6 | ||
|
b5e51dd714 | ||
|
d530e34232 | ||
|
4ad303369c | ||
|
4993f7ea7e | ||
|
8a5eb36a1c | ||
|
9c52681277 | ||
|
26e2da7279 | ||
|
6906046169 | ||
|
395071a763 | ||
|
e88ae2264d | ||
|
91e7fcca47 | ||
|
c4ce4c4b1f | ||
|
24fd848950 | ||
|
11b389f21e | ||
|
b162b49adc | ||
|
40d19394b7 | ||
|
13e315dada | ||
|
6297d9a279 | ||
|
29136cd8a4 | ||
|
87a560c455 | ||
|
297a3646c2 | ||
|
cdaec3808e | ||
|
2ddb16a95f | ||
|
f9f3a5ecde | ||
|
be3c771796 | ||
|
192cca60ae | ||
|
4fa953f20d | ||
|
468866b816 | ||
|
e2cd0f4fb4 | ||
|
cbc95538ed | ||
|
f9bee751be | ||
|
6e2bb3ec70 | ||
|
6a86dec619 | ||
|
3d2acaa308 | ||
|
5917af812e | ||
|
b03c38057b | ||
|
dbe5c58f2a | ||
|
d383c625e2 | ||
|
4522b69c6c | ||
|
23335f6273 | ||
|
34bb4d02e0 | ||
|
6ee143a0a4 | ||
|
4bbeb8b173 |
@@ -659,6 +659,12 @@ S: Supported
|
|||||||
F: hw/block/nvme*
|
F: hw/block/nvme*
|
||||||
F: tests/nvme-test.c
|
F: tests/nvme-test.c
|
||||||
|
|
||||||
|
megasas
|
||||||
|
M: Hannes Reinecke <hare@suse.de>
|
||||||
|
S: Supported
|
||||||
|
F: hw/scsi/megasas.c
|
||||||
|
F: hw/scsi/mfi.h
|
||||||
|
|
||||||
Xilinx EDK
|
Xilinx EDK
|
||||||
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||||
|
60
block.c
60
block.c
@@ -1274,6 +1274,33 @@ out:
|
|||||||
g_free(tmp_filename);
|
g_free(tmp_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||||
|
{
|
||||||
|
QObject *options_obj;
|
||||||
|
QDict *options;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = strstart(filename, "json:", &filename);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
options_obj = qobject_from_json(filename);
|
||||||
|
if (!options_obj) {
|
||||||
|
error_setg(errp, "Could not parse the JSON options");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qobject_type(options_obj) != QTYPE_QDICT) {
|
||||||
|
qobject_decref(options_obj);
|
||||||
|
error_setg(errp, "Invalid JSON object given");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
options = qobject_to_qdict(options_obj);
|
||||||
|
qdict_flatten(options);
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Opens a disk image (raw, qcow2, vmdk, ...)
|
* Opens a disk image (raw, qcow2, vmdk, ...)
|
||||||
*
|
*
|
||||||
@@ -1337,6 +1364,20 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
|||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filename && g_str_has_prefix(filename, "json:")) {
|
||||||
|
QDict *json_options = parse_json_filename(filename, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Options given in the filename have lower priority than options
|
||||||
|
* specified directly */
|
||||||
|
qdict_join(options, json_options, false);
|
||||||
|
QDECREF(json_options);
|
||||||
|
filename = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
bs->options = options;
|
bs->options = options;
|
||||||
options = qdict_clone_shallow(options);
|
options = qdict_clone_shallow(options);
|
||||||
|
|
||||||
@@ -3248,6 +3289,15 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
|
|||||||
|
|
||||||
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
|
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
|
||||||
|
|
||||||
|
if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF &&
|
||||||
|
!(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_write_zeroes &&
|
||||||
|
qemu_iovec_is_zero(qiov)) {
|
||||||
|
flags |= BDRV_REQ_ZERO_WRITE;
|
||||||
|
if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) {
|
||||||
|
flags |= BDRV_REQ_MAY_UNMAP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Do nothing, write notifier decided to fail this request */
|
/* Do nothing, write notifier decided to fail this request */
|
||||||
} else if (flags & BDRV_REQ_ZERO_WRITE) {
|
} else if (flags & BDRV_REQ_ZERO_WRITE) {
|
||||||
@@ -3864,7 +3914,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (!bs->drv->bdrv_co_get_block_status) {
|
if (!bs->drv->bdrv_co_get_block_status) {
|
||||||
*pnum = nb_sectors;
|
*pnum = nb_sectors;
|
||||||
ret = BDRV_BLOCK_DATA;
|
ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED;
|
||||||
if (bs->drv->protocol_name) {
|
if (bs->drv->protocol_name) {
|
||||||
ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
|
ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
|
||||||
}
|
}
|
||||||
@@ -3883,6 +3933,10 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
|||||||
*pnum, pnum);
|
*pnum, pnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) {
|
||||||
|
ret |= BDRV_BLOCK_ALLOCATED;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO)) {
|
if (!(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO)) {
|
||||||
if (bdrv_unallocated_blocks_are_zero(bs)) {
|
if (bdrv_unallocated_blocks_are_zero(bs)) {
|
||||||
ret |= BDRV_BLOCK_ZERO;
|
ret |= BDRV_BLOCK_ZERO;
|
||||||
@@ -3959,9 +4013,7 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return
|
return (ret & BDRV_BLOCK_ALLOCATED);
|
||||||
(ret & BDRV_BLOCK_DATA) ||
|
|
||||||
((ret & BDRV_BLOCK_ZERO) && !bdrv_has_zero_init(bs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
79
block/curl.c
79
block/curl.c
@@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "qapi/qmp/qbool.h"
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
// #define DEBUG
|
// #define DEBUG
|
||||||
@@ -37,6 +38,21 @@
|
|||||||
#if LIBCURL_VERSION_NUM >= 0x071000
|
#if LIBCURL_VERSION_NUM >= 0x071000
|
||||||
/* The multi interface timer callback was introduced in 7.16.0 */
|
/* The multi interface timer callback was introduced in 7.16.0 */
|
||||||
#define NEED_CURL_TIMER_CALLBACK
|
#define NEED_CURL_TIMER_CALLBACK
|
||||||
|
#define HAVE_SOCKET_ACTION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_SOCKET_ACTION
|
||||||
|
/* If curl_multi_socket_action isn't available, define it statically here in
|
||||||
|
* terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is
|
||||||
|
* less efficient but still safe. */
|
||||||
|
static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
||||||
|
curl_socket_t sockfd,
|
||||||
|
int ev_bitmask,
|
||||||
|
int *running_handles)
|
||||||
|
{
|
||||||
|
return curl_multi_socket(multi_handle, sockfd, running_handles);
|
||||||
|
}
|
||||||
|
#define curl_multi_socket_action __curl_multi_socket_action
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
|
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
|
||||||
@@ -46,12 +62,16 @@
|
|||||||
#define CURL_NUM_STATES 8
|
#define CURL_NUM_STATES 8
|
||||||
#define CURL_NUM_ACB 8
|
#define CURL_NUM_ACB 8
|
||||||
#define SECTOR_SIZE 512
|
#define SECTOR_SIZE 512
|
||||||
#define READ_AHEAD_SIZE (256 * 1024)
|
#define READ_AHEAD_DEFAULT (256 * 1024)
|
||||||
|
|
||||||
#define FIND_RET_NONE 0
|
#define FIND_RET_NONE 0
|
||||||
#define FIND_RET_OK 1
|
#define FIND_RET_OK 1
|
||||||
#define FIND_RET_WAIT 2
|
#define FIND_RET_WAIT 2
|
||||||
|
|
||||||
|
#define CURL_BLOCK_OPT_URL "url"
|
||||||
|
#define CURL_BLOCK_OPT_READAHEAD "readahead"
|
||||||
|
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
||||||
|
|
||||||
struct BDRVCURLState;
|
struct BDRVCURLState;
|
||||||
|
|
||||||
typedef struct CURLAIOCB {
|
typedef struct CURLAIOCB {
|
||||||
@@ -88,6 +108,7 @@ typedef struct BDRVCURLState {
|
|||||||
CURLState states[CURL_NUM_STATES];
|
CURLState states[CURL_NUM_STATES];
|
||||||
char *url;
|
char *url;
|
||||||
size_t readahead_size;
|
size_t readahead_size;
|
||||||
|
bool sslverify;
|
||||||
bool accept_range;
|
bool accept_range;
|
||||||
} BDRVCURLState;
|
} BDRVCURLState;
|
||||||
|
|
||||||
@@ -354,6 +375,8 @@ static CURLState *curl_init_state(BDRVCURLState *s)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
||||||
|
(long) s->sslverify);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
|
curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
|
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
|
||||||
(void *)curl_read_cb);
|
(void *)curl_read_cb);
|
||||||
@@ -396,43 +419,7 @@ static void curl_clean_state(CURLState *s)
|
|||||||
static void curl_parse_filename(const char *filename, QDict *options,
|
static void curl_parse_filename(const char *filename, QDict *options,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename));
|
||||||
#define RA_OPTSTR ":readahead="
|
|
||||||
char *file;
|
|
||||||
char *ra;
|
|
||||||
const char *ra_val;
|
|
||||||
int parse_state = 0;
|
|
||||||
|
|
||||||
file = g_strdup(filename);
|
|
||||||
|
|
||||||
/* Parse a trailing ":readahead=#:" param, if present. */
|
|
||||||
ra = file + strlen(file) - 1;
|
|
||||||
while (ra >= file) {
|
|
||||||
if (parse_state == 0) {
|
|
||||||
if (*ra == ':') {
|
|
||||||
parse_state++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (parse_state == 1) {
|
|
||||||
if (*ra > '9' || *ra < '0') {
|
|
||||||
char *opt_start = ra - strlen(RA_OPTSTR) + 1;
|
|
||||||
if (opt_start > file &&
|
|
||||||
strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
|
|
||||||
ra_val = ra + 1;
|
|
||||||
ra -= strlen(RA_OPTSTR) - 1;
|
|
||||||
*ra = '\0';
|
|
||||||
qdict_put(options, "readahead", qstring_from_str(ra_val));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ra--;
|
|
||||||
}
|
|
||||||
|
|
||||||
qdict_put(options, "url", qstring_from_str(file));
|
|
||||||
|
|
||||||
g_free(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuOptsList runtime_opts = {
|
static QemuOptsList runtime_opts = {
|
||||||
@@ -440,15 +427,20 @@ static QemuOptsList runtime_opts = {
|
|||||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||||
.desc = {
|
.desc = {
|
||||||
{
|
{
|
||||||
.name = "url",
|
.name = CURL_BLOCK_OPT_URL,
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "URL to open",
|
.help = "URL to open",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "readahead",
|
.name = CURL_BLOCK_OPT_READAHEAD,
|
||||||
.type = QEMU_OPT_SIZE,
|
.type = QEMU_OPT_SIZE,
|
||||||
.help = "Readahead size",
|
.help = "Readahead size",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_SSLVERIFY,
|
||||||
|
.type = QEMU_OPT_BOOL,
|
||||||
|
.help = "Verify SSL certificate"
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -477,14 +469,17 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
|
s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
|
||||||
|
READ_AHEAD_DEFAULT);
|
||||||
if ((s->readahead_size & 0x1ff) != 0) {
|
if ((s->readahead_size & 0x1ff) != 0) {
|
||||||
error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
|
error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
|
||||||
s->readahead_size);
|
s->readahead_size);
|
||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
}
|
}
|
||||||
|
|
||||||
file = qemu_opt_get(opts, "url");
|
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
|
||||||
|
|
||||||
|
file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
error_setg(errp, "curl block driver requires an 'url' option");
|
error_setg(errp, "curl block driver requires an 'url' option");
|
||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
|
317
block/iscsi.c
317
block/iscsi.c
@@ -30,6 +30,8 @@
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/bitops.h"
|
||||||
|
#include "qemu/bitmap.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "block/scsi.h"
|
#include "block/scsi.h"
|
||||||
@@ -59,6 +61,8 @@ typedef struct IscsiLun {
|
|||||||
struct scsi_inquiry_logical_block_provisioning lbp;
|
struct scsi_inquiry_logical_block_provisioning lbp;
|
||||||
struct scsi_inquiry_block_limits bl;
|
struct scsi_inquiry_block_limits bl;
|
||||||
unsigned char *zeroblock;
|
unsigned char *zeroblock;
|
||||||
|
unsigned long *allocationmap;
|
||||||
|
int cluster_sectors;
|
||||||
} IscsiLun;
|
} IscsiLun;
|
||||||
|
|
||||||
typedef struct IscsiTask {
|
typedef struct IscsiTask {
|
||||||
@@ -92,6 +96,15 @@ typedef struct IscsiAIOCB {
|
|||||||
#define MAX_NOP_FAILURES 3
|
#define MAX_NOP_FAILURES 3
|
||||||
#define ISCSI_CMD_RETRIES 5
|
#define ISCSI_CMD_RETRIES 5
|
||||||
|
|
||||||
|
/* this threshhold is a trade-off knob to choose between
|
||||||
|
* the potential additional overhead of an extra GET_LBA_STATUS request
|
||||||
|
* vs. unnecessarily reading a lot of zero sectors over the wire.
|
||||||
|
* If a read request is greater or equal than ISCSI_CHECKALLOC_THRES
|
||||||
|
* sectors we check the allocation status of the area covered by the
|
||||||
|
* request first if the allocationmap indicates that the area might be
|
||||||
|
* unallocated. */
|
||||||
|
#define ISCSI_CHECKALLOC_THRES 64
|
||||||
|
|
||||||
static void
|
static void
|
||||||
iscsi_bh_cb(void *p)
|
iscsi_bh_cb(void *p)
|
||||||
{
|
{
|
||||||
@@ -273,6 +286,32 @@ static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void iscsi_allocationmap_set(IscsiLun *iscsilun, int64_t sector_num,
|
||||||
|
int nb_sectors)
|
||||||
|
{
|
||||||
|
if (iscsilun->allocationmap == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bitmap_set(iscsilun->allocationmap,
|
||||||
|
sector_num / iscsilun->cluster_sectors,
|
||||||
|
DIV_ROUND_UP(nb_sectors, iscsilun->cluster_sectors));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iscsi_allocationmap_clear(IscsiLun *iscsilun, int64_t sector_num,
|
||||||
|
int nb_sectors)
|
||||||
|
{
|
||||||
|
int64_t cluster_num, nb_clusters;
|
||||||
|
if (iscsilun->allocationmap == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cluster_num = DIV_ROUND_UP(sector_num, iscsilun->cluster_sectors);
|
||||||
|
nb_clusters = (sector_num + nb_sectors) / iscsilun->cluster_sectors
|
||||||
|
- cluster_num;
|
||||||
|
if (nb_clusters > 0) {
|
||||||
|
bitmap_clear(iscsilun->allocationmap, cluster_num, nb_clusters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int coroutine_fn iscsi_co_writev(BlockDriverState *bs,
|
static int coroutine_fn iscsi_co_writev(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors,
|
int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *iov)
|
QEMUIOVector *iov)
|
||||||
@@ -336,9 +375,125 @@ retry:
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||||
|
static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun,
|
||||||
|
int64_t sector_num, int nb_sectors)
|
||||||
|
{
|
||||||
|
unsigned long size;
|
||||||
|
if (iscsilun->allocationmap == NULL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors);
|
||||||
|
return !(find_next_bit(iscsilun->allocationmap, size,
|
||||||
|
sector_num / iscsilun->cluster_sectors) == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
||||||
|
int64_t sector_num,
|
||||||
|
int nb_sectors, int *pnum)
|
||||||
|
{
|
||||||
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
|
struct scsi_get_lba_status *lbas = NULL;
|
||||||
|
struct scsi_lba_status_descriptor *lbasd = NULL;
|
||||||
|
struct IscsiTask iTask;
|
||||||
|
int64_t ret;
|
||||||
|
|
||||||
|
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||||
|
|
||||||
|
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* default to all sectors allocated */
|
||||||
|
ret = BDRV_BLOCK_DATA;
|
||||||
|
ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID;
|
||||||
|
*pnum = nb_sectors;
|
||||||
|
|
||||||
|
/* LUN does not support logical block provisioning */
|
||||||
|
if (iscsilun->lbpme == 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
retry:
|
||||||
|
if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
|
||||||
|
sector_qemu2lun(sector_num, iscsilun),
|
||||||
|
8 + 16, iscsi_co_generic_cb,
|
||||||
|
&iTask) == NULL) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!iTask.complete) {
|
||||||
|
iscsi_set_events(iscsilun);
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iTask.do_retry) {
|
||||||
|
if (iTask.task != NULL) {
|
||||||
|
scsi_free_scsi_task(iTask.task);
|
||||||
|
iTask.task = NULL;
|
||||||
|
}
|
||||||
|
iTask.complete = 0;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||||
|
/* in case the get_lba_status_callout fails (i.e.
|
||||||
|
* because the device is busy or the cmd is not
|
||||||
|
* supported) we pretend all blocks are allocated
|
||||||
|
* for backwards compatibility */
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbas = scsi_datain_unmarshall(iTask.task);
|
||||||
|
if (lbas == NULL) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbasd = &lbas->descriptors[0];
|
||||||
|
|
||||||
|
if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun);
|
||||||
|
|
||||||
|
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
|
||||||
|
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
||||||
|
ret &= ~BDRV_BLOCK_DATA;
|
||||||
|
if (iscsilun->lbprz) {
|
||||||
|
ret |= BDRV_BLOCK_ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret & BDRV_BLOCK_ZERO) {
|
||||||
|
iscsi_allocationmap_clear(iscsilun, sector_num, *pnum);
|
||||||
|
} else {
|
||||||
|
iscsi_allocationmap_set(iscsilun, sector_num, *pnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pnum > nb_sectors) {
|
||||||
|
*pnum = nb_sectors;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
if (iTask.task != NULL) {
|
||||||
|
scsi_free_scsi_task(iTask.task);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LIBISCSI_FEATURE_IOVECTOR */
|
||||||
|
|
||||||
|
|
||||||
static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors,
|
int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *iov)
|
QEMUIOVector *iov)
|
||||||
@@ -355,6 +510,22 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||||
|
if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES &&
|
||||||
|
!iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
|
||||||
|
int64_t ret;
|
||||||
|
int pnum;
|
||||||
|
ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (ret & BDRV_BLOCK_ZERO && pnum >= nb_sectors) {
|
||||||
|
qemu_iovec_memset(iov, 0, 0x00, iov->size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||||
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
|
num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
|
||||||
|
|
||||||
@@ -643,101 +814,6 @@ iscsi_getlength(BlockDriverState *bs)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
|
||||||
|
|
||||||
static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
|
||||||
int64_t sector_num,
|
|
||||||
int nb_sectors, int *pnum)
|
|
||||||
{
|
|
||||||
IscsiLun *iscsilun = bs->opaque;
|
|
||||||
struct scsi_get_lba_status *lbas = NULL;
|
|
||||||
struct scsi_lba_status_descriptor *lbasd = NULL;
|
|
||||||
struct IscsiTask iTask;
|
|
||||||
int64_t ret;
|
|
||||||
|
|
||||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
|
||||||
|
|
||||||
if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* default to all sectors allocated */
|
|
||||||
ret = BDRV_BLOCK_DATA;
|
|
||||||
ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID;
|
|
||||||
*pnum = nb_sectors;
|
|
||||||
|
|
||||||
/* LUN does not support logical block provisioning */
|
|
||||||
if (iscsilun->lbpme == 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
retry:
|
|
||||||
if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
|
|
||||||
sector_qemu2lun(sector_num, iscsilun),
|
|
||||||
8 + 16, iscsi_co_generic_cb,
|
|
||||||
&iTask) == NULL) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!iTask.complete) {
|
|
||||||
iscsi_set_events(iscsilun);
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iTask.do_retry) {
|
|
||||||
if (iTask.task != NULL) {
|
|
||||||
scsi_free_scsi_task(iTask.task);
|
|
||||||
iTask.task = NULL;
|
|
||||||
}
|
|
||||||
iTask.complete = 0;
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
|
||||||
/* in case the get_lba_status_callout fails (i.e.
|
|
||||||
* because the device is busy or the cmd is not
|
|
||||||
* supported) we pretend all blocks are allocated
|
|
||||||
* for backwards compatibility */
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
lbas = scsi_datain_unmarshall(iTask.task);
|
|
||||||
if (lbas == NULL) {
|
|
||||||
ret = -EIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
lbasd = &lbas->descriptors[0];
|
|
||||||
|
|
||||||
if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) {
|
|
||||||
ret = -EIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
*pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun);
|
|
||||||
if (*pnum > nb_sectors) {
|
|
||||||
*pnum = nb_sectors;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
|
|
||||||
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
|
||||||
ret &= ~BDRV_BLOCK_DATA;
|
|
||||||
if (iscsilun->lbprz) {
|
|
||||||
ret |= BDRV_BLOCK_ZERO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (iTask.task != NULL) {
|
|
||||||
scsi_free_scsi_task(iTask.task);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* LIBISCSI_FEATURE_IOVECTOR */
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
|
coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||||
int nb_sectors)
|
int nb_sectors)
|
||||||
@@ -791,6 +867,8 @@ retry:
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,13 +887,14 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
|
if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) {
|
||||||
/* WRITE SAME without UNMAP is not supported by the target */
|
/* WRITE SAME with UNMAP is not supported by the target,
|
||||||
return -ENOTSUP;
|
* fall back and try WRITE SAME without UNMAP */
|
||||||
|
flags &= ~BDRV_REQ_MAY_UNMAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) {
|
if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
|
||||||
/* WRITE SAME with UNMAP is not supported by the target */
|
/* WRITE SAME without UNMAP is not supported by the target */
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,6 +943,12 @@ retry:
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags & BDRV_REQ_MAY_UNMAP) {
|
||||||
|
iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors);
|
||||||
|
} else {
|
||||||
|
iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1295,6 +1380,22 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
|
timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Guess the internal cluster (page) size of the iscsi target by the means
|
||||||
|
* of opt_unmap_gran. Transfer the unmap granularity only if it has a
|
||||||
|
* reasonable size */
|
||||||
|
if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 4 * 1024 &&
|
||||||
|
iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
|
||||||
|
iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran *
|
||||||
|
iscsilun->block_size) >> BDRV_SECTOR_BITS;
|
||||||
|
#if defined(LIBISCSI_FEATURE_IOVECTOR)
|
||||||
|
if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) {
|
||||||
|
iscsilun->allocationmap =
|
||||||
|
bitmap_new(DIV_ROUND_UP(bs->total_sectors,
|
||||||
|
iscsilun->cluster_sectors));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
if (initiator_name != NULL) {
|
if (initiator_name != NULL) {
|
||||||
@@ -1328,6 +1429,7 @@ static void iscsi_close(BlockDriverState *bs)
|
|||||||
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL);
|
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL);
|
||||||
iscsi_destroy_context(iscsi);
|
iscsi_destroy_context(iscsi);
|
||||||
g_free(iscsilun->zeroblock);
|
g_free(iscsilun->zeroblock);
|
||||||
|
g_free(iscsilun->allocationmap);
|
||||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1388,6 +1490,13 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iscsilun->allocationmap != NULL) {
|
||||||
|
g_free(iscsilun->allocationmap);
|
||||||
|
iscsilun->allocationmap =
|
||||||
|
bitmap_new(DIV_ROUND_UP(bs->total_sectors,
|
||||||
|
iscsilun->cluster_sectors));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1450,13 +1559,7 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
|||||||
IscsiLun *iscsilun = bs->opaque;
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz;
|
bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz;
|
||||||
bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws;
|
bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws;
|
||||||
/* Guess the internal cluster (page) size of the iscsi target by the means
|
bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE;
|
||||||
* of opt_unmap_gran. Transfer the unmap granularity only if it has a
|
|
||||||
* reasonable size for bdi->cluster_size */
|
|
||||||
if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 64 * 1024 &&
|
|
||||||
iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
|
|
||||||
bdi->cluster_size = iscsilun->bl.opt_unmap_gran * iscsilun->block_size;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -50,6 +50,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
info->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||||
|
info->detect_zeroes = bs->detect_zeroes;
|
||||||
|
|
||||||
if (bs->io_limits_enabled) {
|
if (bs->io_limits_enabled) {
|
||||||
ThrottleConfig cfg;
|
ThrottleConfig cfg;
|
||||||
|
44
block/qcow.c
44
block/qcow.c
@@ -48,9 +48,10 @@ typedef struct QCowHeader {
|
|||||||
uint64_t size; /* in bytes */
|
uint64_t size; /* in bytes */
|
||||||
uint8_t cluster_bits;
|
uint8_t cluster_bits;
|
||||||
uint8_t l2_bits;
|
uint8_t l2_bits;
|
||||||
|
uint16_t padding;
|
||||||
uint32_t crypt_method;
|
uint32_t crypt_method;
|
||||||
uint64_t l1_table_offset;
|
uint64_t l1_table_offset;
|
||||||
} QCowHeader;
|
} QEMU_PACKED QCowHeader;
|
||||||
|
|
||||||
#define L2_CACHE_SIZE 16
|
#define L2_CACHE_SIZE 16
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ typedef struct BDRVQcowState {
|
|||||||
int cluster_sectors;
|
int cluster_sectors;
|
||||||
int l2_bits;
|
int l2_bits;
|
||||||
int l2_size;
|
int l2_size;
|
||||||
int l1_size;
|
unsigned int l1_size;
|
||||||
uint64_t cluster_offset_mask;
|
uint64_t cluster_offset_mask;
|
||||||
uint64_t l1_table_offset;
|
uint64_t l1_table_offset;
|
||||||
uint64_t *l1_table;
|
uint64_t *l1_table;
|
||||||
@@ -96,7 +97,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
int len, i, shift, ret;
|
unsigned int len, i, shift;
|
||||||
|
int ret;
|
||||||
QCowHeader header;
|
QCowHeader header;
|
||||||
|
|
||||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||||
@@ -127,11 +129,25 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.size <= 1 || header.cluster_bits < 9) {
|
if (header.size <= 1) {
|
||||||
error_setg(errp, "invalid value in qcow header");
|
error_setg(errp, "Image size is too small (must be at least 2 bytes)");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
if (header.cluster_bits < 9 || header.cluster_bits > 16) {
|
||||||
|
error_setg(errp, "Cluster size must be between 512 and 64k");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* l2_bits specifies number of entries; storing a uint64_t in each entry,
|
||||||
|
* so bytes = num_entries << 3. */
|
||||||
|
if (header.l2_bits < 9 - 3 || header.l2_bits > 16 - 3) {
|
||||||
|
error_setg(errp, "L2 table size must be between 512 and 64k");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (header.crypt_method > QCOW_CRYPT_AES) {
|
if (header.crypt_method > QCOW_CRYPT_AES) {
|
||||||
error_setg(errp, "invalid encryption method in qcow header");
|
error_setg(errp, "invalid encryption method in qcow header");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
@@ -151,7 +167,19 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
/* read the level 1 table */
|
/* read the level 1 table */
|
||||||
shift = s->cluster_bits + s->l2_bits;
|
shift = s->cluster_bits + s->l2_bits;
|
||||||
s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
|
if (header.size > UINT64_MAX - (1LL << shift)) {
|
||||||
|
error_setg(errp, "Image too large");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
} else {
|
||||||
|
uint64_t l1_size = (header.size + (1LL << shift) - 1) >> shift;
|
||||||
|
if (l1_size > INT_MAX / sizeof(uint64_t)) {
|
||||||
|
error_setg(errp, "Image too large");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
s->l1_size = l1_size;
|
||||||
|
}
|
||||||
|
|
||||||
s->l1_table_offset = header.l1_table_offset;
|
s->l1_table_offset = header.l1_table_offset;
|
||||||
s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
|
s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
|
||||||
@@ -175,7 +203,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
if (header.backing_file_offset != 0) {
|
if (header.backing_file_offset != 0) {
|
||||||
len = header.backing_file_size;
|
len = header.backing_file_size;
|
||||||
if (len > 1023) {
|
if (len > 1023) {
|
||||||
len = 1023;
|
error_setg(errp, "Backing file name too long");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
ret = bdrv_pread(bs->file, header.backing_file_offset,
|
ret = bdrv_pread(bs->file, header.backing_file_offset,
|
||||||
bs->backing_file, len);
|
bs->backing_file, len);
|
||||||
|
@@ -472,10 +472,17 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
|||||||
s->curr_header = 0;
|
s->curr_header = 0;
|
||||||
} else if (h2_seq > h1_seq) {
|
} else if (h2_seq > h1_seq) {
|
||||||
s->curr_header = 1;
|
s->curr_header = 1;
|
||||||
|
} else {
|
||||||
|
/* The Microsoft Disk2VHD tool will create 2 identical
|
||||||
|
* headers, with identical sequence numbers. If the headers are
|
||||||
|
* identical, don't consider the file corrupt */
|
||||||
|
if (!memcmp(header1, header2, sizeof(VHDXHeader))) {
|
||||||
|
s->curr_header = 0;
|
||||||
} else {
|
} else {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
|
vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
|
||||||
s->headers[s->curr_header]->log_length);
|
s->headers[s->curr_header]->log_length);
|
||||||
|
43
blockdev.c
43
blockdev.c
@@ -288,6 +288,25 @@ static int parse_block_error_action(const char *buf, bool is_read, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int parse_enum_option(const char *lookup[], const char *buf,
|
||||||
|
int max, int def, Error **errp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < max; i++) {
|
||||||
|
if (!strcmp(buf, lookup[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error_setg(errp, "invalid parameter value: %s", buf);
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
|
static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
|
||||||
{
|
{
|
||||||
if (throttle_conflicting(cfg)) {
|
if (throttle_conflicting(cfg)) {
|
||||||
@@ -324,6 +343,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
const char *id;
|
const char *id;
|
||||||
bool has_driver_specific_opts;
|
bool has_driver_specific_opts;
|
||||||
|
BlockdevDetectZeroesOptions detect_zeroes;
|
||||||
BlockDriver *drv = NULL;
|
BlockDriver *drv = NULL;
|
||||||
|
|
||||||
/* Check common options by copying from bs_opts to opts, all other options
|
/* Check common options by copying from bs_opts to opts, all other options
|
||||||
@@ -452,6 +472,24 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detect_zeroes =
|
||||||
|
parse_enum_option(BlockdevDetectZeroesOptions_lookup,
|
||||||
|
qemu_opt_get(opts, "detect-zeroes"),
|
||||||
|
BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
|
||||||
|
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
|
||||||
|
&error);
|
||||||
|
if (error) {
|
||||||
|
error_propagate(errp, error);
|
||||||
|
goto early_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
|
||||||
|
!(bdrv_flags & BDRV_O_UNMAP)) {
|
||||||
|
error_setg(errp, "setting detect-zeroes to unmap is not allowed "
|
||||||
|
"without setting discard operation to unmap");
|
||||||
|
goto early_err;
|
||||||
|
}
|
||||||
|
|
||||||
/* init */
|
/* init */
|
||||||
dinfo = g_malloc0(sizeof(*dinfo));
|
dinfo = g_malloc0(sizeof(*dinfo));
|
||||||
dinfo->id = g_strdup(qemu_opts_id(opts));
|
dinfo->id = g_strdup(qemu_opts_id(opts));
|
||||||
@@ -462,6 +500,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
}
|
}
|
||||||
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
||||||
dinfo->bdrv->read_only = ro;
|
dinfo->bdrv->read_only = ro;
|
||||||
|
dinfo->bdrv->detect_zeroes = detect_zeroes;
|
||||||
dinfo->refcount = 1;
|
dinfo->refcount = 1;
|
||||||
if (serial != NULL) {
|
if (serial != NULL) {
|
||||||
dinfo->serial = g_strdup(serial);
|
dinfo->serial = g_strdup(serial);
|
||||||
@@ -2455,6 +2494,10 @@ QemuOptsList qemu_common_drive_opts = {
|
|||||||
.name = "copy-on-read",
|
.name = "copy-on-read",
|
||||||
.type = QEMU_OPT_BOOL,
|
.type = QEMU_OPT_BOOL,
|
||||||
.help = "copy read data from backing file into image file",
|
.help = "copy read data from backing file into image file",
|
||||||
|
},{
|
||||||
|
.name = "detect-zeroes",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "try to optimize zero writes (off, on, unmap)",
|
||||||
},
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
CONFIG_VIRTIO=y
|
CONFIG_VIRTIO=y
|
||||||
CONFIG_SCLPCONSOLE=y
|
CONFIG_SCLPCONSOLE=y
|
||||||
CONFIG_S390_FLIC=$(CONFIG_KVM)
|
CONFIG_S390_FLIC=y
|
||||||
|
CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
|
||||||
|
76
docs/multiseat.txt
Normal file
76
docs/multiseat.txt
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
multiseat howto (with some multihead coverage)
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
host side
|
||||||
|
---------
|
||||||
|
|
||||||
|
First you must compile qemu with a user interface supporting
|
||||||
|
multihead/multiseat and input event routing. Right now this list is
|
||||||
|
pretty short: sdl2.
|
||||||
|
|
||||||
|
./configure --enable-sdl --with-sdlabi=2.0
|
||||||
|
|
||||||
|
|
||||||
|
Next put together the qemu command line:
|
||||||
|
|
||||||
|
qemu -enable-kvm -usb $memory $disk $whatever \
|
||||||
|
-display sdl \
|
||||||
|
-vga std \
|
||||||
|
-device usb-tablet
|
||||||
|
|
||||||
|
That is it for the first head, which will use the standard vga, the
|
||||||
|
standard ps/2 keyboard (implicitly there) and the usb-tablet. Now the
|
||||||
|
additional switches for the second head:
|
||||||
|
|
||||||
|
-device pci-bridge,addr=12.0,chassis_nr=2,id=head.2 \
|
||||||
|
-device secondary-vga,bus=head.2,addr=02.0,id=video.2 \
|
||||||
|
-device nec-usb-xhci,bus=head.2,addr=0f.0,id=usb.2 \
|
||||||
|
-device usb-kbd,bus=usb.2.0,port=1,display=video.2 \
|
||||||
|
-device usb-tablet,bus=usb.2.0,port=2,display=video.2
|
||||||
|
|
||||||
|
This places a pci bridge in slot 12, connects a display adapter and
|
||||||
|
xhci (usb) controller to the bridge. Then it adds a usb keyboard and
|
||||||
|
usb mouse, both connected to the xhci and linked to the display.
|
||||||
|
|
||||||
|
The "display=video2" sets up the input routing. Any input coming from
|
||||||
|
the window which belongs to the video.2 display adapter will be routed
|
||||||
|
to these input devices.
|
||||||
|
|
||||||
|
|
||||||
|
guest side
|
||||||
|
----------
|
||||||
|
|
||||||
|
You need a pretty recent linux guest. systemd with loginctl. kernel
|
||||||
|
3.14+ with CONFIG_DRM_BOCHS enabled. Fedora 20 will do. Must be
|
||||||
|
fully updated for the new kernel though, i.e. the live iso doesn't cut
|
||||||
|
it.
|
||||||
|
|
||||||
|
Now we'll have to configure the guest. Boot and login. By default
|
||||||
|
all devices belong to seat0. You can use "loginctl seat-status seat0"
|
||||||
|
to list them all (and to get the sysfs paths for cut+paste). Now
|
||||||
|
we'll go assign all pci devices connected the pci bridge in slot 12 to
|
||||||
|
a new head:
|
||||||
|
|
||||||
|
loginctl attach seat-qemu \
|
||||||
|
/sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/drm/card1
|
||||||
|
loginctl attach seat-qemu \
|
||||||
|
/sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/graphics/fb1
|
||||||
|
loginctl attach seat-qemu \
|
||||||
|
/sys/devices/pci0000:00/0000:00:12.0/0000:01:0f.0/usb2
|
||||||
|
|
||||||
|
Use "loginctl seat-status seat-qemu" to check the result. It isn't
|
||||||
|
needed to assign the usb devices to the head individually, assigning a
|
||||||
|
usb (root) hub will automatically assign all usb devices connected to
|
||||||
|
it too.
|
||||||
|
|
||||||
|
BTW: loginctl writes udev rules to /etc/udev/rules.d to make these
|
||||||
|
device assignments permanent, so you need to do this only once.
|
||||||
|
|
||||||
|
Now simply restart gdm (rebooting will do too), and a login screen
|
||||||
|
should show up on the second head.
|
||||||
|
|
||||||
|
Enjoy!
|
||||||
|
|
||||||
|
--
|
||||||
|
Gerd Hoffmann <kraxel@redhat.com>
|
@@ -48,7 +48,7 @@ The QAPI schema definitions can be modularized using the 'include' directive:
|
|||||||
{ 'include': 'path/to/file.json'}
|
{ 'include': 'path/to/file.json'}
|
||||||
|
|
||||||
The directive is evaluated recursively, and include paths are relative to the
|
The directive is evaluated recursively, and include paths are relative to the
|
||||||
file using the directive.
|
file using the directive. Multiple includes of the same file are safe.
|
||||||
|
|
||||||
|
|
||||||
=== Complex types ===
|
=== Complex types ===
|
||||||
@@ -230,14 +230,13 @@ node structure that can be used to chain together a list of such types in
|
|||||||
case we want to accept/return a list of this type with a command), and a
|
case we want to accept/return a list of this type with a command), and a
|
||||||
command which takes that type as a parameter and returns the same type:
|
command which takes that type as a parameter and returns the same type:
|
||||||
|
|
||||||
mdroth@illuin:~/w/qemu2.git$ cat example-schema.json
|
$ cat example-schema.json
|
||||||
{ 'type': 'UserDefOne',
|
{ 'type': 'UserDefOne',
|
||||||
'data': { 'integer': 'int', 'string': 'str' } }
|
'data': { 'integer': 'int', 'string': 'str' } }
|
||||||
|
|
||||||
{ 'command': 'my-command',
|
{ 'command': 'my-command',
|
||||||
'data': {'arg1': 'UserDefOne'},
|
'data': {'arg1': 'UserDefOne'},
|
||||||
'returns': 'UserDefOne' }
|
'returns': 'UserDefOne' }
|
||||||
mdroth@illuin:~/w/qemu2.git$
|
|
||||||
|
|
||||||
=== scripts/qapi-types.py ===
|
=== scripts/qapi-types.py ===
|
||||||
|
|
||||||
@@ -255,14 +254,25 @@ created code.
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \
|
$ python scripts/qapi-types.py --output-dir="qapi-generated" \
|
||||||
--output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json
|
--prefix="example-" --input-file=example-schema.json
|
||||||
mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c
|
$ cat qapi-generated/example-qapi-types.c
|
||||||
/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
#include "qapi/qapi-dealloc-visitor.h"
|
void qapi_free_UserDefOneList(UserDefOneList * obj)
|
||||||
#include "example-qapi-types.h"
|
{
|
||||||
#include "example-qapi-visit.h"
|
QapiDeallocVisitor *md;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
md = qapi_dealloc_visitor_new();
|
||||||
|
v = qapi_dealloc_get_visitor(md);
|
||||||
|
visit_type_UserDefOneList(v, &obj, NULL, NULL);
|
||||||
|
qapi_dealloc_visitor_cleanup(md);
|
||||||
|
}
|
||||||
|
|
||||||
void qapi_free_UserDefOne(UserDefOne * obj)
|
void qapi_free_UserDefOne(UserDefOne * obj)
|
||||||
{
|
{
|
||||||
@@ -279,32 +289,38 @@ Example:
|
|||||||
qapi_dealloc_visitor_cleanup(md);
|
qapi_dealloc_visitor_cleanup(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h
|
$ cat qapi-generated/example-qapi-types.h
|
||||||
/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
[Uninteresting stuff omitted...]
|
||||||
#ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES
|
|
||||||
#define QAPI_GENERATED_EXAMPLE_QAPI_TYPES
|
|
||||||
|
|
||||||
#include "qapi/qapi-types-core.h"
|
#ifndef EXAMPLE_QAPI_TYPES_H
|
||||||
|
#define EXAMPLE_QAPI_TYPES_H
|
||||||
|
|
||||||
|
[Builtin types omitted...]
|
||||||
|
|
||||||
typedef struct UserDefOne UserDefOne;
|
typedef struct UserDefOne UserDefOne;
|
||||||
|
|
||||||
typedef struct UserDefOneList
|
typedef struct UserDefOneList
|
||||||
{
|
{
|
||||||
|
union {
|
||||||
UserDefOne *value;
|
UserDefOne *value;
|
||||||
|
uint64_t padding;
|
||||||
|
};
|
||||||
struct UserDefOneList *next;
|
struct UserDefOneList *next;
|
||||||
} UserDefOneList;
|
} UserDefOneList;
|
||||||
|
|
||||||
|
[Functions on builtin types omitted...]
|
||||||
|
|
||||||
struct UserDefOne
|
struct UserDefOne
|
||||||
{
|
{
|
||||||
int64_t integer;
|
int64_t integer;
|
||||||
char * string;
|
char * string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void qapi_free_UserDefOneList(UserDefOneList * obj);
|
||||||
void qapi_free_UserDefOne(UserDefOne * obj);
|
void qapi_free_UserDefOne(UserDefOne * obj);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
=== scripts/qapi-visit.py ===
|
=== scripts/qapi-visit.py ===
|
||||||
|
|
||||||
Used to generate the visitor functions used to walk through and convert
|
Used to generate the visitor functions used to walk through and convert
|
||||||
@@ -325,51 +341,78 @@ $(prefix)qapi-visit.h: declarations for previously mentioned visitor
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \
|
$ python scripts/qapi-visit.py --output-dir="qapi-generated"
|
||||||
--output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json
|
--prefix="example-" --input-file=example-schema.json
|
||||||
mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c
|
$ cat qapi-generated/example-qapi-visit.c
|
||||||
/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
#include "example-qapi-visit.h"
|
static void visit_type_UserDefOne_fields(Visitor *m, UserDefOne ** obj, Error **errp)
|
||||||
|
{
|
||||||
|
Error *err = NULL;
|
||||||
|
visit_type_int(m, &(*obj)->integer, "integer", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
visit_type_str(m, &(*obj)->string, "string", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
error_propagate(errp, err);
|
||||||
|
}
|
||||||
|
|
||||||
void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp)
|
void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp);
|
Error *err = NULL;
|
||||||
visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp);
|
|
||||||
visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp);
|
visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err);
|
||||||
visit_end_struct(m, errp);
|
if (!err) {
|
||||||
|
if (*obj) {
|
||||||
|
visit_type_UserDefOne_fields(m, obj, errp);
|
||||||
|
}
|
||||||
|
visit_end_struct(m, &err);
|
||||||
|
}
|
||||||
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp)
|
void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
GenericList *i, **prev = (GenericList **)obj;
|
Error *err = NULL;
|
||||||
|
GenericList *i, **prev;
|
||||||
|
|
||||||
visit_start_list(m, name, errp);
|
visit_start_list(m, name, &err);
|
||||||
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) {
|
for (prev = (GenericList **)obj;
|
||||||
|
!err && (i = visit_next_list(m, prev, &err)) != NULL;
|
||||||
|
prev = &i) {
|
||||||
UserDefOneList *native_i = (UserDefOneList *)i;
|
UserDefOneList *native_i = (UserDefOneList *)i;
|
||||||
visit_type_UserDefOne(m, &native_i->value, NULL, errp);
|
visit_type_UserDefOne(m, &native_i->value, NULL, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_end_list(m, errp);
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
|
visit_end_list(m, &err);
|
||||||
|
out:
|
||||||
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h
|
$ python scripts/qapi-commands.py --output-dir="qapi-generated" \
|
||||||
/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
--prefix="example-" --input-file=example-schema.json
|
||||||
|
$ cat qapi-generated/example-qapi-visit.h
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
#ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT
|
#ifndef EXAMPLE_QAPI_VISIT_H
|
||||||
#define QAPI_GENERATED_EXAMPLE_QAPI_VISIT
|
#define EXAMPLE_QAPI_VISIT_H
|
||||||
|
|
||||||
#include "qapi/qapi-visit-core.h"
|
[Visitors for builtin types omitted...]
|
||||||
#include "example-qapi-types.h"
|
|
||||||
|
|
||||||
void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp);
|
void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp);
|
||||||
void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp);
|
void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
mdroth@illuin:~/w/qemu2.git$
|
|
||||||
|
|
||||||
(The actual structure of the visit_type_* functions is a bit more complex
|
|
||||||
in order to propagate errors correctly and avoid leaking memory).
|
|
||||||
|
|
||||||
=== scripts/qapi-commands.py ===
|
=== scripts/qapi-commands.py ===
|
||||||
|
|
||||||
@@ -390,77 +433,80 @@ $(prefix)qmp-commands.h: Function prototypes for the QMP commands
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c
|
$ cat qapi-generated/example-qmp-marshal.c
|
||||||
/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
#include "qemu-objects.h"
|
|
||||||
#include "qapi/qmp-core.h"
|
|
||||||
#include "qapi/qapi-visit-core.h"
|
|
||||||
#include "qapi/qmp-output-visitor.h"
|
|
||||||
#include "qapi/qmp-input-visitor.h"
|
|
||||||
#include "qapi/qapi-dealloc-visitor.h"
|
|
||||||
#include "example-qapi-types.h"
|
|
||||||
#include "example-qapi-visit.h"
|
|
||||||
|
|
||||||
#include "example-qmp-commands.h"
|
|
||||||
static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp)
|
static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp)
|
||||||
{
|
{
|
||||||
QapiDeallocVisitor *md = qapi_dealloc_visitor_new();
|
Error *local_err = NULL;
|
||||||
QmpOutputVisitor *mo = qmp_output_visitor_new();
|
QmpOutputVisitor *mo = qmp_output_visitor_new();
|
||||||
|
QapiDeallocVisitor *md;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
|
|
||||||
v = qmp_output_get_visitor(mo);
|
v = qmp_output_get_visitor(mo);
|
||||||
visit_type_UserDefOne(v, &ret_in, "unused", errp);
|
visit_type_UserDefOne(v, &ret_in, "unused", &local_err);
|
||||||
v = qapi_dealloc_get_visitor(md);
|
if (local_err) {
|
||||||
visit_type_UserDefOne(v, &ret_in, "unused", errp);
|
goto out;
|
||||||
qapi_dealloc_visitor_cleanup(md);
|
}
|
||||||
|
|
||||||
|
|
||||||
*ret_out = qmp_output_get_qobject(mo);
|
*ret_out = qmp_output_get_qobject(mo);
|
||||||
|
|
||||||
|
out:
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
qmp_output_visitor_cleanup(mo);
|
||||||
|
md = qapi_dealloc_visitor_new();
|
||||||
|
v = qapi_dealloc_get_visitor(md);
|
||||||
|
visit_type_UserDefOne(v, &ret_in, "unused", NULL);
|
||||||
|
qapi_dealloc_visitor_cleanup(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qmp_marshal_input_my_command(QmpState *qmp__sess, QDict *args, QObject **ret, Error **errp)
|
static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
UserDefOne * retval = NULL;
|
UserDefOne * retval = NULL;
|
||||||
QmpInputVisitor *mi;
|
QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args));
|
||||||
QapiDeallocVisitor *md;
|
QapiDeallocVisitor *md;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
UserDefOne * arg1 = NULL;
|
UserDefOne * arg1 = NULL;
|
||||||
|
|
||||||
mi = qmp_input_visitor_new(QOBJECT(args));
|
|
||||||
v = qmp_input_get_visitor(mi);
|
v = qmp_input_get_visitor(mi);
|
||||||
visit_type_UserDefOne(v, &arg1, "arg1", errp);
|
visit_type_UserDefOne(v, &arg1, "arg1", &local_err);
|
||||||
|
if (local_err) {
|
||||||
if (error_is_set(errp)) {
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
retval = qmp_my_command(arg1, errp);
|
|
||||||
qmp_marshal_output_my_command(retval, ret, errp);
|
retval = qmp_my_command(arg1, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
qmp_marshal_output_my_command(retval, ret, &local_err);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
qmp_input_visitor_cleanup(mi);
|
||||||
md = qapi_dealloc_visitor_new();
|
md = qapi_dealloc_visitor_new();
|
||||||
v = qapi_dealloc_get_visitor(md);
|
v = qapi_dealloc_get_visitor(md);
|
||||||
visit_type_UserDefOne(v, &arg1, "arg1", errp);
|
visit_type_UserDefOne(v, &arg1, "arg1", NULL);
|
||||||
qapi_dealloc_visitor_cleanup(md);
|
qapi_dealloc_visitor_cleanup(md);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qmp_init_marshal(void)
|
static void qmp_init_marshal(void)
|
||||||
{
|
{
|
||||||
qmp_register_command("my-command", qmp_marshal_input_my_command);
|
qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_init(qmp_init_marshal);
|
qapi_init(qmp_init_marshal);
|
||||||
mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h
|
$ cat qapi-generated/example-qmp-commands.h
|
||||||
/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
#ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS
|
#ifndef EXAMPLE_QMP_COMMANDS_H
|
||||||
#define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS
|
#define EXAMPLE_QMP_COMMANDS_H
|
||||||
|
|
||||||
#include "example-qapi-types.h"
|
#include "example-qapi-types.h"
|
||||||
#include "error.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp);
|
UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
mdroth@illuin:~/w/qemu2.git$
|
|
||||||
|
@@ -556,6 +556,7 @@ ETEXI
|
|||||||
.params = "keys [hold_ms]",
|
.params = "keys [hold_ms]",
|
||||||
.help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)",
|
.help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)",
|
||||||
.mhandler.cmd = hmp_send_key,
|
.mhandler.cmd = hmp_send_key,
|
||||||
|
.command_completion = sendkey_completion,
|
||||||
},
|
},
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
@@ -1233,9 +1234,10 @@ ETEXI
|
|||||||
{
|
{
|
||||||
.name = "netdev_add",
|
.name = "netdev_add",
|
||||||
.args_type = "netdev:O",
|
.args_type = "netdev:O",
|
||||||
.params = "[user|tap|socket|hubport|netmap],id=str[,prop=value][,...]",
|
.params = "[user|tap|socket|vde|bridge|hubport|netmap],id=str[,prop=value][,...]",
|
||||||
.help = "add host network device",
|
.help = "add host network device",
|
||||||
.mhandler.cmd = hmp_netdev_add,
|
.mhandler.cmd = hmp_netdev_add,
|
||||||
|
.command_completion = netdev_add_completion,
|
||||||
},
|
},
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
@@ -1250,6 +1252,7 @@ ETEXI
|
|||||||
.params = "id",
|
.params = "id",
|
||||||
.help = "remove host network device",
|
.help = "remove host network device",
|
||||||
.mhandler.cmd = hmp_netdev_del,
|
.mhandler.cmd = hmp_netdev_del,
|
||||||
|
.command_completion = netdev_del_completion,
|
||||||
},
|
},
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
@@ -1339,6 +1342,7 @@ ETEXI
|
|||||||
.params = "name on|off",
|
.params = "name on|off",
|
||||||
.help = "change the link status of a network adapter",
|
.help = "change the link status of a network adapter",
|
||||||
.mhandler.cmd = hmp_set_link,
|
.mhandler.cmd = hmp_set_link,
|
||||||
|
.command_completion = set_link_completion,
|
||||||
},
|
},
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
@@ -1622,6 +1626,7 @@ ETEXI
|
|||||||
.params = "args",
|
.params = "args",
|
||||||
.help = "add chardev",
|
.help = "add chardev",
|
||||||
.mhandler.cmd = hmp_chardev_add,
|
.mhandler.cmd = hmp_chardev_add,
|
||||||
|
.command_completion = chardev_add_completion,
|
||||||
},
|
},
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
@@ -1638,6 +1643,7 @@ ETEXI
|
|||||||
.params = "id",
|
.params = "id",
|
||||||
.help = "remove chardev",
|
.help = "remove chardev",
|
||||||
.mhandler.cmd = hmp_chardev_remove,
|
.mhandler.cmd = hmp_chardev_remove,
|
||||||
|
.command_completion = chardev_remove_completion,
|
||||||
},
|
},
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
|
21
hmp.c
21
hmp.c
@@ -341,6 +341,11 @@ void hmp_info_block(Monitor *mon, const QDict *qdict)
|
|||||||
info->value->inserted->backing_file_depth);
|
info->value->inserted->backing_file_depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info->value->inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) {
|
||||||
|
monitor_printf(mon, " Detect zeroes: %s\n",
|
||||||
|
BlockdevDetectZeroesOptions_lookup[info->value->inserted->detect_zeroes]);
|
||||||
|
}
|
||||||
|
|
||||||
if (info->value->inserted->bps
|
if (info->value->inserted->bps
|
||||||
|| info->value->inserted->bps_rd
|
|| info->value->inserted->bps_rd
|
||||||
|| info->value->inserted->bps_wr
|
|| info->value->inserted->bps_wr
|
||||||
@@ -1388,6 +1393,7 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict)
|
|||||||
void hmp_object_add(Monitor *mon, const QDict *qdict)
|
void hmp_object_add(Monitor *mon, const QDict *qdict)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
Error *err_end = NULL;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
char *type = NULL;
|
char *type = NULL;
|
||||||
char *id = NULL;
|
char *id = NULL;
|
||||||
@@ -1411,24 +1417,23 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
|
|||||||
qdict_del(pdict, "qom-type");
|
qdict_del(pdict, "qom-type");
|
||||||
visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
|
visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out_clean;
|
goto out_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_del(pdict, "id");
|
qdict_del(pdict, "id");
|
||||||
visit_type_str(opts_get_visitor(ov), &id, "id", &err);
|
visit_type_str(opts_get_visitor(ov), &id, "id", &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out_clean;
|
goto out_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
object_add(type, id, pdict, opts_get_visitor(ov), &err);
|
object_add(type, id, pdict, opts_get_visitor(ov), &err);
|
||||||
if (err) {
|
|
||||||
goto out_clean;
|
out_end:
|
||||||
}
|
visit_end_struct(opts_get_visitor(ov), &err_end);
|
||||||
visit_end_struct(opts_get_visitor(ov), &err);
|
if (!err && err_end) {
|
||||||
if (err) {
|
|
||||||
qmp_object_del(id, NULL);
|
qmp_object_del(id, NULL);
|
||||||
}
|
}
|
||||||
|
error_propagate(&err, err_end);
|
||||||
out_clean:
|
out_clean:
|
||||||
opts_visitor_cleanup(ov);
|
opts_visitor_cleanup(ov);
|
||||||
|
|
||||||
|
6
hmp.h
6
hmp.h
@@ -97,5 +97,11 @@ void object_add_completion(ReadLineState *rs, int nb_args, const char *str);
|
|||||||
void object_del_completion(ReadLineState *rs, int nb_args, const char *str);
|
void object_del_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
void device_add_completion(ReadLineState *rs, int nb_args, const char *str);
|
void device_add_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
void device_del_completion(ReadLineState *rs, int nb_args, const char *str);
|
void device_del_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
|
void sendkey_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
|
void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
|
void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
|
void set_link_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
|
void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
|
void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -245,7 +245,7 @@ static void intel_hda_update_int_sts(IntelHDAState *d)
|
|||||||
|
|
||||||
/* update global status */
|
/* update global status */
|
||||||
if (sts & d->int_ctl) {
|
if (sts & d->int_ctl) {
|
||||||
sts |= (1 << 31);
|
sts |= (1U << 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
d->int_sts = sts;
|
d->int_sts = sts;
|
||||||
@@ -257,7 +257,7 @@ static void intel_hda_update_irq(IntelHDAState *d)
|
|||||||
int level;
|
int level;
|
||||||
|
|
||||||
intel_hda_update_int_sts(d);
|
intel_hda_update_int_sts(d);
|
||||||
if (d->int_sts & (1 << 31) && d->int_ctl & (1 << 31)) {
|
if (d->int_sts & (1U << 31) && d->int_ctl & (1U << 31)) {
|
||||||
level = 1;
|
level = 1;
|
||||||
} else {
|
} else {
|
||||||
level = 0;
|
level = 0;
|
||||||
@@ -574,7 +574,7 @@ static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint3
|
|||||||
if (st->ctl & 0x01) {
|
if (st->ctl & 0x01) {
|
||||||
/* reset */
|
/* reset */
|
||||||
dprint(d, 1, "st #%d: reset\n", reg->stream);
|
dprint(d, 1, "st #%d: reset\n", reg->stream);
|
||||||
st->ctl = 0;
|
st->ctl = SD_STS_FIFO_READY << 24;
|
||||||
}
|
}
|
||||||
if ((st->ctl & 0x02) != (old & 0x02)) {
|
if ((st->ctl & 0x02) != (old & 0x02)) {
|
||||||
uint32_t stnr = (st->ctl >> 20) & 0x0f;
|
uint32_t stnr = (st->ctl >> 20) & 0x0f;
|
||||||
@@ -829,6 +829,7 @@ static const struct IntelHDAReg regtab[] = {
|
|||||||
.wclear = 0x1c000000, \
|
.wclear = 0x1c000000, \
|
||||||
.offset = offsetof(IntelHDAState, st[_i].ctl), \
|
.offset = offsetof(IntelHDAState, st[_i].ctl), \
|
||||||
.whandler = intel_hda_set_st_ctl, \
|
.whandler = intel_hda_set_st_ctl, \
|
||||||
|
.reset = SD_STS_FIFO_READY << 24 \
|
||||||
}, \
|
}, \
|
||||||
[ ST_REG(_i, ICH6_REG_SD_LPIB) ] = { \
|
[ ST_REG(_i, ICH6_REG_SD_LPIB) ] = { \
|
||||||
.stream = _i, \
|
.stream = _i, \
|
||||||
|
216
hw/input/hid.c
216
hw/input/hid.c
@@ -105,70 +105,135 @@ void hid_set_next_idle(HIDState *hs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
|
static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
|
||||||
|
InputEvent *evt)
|
||||||
{
|
{
|
||||||
e->xdx = e->ydy = e->dz = 0;
|
static const int bmap[INPUT_BUTTON_MAX] = {
|
||||||
e->buttons_state = buttons;
|
[INPUT_BUTTON_LEFT] = 0x01,
|
||||||
}
|
[INPUT_BUTTON_RIGHT] = 0x02,
|
||||||
|
[INPUT_BUTTON_MIDDLE] = 0x04,
|
||||||
|
};
|
||||||
|
HIDState *hs = (HIDState *)dev;
|
||||||
|
HIDPointerEvent *e;
|
||||||
|
|
||||||
static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
|
assert(hs->n < QUEUE_LENGTH);
|
||||||
int x1, int y1, int z1) {
|
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
|
||||||
if (xyrel) {
|
|
||||||
e->xdx += x1;
|
switch (evt->kind) {
|
||||||
e->ydy += y1;
|
case INPUT_EVENT_KIND_REL:
|
||||||
|
if (evt->rel->axis == INPUT_AXIS_X) {
|
||||||
|
e->xdx += evt->rel->value;
|
||||||
|
} else if (evt->rel->axis == INPUT_AXIS_Y) {
|
||||||
|
e->ydy -= evt->rel->value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INPUT_EVENT_KIND_ABS:
|
||||||
|
if (evt->rel->axis == INPUT_AXIS_X) {
|
||||||
|
e->xdx = evt->rel->value;
|
||||||
|
} else if (evt->rel->axis == INPUT_AXIS_Y) {
|
||||||
|
e->ydy = evt->rel->value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INPUT_EVENT_KIND_BTN:
|
||||||
|
if (evt->btn->down) {
|
||||||
|
e->buttons_state |= bmap[evt->btn->button];
|
||||||
|
if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
|
||||||
|
e->dz--;
|
||||||
|
} else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
|
||||||
|
e->dz++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
e->xdx = x1;
|
e->buttons_state &= ~bmap[evt->btn->button];
|
||||||
e->ydy = y1;
|
|
||||||
/* Windows drivers do not like the 0/0 position and ignore such
|
|
||||||
* events. */
|
|
||||||
if (!(x1 | y1)) {
|
|
||||||
e->xdx = 1;
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
e->dz += z1;
|
|
||||||
|
default:
|
||||||
|
/* keep gcc happy */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hid_pointer_event(void *opaque,
|
}
|
||||||
int x1, int y1, int z1, int buttons_state)
|
|
||||||
|
static void hid_pointer_sync(DeviceState *dev)
|
||||||
{
|
{
|
||||||
HIDState *hs = opaque;
|
HIDState *hs = (HIDState *)dev;
|
||||||
unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
|
HIDPointerEvent *prev, *curr, *next;
|
||||||
unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
|
bool event_compression = false;
|
||||||
|
|
||||||
/* We combine events where feasible to keep the queue small. We shouldn't
|
if (hs->n == QUEUE_LENGTH-1) {
|
||||||
* combine anything with the first event of a particular button state, as
|
/*
|
||||||
* that would change the location of the button state change. When the
|
* Queue full. We are loosing information, but we at least
|
||||||
* queue is empty, a second event is needed because we don't know if
|
* keep track of most recent button state.
|
||||||
* the first event changed the button state. */
|
*/
|
||||||
if (hs->n == QUEUE_LENGTH) {
|
return;
|
||||||
/* Queue full. Discard old button state, combine motion normally. */
|
|
||||||
hs->ptr.queue[use_slot].buttons_state = buttons_state;
|
|
||||||
} else if (hs->n < 2 ||
|
|
||||||
hs->ptr.queue[use_slot].buttons_state != buttons_state ||
|
|
||||||
hs->ptr.queue[previous_slot].buttons_state !=
|
|
||||||
hs->ptr.queue[use_slot].buttons_state) {
|
|
||||||
/* Cannot or should not combine, so add an empty item to the queue. */
|
|
||||||
QUEUE_INCR(use_slot);
|
|
||||||
hs->n++;
|
|
||||||
hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
|
|
||||||
}
|
}
|
||||||
hid_pointer_event_combine(&hs->ptr.queue[use_slot],
|
|
||||||
hs->kind == HID_MOUSE,
|
prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
|
||||||
x1, y1, z1);
|
curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
|
||||||
|
next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
|
||||||
|
|
||||||
|
if (hs->n > 0) {
|
||||||
|
/*
|
||||||
|
* No button state change between previous and current event
|
||||||
|
* (and previous wasn't seen by the guest yet), so there is
|
||||||
|
* motion information only and we can combine the two event
|
||||||
|
* into one.
|
||||||
|
*/
|
||||||
|
if (curr->buttons_state == prev->buttons_state) {
|
||||||
|
event_compression = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_compression) {
|
||||||
|
/* add current motion to previous, clear current */
|
||||||
|
if (hs->kind == HID_MOUSE) {
|
||||||
|
prev->xdx += curr->xdx;
|
||||||
|
curr->xdx = 0;
|
||||||
|
prev->ydy -= curr->ydy;
|
||||||
|
curr->ydy = 0;
|
||||||
|
} else {
|
||||||
|
prev->xdx = curr->xdx;
|
||||||
|
prev->ydy = curr->ydy;
|
||||||
|
}
|
||||||
|
prev->dz += curr->dz;
|
||||||
|
curr->dz = 0;
|
||||||
|
} else {
|
||||||
|
/* prepate next (clear rel, copy abs + btns) */
|
||||||
|
if (hs->kind == HID_MOUSE) {
|
||||||
|
next->xdx = 0;
|
||||||
|
next->ydy = 0;
|
||||||
|
} else {
|
||||||
|
next->xdx = curr->xdx;
|
||||||
|
next->ydy = curr->ydy;
|
||||||
|
}
|
||||||
|
next->dz = 0;
|
||||||
|
next->buttons_state = curr->buttons_state;
|
||||||
|
/* make current guest visible, notify guest */
|
||||||
|
hs->n++;
|
||||||
hs->event(hs);
|
hs->event(hs);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void hid_keyboard_event(void *opaque, int keycode)
|
static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
|
||||||
|
InputEvent *evt)
|
||||||
{
|
{
|
||||||
HIDState *hs = opaque;
|
HIDState *hs = (HIDState *)dev;
|
||||||
|
int scancodes[3], i, count;
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
if (hs->n == QUEUE_LENGTH) {
|
count = qemu_input_key_value_to_scancode(evt->key->key,
|
||||||
|
evt->key->down,
|
||||||
|
scancodes);
|
||||||
|
if (hs->n + count > QUEUE_LENGTH) {
|
||||||
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
|
slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
|
||||||
hs->kbd.keycodes[slot] = keycode;
|
hs->kbd.keycodes[slot] = scancodes[i];
|
||||||
|
}
|
||||||
hs->event(hs);
|
hs->event(hs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,14 +312,14 @@ static inline int int_clamp(int val, int vmin, int vmax)
|
|||||||
void hid_pointer_activate(HIDState *hs)
|
void hid_pointer_activate(HIDState *hs)
|
||||||
{
|
{
|
||||||
if (!hs->ptr.mouse_grabbed) {
|
if (!hs->ptr.mouse_grabbed) {
|
||||||
qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
|
qemu_input_handler_activate(hs->s);
|
||||||
hs->ptr.mouse_grabbed = 1;
|
hs->ptr.mouse_grabbed = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
int dx, dy, dz, b, l;
|
int dx, dy, dz, l;
|
||||||
int index;
|
int index;
|
||||||
HIDPointerEvent *e;
|
HIDPointerEvent *e;
|
||||||
|
|
||||||
@@ -279,17 +344,6 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
|||||||
dz = int_clamp(e->dz, -127, 127);
|
dz = int_clamp(e->dz, -127, 127);
|
||||||
e->dz -= dz;
|
e->dz -= dz;
|
||||||
|
|
||||||
b = 0;
|
|
||||||
if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
|
|
||||||
b |= 0x01;
|
|
||||||
}
|
|
||||||
if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
|
|
||||||
b |= 0x02;
|
|
||||||
}
|
|
||||||
if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
|
|
||||||
b |= 0x04;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hs->n &&
|
if (hs->n &&
|
||||||
!e->dz &&
|
!e->dz &&
|
||||||
(hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
|
(hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
|
||||||
@@ -304,7 +358,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
|||||||
switch (hs->kind) {
|
switch (hs->kind) {
|
||||||
case HID_MOUSE:
|
case HID_MOUSE:
|
||||||
if (len > l) {
|
if (len > l) {
|
||||||
buf[l++] = b;
|
buf[l++] = e->buttons_state;
|
||||||
}
|
}
|
||||||
if (len > l) {
|
if (len > l) {
|
||||||
buf[l++] = dx;
|
buf[l++] = dx;
|
||||||
@@ -319,7 +373,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
|
|||||||
|
|
||||||
case HID_TABLET:
|
case HID_TABLET:
|
||||||
if (len > l) {
|
if (len > l) {
|
||||||
buf[l++] = b;
|
buf[l++] = e->buttons_state;
|
||||||
}
|
}
|
||||||
if (len > l) {
|
if (len > l) {
|
||||||
buf[l++] = dx & 0xff;
|
buf[l++] = dx & 0xff;
|
||||||
@@ -413,31 +467,45 @@ void hid_reset(HIDState *hs)
|
|||||||
|
|
||||||
void hid_free(HIDState *hs)
|
void hid_free(HIDState *hs)
|
||||||
{
|
{
|
||||||
switch (hs->kind) {
|
qemu_input_handler_unregister(hs->s);
|
||||||
case HID_KEYBOARD:
|
|
||||||
qemu_remove_kbd_event_handler(hs->kbd.eh_entry);
|
|
||||||
break;
|
|
||||||
case HID_MOUSE:
|
|
||||||
case HID_TABLET:
|
|
||||||
qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
hid_del_idle_timer(hs);
|
hid_del_idle_timer(hs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QemuInputHandler hid_keyboard_handler = {
|
||||||
|
.name = "QEMU HID Keyboard",
|
||||||
|
.mask = INPUT_EVENT_MASK_KEY,
|
||||||
|
.event = hid_keyboard_event,
|
||||||
|
};
|
||||||
|
|
||||||
|
static QemuInputHandler hid_mouse_handler = {
|
||||||
|
.name = "QEMU HID Mouse",
|
||||||
|
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
|
||||||
|
.event = hid_pointer_event,
|
||||||
|
.sync = hid_pointer_sync,
|
||||||
|
};
|
||||||
|
|
||||||
|
static QemuInputHandler hid_tablet_handler = {
|
||||||
|
.name = "QEMU HID Tablet",
|
||||||
|
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
|
||||||
|
.event = hid_pointer_event,
|
||||||
|
.sync = hid_pointer_sync,
|
||||||
|
};
|
||||||
|
|
||||||
void hid_init(HIDState *hs, int kind, HIDEventFunc event)
|
void hid_init(HIDState *hs, int kind, HIDEventFunc event)
|
||||||
{
|
{
|
||||||
hs->kind = kind;
|
hs->kind = kind;
|
||||||
hs->event = event;
|
hs->event = event;
|
||||||
|
|
||||||
if (hs->kind == HID_KEYBOARD) {
|
if (hs->kind == HID_KEYBOARD) {
|
||||||
hs->kbd.eh_entry = qemu_add_kbd_event_handler(hid_keyboard_event, hs);
|
hs->s = qemu_input_handler_register((DeviceState *)hs,
|
||||||
|
&hid_keyboard_handler);
|
||||||
|
qemu_input_handler_activate(hs->s);
|
||||||
} else if (hs->kind == HID_MOUSE) {
|
} else if (hs->kind == HID_MOUSE) {
|
||||||
hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
|
hs->s = qemu_input_handler_register((DeviceState *)hs,
|
||||||
0, "QEMU HID Mouse");
|
&hid_mouse_handler);
|
||||||
} else if (hs->kind == HID_TABLET) {
|
} else if (hs->kind == HID_TABLET) {
|
||||||
hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
|
hs->s = qemu_input_handler_register((DeviceState *)hs,
|
||||||
1, "QEMU HID Tablet");
|
&hid_tablet_handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,3 +26,4 @@ obj-$(CONFIG_XICS) += xics.o
|
|||||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||||
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
||||||
|
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
|
||||||
|
@@ -1,322 +1,103 @@
|
|||||||
/*
|
/*
|
||||||
* QEMU S390x KVM floating interrupt controller (flic)
|
* QEMU S390x floating interrupt controller (flic)
|
||||||
*
|
*
|
||||||
* Copyright 2014 IBM Corp.
|
* Copyright 2014 IBM Corp.
|
||||||
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
||||||
|
* Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||||
*
|
*
|
||||||
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||||
* your option) any later version. See the COPYING file in the top-level
|
* your option) any later version. See the COPYING file in the top-level
|
||||||
* directory.
|
* directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "sysemu/kvm.h"
|
|
||||||
#include "migration/qemu-file.h"
|
#include "migration/qemu-file.h"
|
||||||
#include "hw/s390x/s390_flic.h"
|
#include "hw/s390x/s390_flic.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#define FLIC_SAVE_INITIAL_SIZE getpagesize()
|
S390FLICState *s390_get_flic(void)
|
||||||
#define FLIC_FAILED (-1UL)
|
{
|
||||||
#define FLIC_SAVEVM_VERSION 1
|
S390FLICState *fs;
|
||||||
|
|
||||||
|
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL));
|
||||||
|
if (!fs) {
|
||||||
|
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL));
|
||||||
|
}
|
||||||
|
return fs;
|
||||||
|
}
|
||||||
|
|
||||||
void s390_flic_init(void)
|
void s390_flic_init(void)
|
||||||
{
|
{
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (kvm_enabled()) {
|
dev = s390_flic_kvm_create();
|
||||||
dev = qdev_create(NULL, "s390-flic");
|
if (!dev) {
|
||||||
object_property_add_child(qdev_get_machine(), "s390-flic",
|
dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC);
|
||||||
|
object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC,
|
||||||
OBJECT(dev), NULL);
|
OBJECT(dev), NULL);
|
||||||
|
}
|
||||||
r = qdev_init(dev);
|
r = qdev_init(dev);
|
||||||
if (r) {
|
if (r) {
|
||||||
error_report("flic: couldn't create qdev");
|
error_report("flic: couldn't create qdev");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
|
||||||
* flic_get_all_irqs - store all pending irqs in buffer
|
uint8_t isc, bool swap,
|
||||||
* @buf: pointer to buffer which is passed to kernel
|
bool is_maskable)
|
||||||
* @len: length of buffer
|
|
||||||
* @flic: pointer to flic device state
|
|
||||||
*
|
|
||||||
* Returns: -ENOMEM if buffer is too small,
|
|
||||||
* -EINVAL if attr.group is invalid,
|
|
||||||
* -EFAULT if copying to userspace failed,
|
|
||||||
* on success return number of stored interrupts
|
|
||||||
*/
|
|
||||||
static int flic_get_all_irqs(KVMS390FLICState *flic,
|
|
||||||
void *buf, int len)
|
|
||||||
{
|
{
|
||||||
struct kvm_device_attr attr = {
|
/* nothing to do */
|
||||||
.group = KVM_DEV_FLIC_GET_ALL_IRQS,
|
return 0;
|
||||||
.addr = (uint64_t) buf,
|
|
||||||
.attr = len,
|
|
||||||
};
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
|
|
||||||
|
|
||||||
return rc == -1 ? -errno : rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flic_enable_pfault(KVMS390FLICState *flic)
|
static int qemu_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
|
||||||
|
uint64_t map_addr, bool do_map)
|
||||||
{
|
{
|
||||||
struct kvm_device_attr attr = {
|
/* nothing to do */
|
||||||
.group = KVM_DEV_FLIC_APF_ENABLE,
|
return 0;
|
||||||
};
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
fprintf(stderr, "flic: couldn't enable pfault\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flic_disable_wait_pfault(KVMS390FLICState *flic)
|
static int qemu_s390_add_adapter_routes(S390FLICState *fs,
|
||||||
|
AdapterRoutes *routes)
|
||||||
{
|
{
|
||||||
struct kvm_device_attr attr = {
|
return -ENOSYS;
|
||||||
.group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
|
|
||||||
};
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
fprintf(stderr, "flic: couldn't disable pfault\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** flic_enqueue_irqs - returns 0 on success
|
static void qemu_s390_release_adapter_routes(S390FLICState *fs,
|
||||||
* @buf: pointer to buffer which is passed to kernel
|
AdapterRoutes *routes)
|
||||||
* @len: length of buffer
|
|
||||||
* @flic: pointer to flic device state
|
|
||||||
*
|
|
||||||
* Returns: -EINVAL if attr.group is unknown
|
|
||||||
*/
|
|
||||||
static int flic_enqueue_irqs(void *buf, uint64_t len,
|
|
||||||
KVMS390FLICState *flic)
|
|
||||||
{
|
{
|
||||||
int rc;
|
}
|
||||||
struct kvm_device_attr attr = {
|
|
||||||
.group = KVM_DEV_FLIC_ENQUEUE,
|
static void qemu_s390_flic_class_init(ObjectClass *oc, void *data)
|
||||||
.addr = (uint64_t) buf,
|
{
|
||||||
.attr = len,
|
S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
|
||||||
|
|
||||||
|
fsc->register_io_adapter = qemu_s390_register_io_adapter;
|
||||||
|
fsc->io_adapter_map = qemu_s390_io_adapter_map;
|
||||||
|
fsc->add_adapter_routes = qemu_s390_add_adapter_routes;
|
||||||
|
fsc->release_adapter_routes = qemu_s390_release_adapter_routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo qemu_s390_flic_info = {
|
||||||
|
.name = TYPE_QEMU_S390_FLIC,
|
||||||
|
.parent = TYPE_S390_FLIC_COMMON,
|
||||||
|
.instance_size = sizeof(QEMUS390FLICState),
|
||||||
|
.class_init = qemu_s390_flic_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
static const TypeInfo s390_flic_common_info = {
|
||||||
|
.name = TYPE_S390_FLIC_COMMON,
|
||||||
return rc ? -errno : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __get_all_irqs - store all pending irqs in buffer
|
|
||||||
* @flic: pointer to flic device state
|
|
||||||
* @buf: pointer to pointer to a buffer
|
|
||||||
* @len: length of buffer
|
|
||||||
*
|
|
||||||
* Returns: return value of flic_get_all_irqs
|
|
||||||
* Note: Retry and increase buffer size until flic_get_all_irqs
|
|
||||||
* either returns a value >= 0 or a negative error code.
|
|
||||||
* -ENOMEM is an exception, which means the buffer is too small
|
|
||||||
* and we should try again. Other negative error codes can be
|
|
||||||
* -EFAULT and -EINVAL which we ignore at this point
|
|
||||||
*/
|
|
||||||
static int __get_all_irqs(KVMS390FLICState *flic,
|
|
||||||
void **buf, int len)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
do {
|
|
||||||
/* returns -ENOMEM if buffer is too small and number
|
|
||||||
* of queued interrupts on success */
|
|
||||||
r = flic_get_all_irqs(flic, *buf, len);
|
|
||||||
if (r >= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
len *= 2;
|
|
||||||
*buf = g_try_realloc(*buf, len);
|
|
||||||
if (!buf) {
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
} while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* kvm_flic_save - Save pending floating interrupts
|
|
||||||
* @f: QEMUFile containing migration state
|
|
||||||
* @opaque: pointer to flic device state
|
|
||||||
*
|
|
||||||
* Note: Pass buf and len to kernel. Start with one page and
|
|
||||||
* increase until buffer is sufficient or maxium size is
|
|
||||||
* reached
|
|
||||||
*/
|
|
||||||
static void kvm_flic_save(QEMUFile *f, void *opaque)
|
|
||||||
{
|
|
||||||
KVMS390FLICState *flic = opaque;
|
|
||||||
int len = FLIC_SAVE_INITIAL_SIZE;
|
|
||||||
void *buf;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
|
|
||||||
|
|
||||||
buf = g_try_malloc0(len);
|
|
||||||
if (!buf) {
|
|
||||||
/* Storing FLIC_FAILED into the count field here will cause the
|
|
||||||
* target system to fail when attempting to load irqs from the
|
|
||||||
* migration state */
|
|
||||||
error_report("flic: couldn't allocate memory");
|
|
||||||
qemu_put_be64(f, FLIC_FAILED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
count = __get_all_irqs(flic, &buf, len);
|
|
||||||
if (count < 0) {
|
|
||||||
error_report("flic: couldn't retrieve irqs from kernel, rc %d",
|
|
||||||
count);
|
|
||||||
/* Storing FLIC_FAILED into the count field here will cause the
|
|
||||||
* target system to fail when attempting to load irqs from the
|
|
||||||
* migration state */
|
|
||||||
qemu_put_be64(f, FLIC_FAILED);
|
|
||||||
} else {
|
|
||||||
qemu_put_be64(f, count);
|
|
||||||
qemu_put_buffer(f, (uint8_t *) buf,
|
|
||||||
count * sizeof(struct kvm_s390_irq));
|
|
||||||
}
|
|
||||||
g_free(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* kvm_flic_load - Load pending floating interrupts
|
|
||||||
* @f: QEMUFile containing migration state
|
|
||||||
* @opaque: pointer to flic device state
|
|
||||||
* @version_id: version id for migration
|
|
||||||
*
|
|
||||||
* Returns: value of flic_enqueue_irqs, -EINVAL on error
|
|
||||||
* Note: Do nothing when no interrupts where stored
|
|
||||||
* in QEMUFile
|
|
||||||
*/
|
|
||||||
static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
|
|
||||||
{
|
|
||||||
uint64_t len = 0;
|
|
||||||
uint64_t count = 0;
|
|
||||||
void *buf = NULL;
|
|
||||||
int r = 0;
|
|
||||||
|
|
||||||
if (version_id != FLIC_SAVEVM_VERSION) {
|
|
||||||
r = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
flic_enable_pfault((struct KVMS390FLICState *) opaque);
|
|
||||||
|
|
||||||
count = qemu_get_be64(f);
|
|
||||||
len = count * sizeof(struct kvm_s390_irq);
|
|
||||||
if (count == FLIC_FAILED) {
|
|
||||||
r = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (count == 0) {
|
|
||||||
r = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
buf = g_try_malloc0(len);
|
|
||||||
if (!buf) {
|
|
||||||
r = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
|
|
||||||
r = -EINVAL;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
|
|
||||||
|
|
||||||
out_free:
|
|
||||||
g_free(buf);
|
|
||||||
out:
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
|
|
||||||
{
|
|
||||||
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
|
||||||
struct kvm_create_device cd = {0};
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
flic_state->fd = -1;
|
|
||||||
if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
|
|
||||||
trace_flic_no_device_api(errno);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cd.type = KVM_DEV_TYPE_FLIC;
|
|
||||||
ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
|
|
||||||
if (ret < 0) {
|
|
||||||
trace_flic_create_device(errno);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
flic_state->fd = cd.fd;
|
|
||||||
|
|
||||||
/* Register savevm handler for floating interrupts */
|
|
||||||
register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
|
|
||||||
kvm_flic_load, (void *) flic_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
|
|
||||||
{
|
|
||||||
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
|
||||||
|
|
||||||
unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void kvm_s390_flic_reset(DeviceState *dev)
|
|
||||||
{
|
|
||||||
KVMS390FLICState *flic = KVM_S390_FLIC(dev);
|
|
||||||
struct kvm_device_attr attr = {
|
|
||||||
.group = KVM_DEV_FLIC_CLEAR_IRQS,
|
|
||||||
};
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
if (flic->fd == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
flic_disable_wait_pfault(flic);
|
|
||||||
|
|
||||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
|
||||||
if (rc) {
|
|
||||||
trace_flic_reset_failed(errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
flic_enable_pfault(flic);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
|
|
||||||
{
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
|
||||||
|
|
||||||
dc->realize = kvm_s390_flic_realize;
|
|
||||||
dc->unrealize = kvm_s390_flic_unrealize;
|
|
||||||
dc->reset = kvm_s390_flic_reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const TypeInfo kvm_s390_flic_info = {
|
|
||||||
.name = TYPE_KVM_S390_FLIC,
|
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
.instance_size = sizeof(KVMS390FLICState),
|
.instance_size = sizeof(S390FLICState),
|
||||||
.class_init = kvm_s390_flic_class_init,
|
.class_size = sizeof(S390FLICStateClass),
|
||||||
};
|
};
|
||||||
|
|
||||||
static void kvm_s390_flic_register_types(void)
|
static void qemu_s390_flic_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&kvm_s390_flic_info);
|
type_register_static(&s390_flic_common_info);
|
||||||
|
type_register_static(&qemu_s390_flic_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(kvm_s390_flic_register_types)
|
type_init(qemu_s390_flic_register_types)
|
||||||
|
420
hw/intc/s390_flic_kvm.c
Normal file
420
hw/intc/s390_flic_kvm.c
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
/*
|
||||||
|
* QEMU S390x KVM floating interrupt controller (flic)
|
||||||
|
*
|
||||||
|
* Copyright 2014 IBM Corp.
|
||||||
|
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
||||||
|
* Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||||
|
* your option) any later version. See the COPYING file in the top-level
|
||||||
|
* directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
#include "migration/qemu-file.h"
|
||||||
|
#include "hw/s390x/s390_flic.h"
|
||||||
|
#include "hw/s390x/adapter.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
#define FLIC_SAVE_INITIAL_SIZE getpagesize()
|
||||||
|
#define FLIC_FAILED (-1UL)
|
||||||
|
#define FLIC_SAVEVM_VERSION 1
|
||||||
|
|
||||||
|
typedef struct KVMS390FLICState {
|
||||||
|
S390FLICState parent_obj;
|
||||||
|
|
||||||
|
uint32_t fd;
|
||||||
|
} KVMS390FLICState;
|
||||||
|
|
||||||
|
DeviceState *s390_flic_kvm_create(void)
|
||||||
|
{
|
||||||
|
DeviceState *dev = NULL;
|
||||||
|
|
||||||
|
if (kvm_enabled()) {
|
||||||
|
dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
|
||||||
|
object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
|
||||||
|
OBJECT(dev), NULL);
|
||||||
|
}
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* flic_get_all_irqs - store all pending irqs in buffer
|
||||||
|
* @buf: pointer to buffer which is passed to kernel
|
||||||
|
* @len: length of buffer
|
||||||
|
* @flic: pointer to flic device state
|
||||||
|
*
|
||||||
|
* Returns: -ENOMEM if buffer is too small,
|
||||||
|
* -EINVAL if attr.group is invalid,
|
||||||
|
* -EFAULT if copying to userspace failed,
|
||||||
|
* on success return number of stored interrupts
|
||||||
|
*/
|
||||||
|
static int flic_get_all_irqs(KVMS390FLICState *flic,
|
||||||
|
void *buf, int len)
|
||||||
|
{
|
||||||
|
struct kvm_device_attr attr = {
|
||||||
|
.group = KVM_DEV_FLIC_GET_ALL_IRQS,
|
||||||
|
.addr = (uint64_t) buf,
|
||||||
|
.attr = len,
|
||||||
|
};
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
|
||||||
|
|
||||||
|
return rc == -1 ? -errno : rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flic_enable_pfault(KVMS390FLICState *flic)
|
||||||
|
{
|
||||||
|
struct kvm_device_attr attr = {
|
||||||
|
.group = KVM_DEV_FLIC_APF_ENABLE,
|
||||||
|
};
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
fprintf(stderr, "flic: couldn't enable pfault\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flic_disable_wait_pfault(KVMS390FLICState *flic)
|
||||||
|
{
|
||||||
|
struct kvm_device_attr attr = {
|
||||||
|
.group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
|
||||||
|
};
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
fprintf(stderr, "flic: couldn't disable pfault\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** flic_enqueue_irqs - returns 0 on success
|
||||||
|
* @buf: pointer to buffer which is passed to kernel
|
||||||
|
* @len: length of buffer
|
||||||
|
* @flic: pointer to flic device state
|
||||||
|
*
|
||||||
|
* Returns: -EINVAL if attr.group is unknown
|
||||||
|
*/
|
||||||
|
static int flic_enqueue_irqs(void *buf, uint64_t len,
|
||||||
|
KVMS390FLICState *flic)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct kvm_device_attr attr = {
|
||||||
|
.group = KVM_DEV_FLIC_ENQUEUE,
|
||||||
|
.addr = (uint64_t) buf,
|
||||||
|
.attr = len,
|
||||||
|
};
|
||||||
|
|
||||||
|
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||||
|
|
||||||
|
return rc ? -errno : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __get_all_irqs - store all pending irqs in buffer
|
||||||
|
* @flic: pointer to flic device state
|
||||||
|
* @buf: pointer to pointer to a buffer
|
||||||
|
* @len: length of buffer
|
||||||
|
*
|
||||||
|
* Returns: return value of flic_get_all_irqs
|
||||||
|
* Note: Retry and increase buffer size until flic_get_all_irqs
|
||||||
|
* either returns a value >= 0 or a negative error code.
|
||||||
|
* -ENOMEM is an exception, which means the buffer is too small
|
||||||
|
* and we should try again. Other negative error codes can be
|
||||||
|
* -EFAULT and -EINVAL which we ignore at this point
|
||||||
|
*/
|
||||||
|
static int __get_all_irqs(KVMS390FLICState *flic,
|
||||||
|
void **buf, int len)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* returns -ENOMEM if buffer is too small and number
|
||||||
|
* of queued interrupts on success */
|
||||||
|
r = flic_get_all_irqs(flic, *buf, len);
|
||||||
|
if (r >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len *= 2;
|
||||||
|
*buf = g_try_realloc(*buf, len);
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
} while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
|
||||||
|
uint8_t isc, bool swap,
|
||||||
|
bool is_maskable)
|
||||||
|
{
|
||||||
|
struct kvm_s390_io_adapter adapter = {
|
||||||
|
.id = id,
|
||||||
|
.isc = isc,
|
||||||
|
.maskable = is_maskable,
|
||||||
|
.swap = swap,
|
||||||
|
};
|
||||||
|
KVMS390FLICState *flic = KVM_S390_FLIC(fs);
|
||||||
|
int r, ret;
|
||||||
|
struct kvm_device_attr attr = {
|
||||||
|
.group = KVM_DEV_FLIC_ADAPTER_REGISTER,
|
||||||
|
.addr = (uint64_t)&adapter,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||||
|
|
||||||
|
ret = r ? -errno : 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
|
||||||
|
uint64_t map_addr, bool do_map)
|
||||||
|
{
|
||||||
|
struct kvm_s390_io_adapter_req req = {
|
||||||
|
.id = id,
|
||||||
|
.type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
|
||||||
|
.addr = map_addr,
|
||||||
|
};
|
||||||
|
struct kvm_device_attr attr = {
|
||||||
|
.group = KVM_DEV_FLIC_ADAPTER_MODIFY,
|
||||||
|
.addr = (uint64_t)&req,
|
||||||
|
};
|
||||||
|
KVMS390FLICState *flic = KVM_S390_FLIC(fs);
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||||
|
return r ? -errno : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_s390_add_adapter_routes(S390FLICState *fs,
|
||||||
|
AdapterRoutes *routes)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
uint64_t ind_offset = routes->adapter.ind_offset;
|
||||||
|
|
||||||
|
for (i = 0; i < routes->num_routes; i++) {
|
||||||
|
ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out_undo;
|
||||||
|
}
|
||||||
|
routes->gsi[i] = ret;
|
||||||
|
routes->adapter.ind_offset++;
|
||||||
|
}
|
||||||
|
/* Restore passed-in structure to original state. */
|
||||||
|
routes->adapter.ind_offset = ind_offset;
|
||||||
|
return 0;
|
||||||
|
out_undo:
|
||||||
|
while (--i >= 0) {
|
||||||
|
kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
|
||||||
|
routes->gsi[i] = -1;
|
||||||
|
}
|
||||||
|
routes->adapter.ind_offset = ind_offset;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_s390_release_adapter_routes(S390FLICState *fs,
|
||||||
|
AdapterRoutes *routes)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < routes->num_routes; i++) {
|
||||||
|
if (routes->gsi[i] >= 0) {
|
||||||
|
kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
|
||||||
|
routes->gsi[i] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_flic_save - Save pending floating interrupts
|
||||||
|
* @f: QEMUFile containing migration state
|
||||||
|
* @opaque: pointer to flic device state
|
||||||
|
*
|
||||||
|
* Note: Pass buf and len to kernel. Start with one page and
|
||||||
|
* increase until buffer is sufficient or maxium size is
|
||||||
|
* reached
|
||||||
|
*/
|
||||||
|
static void kvm_flic_save(QEMUFile *f, void *opaque)
|
||||||
|
{
|
||||||
|
KVMS390FLICState *flic = opaque;
|
||||||
|
int len = FLIC_SAVE_INITIAL_SIZE;
|
||||||
|
void *buf;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
|
||||||
|
|
||||||
|
buf = g_try_malloc0(len);
|
||||||
|
if (!buf) {
|
||||||
|
/* Storing FLIC_FAILED into the count field here will cause the
|
||||||
|
* target system to fail when attempting to load irqs from the
|
||||||
|
* migration state */
|
||||||
|
error_report("flic: couldn't allocate memory");
|
||||||
|
qemu_put_be64(f, FLIC_FAILED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = __get_all_irqs(flic, &buf, len);
|
||||||
|
if (count < 0) {
|
||||||
|
error_report("flic: couldn't retrieve irqs from kernel, rc %d",
|
||||||
|
count);
|
||||||
|
/* Storing FLIC_FAILED into the count field here will cause the
|
||||||
|
* target system to fail when attempting to load irqs from the
|
||||||
|
* migration state */
|
||||||
|
qemu_put_be64(f, FLIC_FAILED);
|
||||||
|
} else {
|
||||||
|
qemu_put_be64(f, count);
|
||||||
|
qemu_put_buffer(f, (uint8_t *) buf,
|
||||||
|
count * sizeof(struct kvm_s390_irq));
|
||||||
|
}
|
||||||
|
g_free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_flic_load - Load pending floating interrupts
|
||||||
|
* @f: QEMUFile containing migration state
|
||||||
|
* @opaque: pointer to flic device state
|
||||||
|
* @version_id: version id for migration
|
||||||
|
*
|
||||||
|
* Returns: value of flic_enqueue_irqs, -EINVAL on error
|
||||||
|
* Note: Do nothing when no interrupts where stored
|
||||||
|
* in QEMUFile
|
||||||
|
*/
|
||||||
|
static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
uint64_t len = 0;
|
||||||
|
uint64_t count = 0;
|
||||||
|
void *buf = NULL;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
if (version_id != FLIC_SAVEVM_VERSION) {
|
||||||
|
r = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
flic_enable_pfault((struct KVMS390FLICState *) opaque);
|
||||||
|
|
||||||
|
count = qemu_get_be64(f);
|
||||||
|
len = count * sizeof(struct kvm_s390_irq);
|
||||||
|
if (count == FLIC_FAILED) {
|
||||||
|
r = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (count == 0) {
|
||||||
|
r = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
buf = g_try_malloc0(len);
|
||||||
|
if (!buf) {
|
||||||
|
r = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
|
||||||
|
r = -EINVAL;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
g_free(buf);
|
||||||
|
out:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
||||||
|
struct kvm_create_device cd = {0};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
flic_state->fd = -1;
|
||||||
|
if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
|
||||||
|
trace_flic_no_device_api(errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cd.type = KVM_DEV_TYPE_FLIC;
|
||||||
|
ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
|
||||||
|
if (ret < 0) {
|
||||||
|
trace_flic_create_device(errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flic_state->fd = cd.fd;
|
||||||
|
|
||||||
|
/* Register savevm handler for floating interrupts */
|
||||||
|
register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
|
||||||
|
kvm_flic_load, (void *) flic_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
||||||
|
|
||||||
|
unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_s390_flic_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
KVMS390FLICState *flic = KVM_S390_FLIC(dev);
|
||||||
|
struct kvm_device_attr attr = {
|
||||||
|
.group = KVM_DEV_FLIC_CLEAR_IRQS,
|
||||||
|
};
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (flic->fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flic_disable_wait_pfault(flic);
|
||||||
|
|
||||||
|
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||||
|
if (rc) {
|
||||||
|
trace_flic_reset_failed(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
flic_enable_pfault(flic);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
|
||||||
|
|
||||||
|
dc->realize = kvm_s390_flic_realize;
|
||||||
|
dc->unrealize = kvm_s390_flic_unrealize;
|
||||||
|
dc->reset = kvm_s390_flic_reset;
|
||||||
|
fsc->register_io_adapter = kvm_s390_register_io_adapter;
|
||||||
|
fsc->io_adapter_map = kvm_s390_io_adapter_map;
|
||||||
|
fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
|
||||||
|
fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo kvm_s390_flic_info = {
|
||||||
|
.name = TYPE_KVM_S390_FLIC,
|
||||||
|
.parent = TYPE_S390_FLIC_COMMON,
|
||||||
|
.instance_size = sizeof(KVMS390FLICState),
|
||||||
|
.class_init = kvm_s390_flic_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void kvm_s390_flic_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&kvm_s390_flic_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(kvm_s390_flic_register_types)
|
@@ -16,6 +16,7 @@
|
|||||||
#include "ioinst.h"
|
#include "ioinst.h"
|
||||||
#include "css.h"
|
#include "css.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "hw/s390x/s390_flic.h"
|
||||||
|
|
||||||
typedef struct CrwContainer {
|
typedef struct CrwContainer {
|
||||||
CRW crw;
|
CRW crw;
|
||||||
@@ -39,6 +40,13 @@ typedef struct CssImage {
|
|||||||
ChpInfo chpids[MAX_CHPID + 1];
|
ChpInfo chpids[MAX_CHPID + 1];
|
||||||
} CssImage;
|
} CssImage;
|
||||||
|
|
||||||
|
typedef struct IoAdapter {
|
||||||
|
uint32_t id;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t isc;
|
||||||
|
QTAILQ_ENTRY(IoAdapter) sibling;
|
||||||
|
} IoAdapter;
|
||||||
|
|
||||||
typedef struct ChannelSubSys {
|
typedef struct ChannelSubSys {
|
||||||
QTAILQ_HEAD(, CrwContainer) pending_crws;
|
QTAILQ_HEAD(, CrwContainer) pending_crws;
|
||||||
bool do_crw_mchk;
|
bool do_crw_mchk;
|
||||||
@@ -49,6 +57,7 @@ typedef struct ChannelSubSys {
|
|||||||
uint64_t chnmon_area;
|
uint64_t chnmon_area;
|
||||||
CssImage *css[MAX_CSSID + 1];
|
CssImage *css[MAX_CSSID + 1];
|
||||||
uint8_t default_cssid;
|
uint8_t default_cssid;
|
||||||
|
QTAILQ_HEAD(, IoAdapter) io_adapters;
|
||||||
} ChannelSubSys;
|
} ChannelSubSys;
|
||||||
|
|
||||||
static ChannelSubSys *channel_subsys;
|
static ChannelSubSys *channel_subsys;
|
||||||
@@ -69,6 +78,46 @@ int css_create_css_image(uint8_t cssid, bool default_image)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
|
||||||
|
bool maskable, uint32_t *id)
|
||||||
|
{
|
||||||
|
IoAdapter *adapter;
|
||||||
|
bool found = false;
|
||||||
|
int ret;
|
||||||
|
S390FLICState *fs = s390_get_flic();
|
||||||
|
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||||
|
|
||||||
|
*id = 0;
|
||||||
|
QTAILQ_FOREACH(adapter, &channel_subsys->io_adapters, sibling) {
|
||||||
|
if ((adapter->type == type) && (adapter->isc == isc)) {
|
||||||
|
*id = adapter->id;
|
||||||
|
found = true;
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (adapter->id >= *id) {
|
||||||
|
*id = adapter->id + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
adapter = g_new0(IoAdapter, 1);
|
||||||
|
ret = fsc->register_io_adapter(fs, *id, isc, swap, maskable);
|
||||||
|
if (ret == 0) {
|
||||||
|
adapter->id = *id;
|
||||||
|
adapter->isc = isc;
|
||||||
|
adapter->type = type;
|
||||||
|
QTAILQ_INSERT_TAIL(&channel_subsys->io_adapters, adapter, sibling);
|
||||||
|
} else {
|
||||||
|
g_free(adapter);
|
||||||
|
fprintf(stderr, "Unexpected error %d when registering adapter %d\n",
|
||||||
|
ret, *id);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t css_build_subchannel_id(SubchDev *sch)
|
uint16_t css_build_subchannel_id(SubchDev *sch)
|
||||||
{
|
{
|
||||||
if (channel_subsys->max_cssid > 0) {
|
if (channel_subsys->max_cssid > 0) {
|
||||||
@@ -1235,6 +1284,7 @@ static void css_init(void)
|
|||||||
channel_subsys->do_crw_mchk = true;
|
channel_subsys->do_crw_mchk = true;
|
||||||
channel_subsys->crws_lost = false;
|
channel_subsys->crws_lost = false;
|
||||||
channel_subsys->chnmon_active = false;
|
channel_subsys->chnmon_active = false;
|
||||||
|
QTAILQ_INIT(&channel_subsys->io_adapters);
|
||||||
}
|
}
|
||||||
machine_init(css_init);
|
machine_init(css_init);
|
||||||
|
|
||||||
|
@@ -98,4 +98,8 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
|
|||||||
int hotplugged, int add);
|
int hotplugged, int add);
|
||||||
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
|
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
|
||||||
void css_adapter_interrupt(uint8_t isc);
|
void css_adapter_interrupt(uint8_t isc);
|
||||||
|
|
||||||
|
#define CSS_IO_ADAPTER_VIRTIO 1
|
||||||
|
int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
|
||||||
|
bool maskable, uint32_t *id);
|
||||||
#endif
|
#endif
|
||||||
|
@@ -21,12 +21,77 @@
|
|||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
#include "hw/virtio/virtio-bus.h"
|
#include "hw/virtio/virtio-bus.h"
|
||||||
|
#include "hw/s390x/adapter.h"
|
||||||
|
#include "hw/s390x/s390_flic.h"
|
||||||
|
|
||||||
#include "ioinst.h"
|
#include "ioinst.h"
|
||||||
#include "css.h"
|
#include "css.h"
|
||||||
#include "virtio-ccw.h"
|
#include "virtio-ccw.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
static QTAILQ_HEAD(, IndAddr) indicator_addresses =
|
||||||
|
QTAILQ_HEAD_INITIALIZER(indicator_addresses);
|
||||||
|
|
||||||
|
static IndAddr *get_indicator(hwaddr ind_addr, int len)
|
||||||
|
{
|
||||||
|
IndAddr *indicator;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(indicator, &indicator_addresses, sibling) {
|
||||||
|
if (indicator->addr == ind_addr) {
|
||||||
|
indicator->refcnt++;
|
||||||
|
return indicator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indicator = g_new0(IndAddr, 1);
|
||||||
|
indicator->addr = ind_addr;
|
||||||
|
indicator->len = len;
|
||||||
|
indicator->refcnt = 1;
|
||||||
|
QTAILQ_INSERT_TAIL(&indicator_addresses, indicator, sibling);
|
||||||
|
return indicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
|
||||||
|
bool do_map)
|
||||||
|
{
|
||||||
|
S390FLICState *fs = s390_get_flic();
|
||||||
|
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||||
|
|
||||||
|
return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_indicator(AdapterInfo *adapter, IndAddr *indicator)
|
||||||
|
{
|
||||||
|
assert(indicator->refcnt > 0);
|
||||||
|
indicator->refcnt--;
|
||||||
|
if (indicator->refcnt > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QTAILQ_REMOVE(&indicator_addresses, indicator, sibling);
|
||||||
|
if (indicator->map) {
|
||||||
|
s390_io_adapter_map(adapter, indicator->map, false);
|
||||||
|
}
|
||||||
|
g_free(indicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int map_indicator(AdapterInfo *adapter, IndAddr *indicator)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (indicator->map) {
|
||||||
|
return 0; /* already mapped is not an error */
|
||||||
|
}
|
||||||
|
indicator->map = indicator->addr;
|
||||||
|
ret = s390_io_adapter_map(adapter, indicator->map, true);
|
||||||
|
if ((ret != 0) && (ret != -ENOSYS)) {
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_err:
|
||||||
|
indicator->map = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
|
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
|
||||||
VirtioCcwDevice *dev);
|
VirtioCcwDevice *dev);
|
||||||
|
|
||||||
@@ -445,7 +510,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
|||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
} else {
|
} else {
|
||||||
indicators = ldq_phys(&address_space_memory, ccw.cda);
|
indicators = ldq_phys(&address_space_memory, ccw.cda);
|
||||||
dev->indicators = indicators;
|
dev->indicators = get_indicator(indicators, sizeof(uint64_t));
|
||||||
sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
|
sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
@@ -465,7 +530,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
|||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
} else {
|
} else {
|
||||||
indicators = ldq_phys(&address_space_memory, ccw.cda);
|
indicators = ldq_phys(&address_space_memory, ccw.cda);
|
||||||
dev->indicators2 = indicators;
|
dev->indicators2 = get_indicator(indicators, sizeof(uint64_t));
|
||||||
sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
|
sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
@@ -517,13 +582,20 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
|||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
} else {
|
} else {
|
||||||
len = hw_len;
|
len = hw_len;
|
||||||
dev->summary_indicator = thinint->summary_indicator;
|
dev->summary_indicator =
|
||||||
dev->indicators = thinint->device_indicator;
|
get_indicator(thinint->summary_indicator, sizeof(uint8_t));
|
||||||
|
dev->indicators = get_indicator(thinint->device_indicator,
|
||||||
|
thinint->ind_bit / 8 + 1);
|
||||||
dev->thinint_isc = thinint->isc;
|
dev->thinint_isc = thinint->isc;
|
||||||
dev->ind_bit = thinint->ind_bit;
|
dev->routes.adapter.ind_offset = thinint->ind_bit;
|
||||||
|
dev->routes.adapter.summary_offset = 7;
|
||||||
cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len);
|
cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len);
|
||||||
sch->thinint_active = ((dev->indicators != 0) &&
|
ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO,
|
||||||
(dev->summary_indicator != 0));
|
dev->thinint_isc, true, false,
|
||||||
|
&dev->routes.adapter.adapter_id);
|
||||||
|
assert(ret == 0);
|
||||||
|
sch->thinint_active = ((dev->indicators != NULL) &&
|
||||||
|
(dev->summary_indicator != NULL));
|
||||||
sch->curr_status.scsw.count = ccw.count - len;
|
sch->curr_status.scsw.count = ccw.count - len;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
@@ -554,7 +626,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
|
|||||||
sch->driver_data = dev;
|
sch->driver_data = dev;
|
||||||
dev->sch = sch;
|
dev->sch = sch;
|
||||||
|
|
||||||
dev->indicators = 0;
|
dev->indicators = NULL;
|
||||||
|
|
||||||
/* Initialize subchannel structure. */
|
/* Initialize subchannel structure. */
|
||||||
sch->channel_prog = 0x0;
|
sch->channel_prog = 0x0;
|
||||||
@@ -693,7 +765,10 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
|
|||||||
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
|
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
|
||||||
g_free(sch);
|
g_free(sch);
|
||||||
}
|
}
|
||||||
dev->indicators = 0;
|
if (dev->indicators) {
|
||||||
|
release_indicator(&dev->routes.adapter, dev->indicators);
|
||||||
|
dev->indicators = NULL;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,17 +1025,19 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
|
|||||||
* ind_bit indicates the start of the indicators in a big
|
* ind_bit indicates the start of the indicators in a big
|
||||||
* endian notation.
|
* endian notation.
|
||||||
*/
|
*/
|
||||||
virtio_set_ind_atomic(sch, dev->indicators +
|
uint64_t ind_bit = dev->routes.adapter.ind_offset;
|
||||||
(dev->ind_bit + vector) / 8,
|
|
||||||
0x80 >> ((dev->ind_bit + vector) % 8));
|
virtio_set_ind_atomic(sch, dev->indicators->addr +
|
||||||
if (!virtio_set_ind_atomic(sch, dev->summary_indicator,
|
(ind_bit + vector) / 8,
|
||||||
|
0x80 >> ((ind_bit + vector) % 8));
|
||||||
|
if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr,
|
||||||
0x01)) {
|
0x01)) {
|
||||||
css_adapter_interrupt(dev->thinint_isc);
|
css_adapter_interrupt(dev->thinint_isc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
indicators = ldq_phys(&address_space_memory, dev->indicators);
|
indicators = ldq_phys(&address_space_memory, dev->indicators->addr);
|
||||||
indicators |= 1ULL << vector;
|
indicators |= 1ULL << vector;
|
||||||
stq_phys(&address_space_memory, dev->indicators, indicators);
|
stq_phys(&address_space_memory, dev->indicators->addr, indicators);
|
||||||
css_conditional_io_interrupt(sch);
|
css_conditional_io_interrupt(sch);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -968,9 +1045,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
vector = 0;
|
vector = 0;
|
||||||
indicators = ldq_phys(&address_space_memory, dev->indicators2);
|
indicators = ldq_phys(&address_space_memory, dev->indicators2->addr);
|
||||||
indicators |= 1ULL << vector;
|
indicators |= 1ULL << vector;
|
||||||
stq_phys(&address_space_memory, dev->indicators2, indicators);
|
stq_phys(&address_space_memory, dev->indicators2->addr, indicators);
|
||||||
css_conditional_io_interrupt(sch);
|
css_conditional_io_interrupt(sch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -991,9 +1068,18 @@ static void virtio_ccw_reset(DeviceState *d)
|
|||||||
virtio_ccw_stop_ioeventfd(dev);
|
virtio_ccw_stop_ioeventfd(dev);
|
||||||
virtio_reset(vdev);
|
virtio_reset(vdev);
|
||||||
css_reset_sch(dev->sch);
|
css_reset_sch(dev->sch);
|
||||||
dev->indicators = 0;
|
if (dev->indicators) {
|
||||||
dev->indicators2 = 0;
|
release_indicator(&dev->routes.adapter, dev->indicators);
|
||||||
dev->summary_indicator = 0;
|
dev->indicators = NULL;
|
||||||
|
}
|
||||||
|
if (dev->indicators2) {
|
||||||
|
release_indicator(&dev->routes.adapter, dev->indicators2);
|
||||||
|
dev->indicators2 = NULL;
|
||||||
|
}
|
||||||
|
if (dev->summary_indicator) {
|
||||||
|
release_indicator(&dev->routes.adapter, dev->summary_indicator);
|
||||||
|
dev->summary_indicator = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
|
static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
|
||||||
@@ -1027,6 +1113,79 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
|
|||||||
return virtio_ccw_set_guest2host_notifier(dev, n, assign, false);
|
return virtio_ccw_set_guest2host_notifier(dev, n, assign, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (!dev->sch->thinint_active) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = map_indicator(&dev->routes.adapter, dev->summary_indicator);
|
||||||
|
if (r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
r = map_indicator(&dev->routes.adapter, dev->indicators);
|
||||||
|
if (r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
dev->routes.adapter.summary_addr = dev->summary_indicator->map;
|
||||||
|
dev->routes.adapter.ind_addr = dev->indicators->map;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
int ret;
|
||||||
|
S390FLICState *fs = s390_get_flic();
|
||||||
|
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||||
|
|
||||||
|
ret = virtio_ccw_get_mappings(dev);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
for (i = 0; i < nvqs; i++) {
|
||||||
|
if (!virtio_queue_get_num(vdev, i)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dev->routes.num_routes = i;
|
||||||
|
return fsc->add_adapter_routes(fs, &dev->routes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs)
|
||||||
|
{
|
||||||
|
S390FLICState *fs = s390_get_flic();
|
||||||
|
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||||
|
|
||||||
|
fsc->release_adapter_routes(fs, &dev->routes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||||
|
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
|
||||||
|
|
||||||
|
return kvm_irqchip_add_irqfd_notifier(kvm_state, notifier, NULL,
|
||||||
|
dev->routes.gsi[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||||
|
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, notifier,
|
||||||
|
dev->routes.gsi[n]);
|
||||||
|
assert(ret == 0);
|
||||||
|
}
|
||||||
|
|
||||||
static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
||||||
bool assign, bool with_irqfd)
|
bool assign, bool with_irqfd)
|
||||||
{
|
{
|
||||||
@@ -1042,11 +1201,17 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
|
virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
|
||||||
/* We do not support irqfd for classic I/O interrupts, because the
|
if (with_irqfd) {
|
||||||
* classic interrupts are intermixed with the subchannel status, that
|
r = virtio_ccw_add_irqfd(dev, n);
|
||||||
* is queried with test subchannel. We want to use vhost, though.
|
if (r) {
|
||||||
* Lets make sure to have vhost running and wire up the irq fd to
|
virtio_queue_set_guest_notifier_fd_handler(vq, false,
|
||||||
* land in qemu (and only the irq fd) in this code.
|
with_irqfd);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We do not support individual masking for channel devices, so we
|
||||||
|
* need to manually trigger any guest masking callbacks here.
|
||||||
*/
|
*/
|
||||||
if (k->guest_notifier_mask) {
|
if (k->guest_notifier_mask) {
|
||||||
k->guest_notifier_mask(vdev, n, false);
|
k->guest_notifier_mask(vdev, n, false);
|
||||||
@@ -1060,6 +1225,9 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
|||||||
if (k->guest_notifier_mask) {
|
if (k->guest_notifier_mask) {
|
||||||
k->guest_notifier_mask(vdev, n, true);
|
k->guest_notifier_mask(vdev, n, true);
|
||||||
}
|
}
|
||||||
|
if (with_irqfd) {
|
||||||
|
virtio_ccw_remove_irqfd(dev, n);
|
||||||
|
}
|
||||||
virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
|
virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
|
||||||
event_notifier_cleanup(notifier);
|
event_notifier_cleanup(notifier);
|
||||||
}
|
}
|
||||||
@@ -1071,24 +1239,39 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
|
|||||||
{
|
{
|
||||||
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
|
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
|
||||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled();
|
||||||
int r, n;
|
int r, n;
|
||||||
|
|
||||||
|
if (with_irqfd && assigned) {
|
||||||
|
/* irq routes need to be set up before assigning irqfds */
|
||||||
|
r = virtio_ccw_setup_irqroutes(dev, nvqs);
|
||||||
|
if (r < 0) {
|
||||||
|
goto irqroute_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (n = 0; n < nvqs; n++) {
|
for (n = 0; n < nvqs; n++) {
|
||||||
if (!virtio_queue_get_num(vdev, n)) {
|
if (!virtio_queue_get_num(vdev, n)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* false -> true, as soon as irqfd works */
|
r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd);
|
||||||
r = virtio_ccw_set_guest_notifier(dev, n, assigned, false);
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
goto assign_error;
|
goto assign_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (with_irqfd && !assigned) {
|
||||||
|
/* release irq routes after irqfds have been released */
|
||||||
|
virtio_ccw_release_irqroutes(dev, nvqs);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
assign_error:
|
assign_error:
|
||||||
while (--n >= 0) {
|
while (--n >= 0) {
|
||||||
virtio_ccw_set_guest_notifier(dev, n, !assigned, false);
|
virtio_ccw_set_guest_notifier(dev, n, !assigned, false);
|
||||||
}
|
}
|
||||||
|
irqroute_error:
|
||||||
|
if (with_irqfd && assigned) {
|
||||||
|
virtio_ccw_release_irqroutes(dev, nvqs);
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
#include <hw/virtio/virtio-balloon.h>
|
#include <hw/virtio/virtio-balloon.h>
|
||||||
#include <hw/virtio/virtio-rng.h>
|
#include <hw/virtio/virtio-rng.h>
|
||||||
#include <hw/virtio/virtio-bus.h>
|
#include <hw/virtio/virtio-bus.h>
|
||||||
|
#include <hw/s390x/s390_flic.h>
|
||||||
|
|
||||||
#define VIRTUAL_CSSID 0xfe
|
#define VIRTUAL_CSSID 0xfe
|
||||||
|
|
||||||
@@ -75,6 +76,14 @@ typedef struct VirtIOCCWDeviceClass {
|
|||||||
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1
|
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1
|
||||||
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)
|
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)
|
||||||
|
|
||||||
|
typedef struct IndAddr {
|
||||||
|
hwaddr addr;
|
||||||
|
uint64_t map;
|
||||||
|
unsigned long refcnt;
|
||||||
|
int len;
|
||||||
|
QTAILQ_ENTRY(IndAddr) sibling;
|
||||||
|
} IndAddr;
|
||||||
|
|
||||||
struct VirtioCcwDevice {
|
struct VirtioCcwDevice {
|
||||||
DeviceState parent_obj;
|
DeviceState parent_obj;
|
||||||
SubchDev *sch;
|
SubchDev *sch;
|
||||||
@@ -85,10 +94,11 @@ struct VirtioCcwDevice {
|
|||||||
bool ioeventfd_disabled;
|
bool ioeventfd_disabled;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint8_t thinint_isc;
|
uint8_t thinint_isc;
|
||||||
|
AdapterRoutes routes;
|
||||||
/* Guest provided values: */
|
/* Guest provided values: */
|
||||||
hwaddr indicators;
|
IndAddr *indicators;
|
||||||
hwaddr indicators2;
|
IndAddr *indicators2;
|
||||||
hwaddr summary_indicator;
|
IndAddr *summary_indicator;
|
||||||
uint64_t ind_bit;
|
uint64_t ind_bit;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "sysemu/dma.h"
|
#include "sysemu/dma.h"
|
||||||
|
#include "hw/pci/msi.h"
|
||||||
#include "hw/pci/msix.h"
|
#include "hw/pci/msix.h"
|
||||||
#include "qemu/iov.h"
|
#include "qemu/iov.h"
|
||||||
#include "hw/scsi/scsi.h"
|
#include "hw/scsi/scsi.h"
|
||||||
@@ -43,9 +44,11 @@
|
|||||||
|
|
||||||
#define MEGASAS_FLAG_USE_JBOD 0
|
#define MEGASAS_FLAG_USE_JBOD 0
|
||||||
#define MEGASAS_MASK_USE_JBOD (1 << MEGASAS_FLAG_USE_JBOD)
|
#define MEGASAS_MASK_USE_JBOD (1 << MEGASAS_FLAG_USE_JBOD)
|
||||||
#define MEGASAS_FLAG_USE_MSIX 1
|
#define MEGASAS_FLAG_USE_MSI 1
|
||||||
|
#define MEGASAS_MASK_USE_MSI (1 << MEGASAS_FLAG_USE_MSI)
|
||||||
|
#define MEGASAS_FLAG_USE_MSIX 2
|
||||||
#define MEGASAS_MASK_USE_MSIX (1 << MEGASAS_FLAG_USE_MSIX)
|
#define MEGASAS_MASK_USE_MSIX (1 << MEGASAS_FLAG_USE_MSIX)
|
||||||
#define MEGASAS_FLAG_USE_QUEUE64 2
|
#define MEGASAS_FLAG_USE_QUEUE64 3
|
||||||
#define MEGASAS_MASK_USE_QUEUE64 (1 << MEGASAS_FLAG_USE_QUEUE64)
|
#define MEGASAS_MASK_USE_QUEUE64 (1 << MEGASAS_FLAG_USE_QUEUE64)
|
||||||
|
|
||||||
static const char *mfi_frame_desc[] = {
|
static const char *mfi_frame_desc[] = {
|
||||||
@@ -132,6 +135,11 @@ static bool megasas_use_queue64(MegasasState *s)
|
|||||||
return s->flags & MEGASAS_MASK_USE_QUEUE64;
|
return s->flags & MEGASAS_MASK_USE_QUEUE64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool megasas_use_msi(MegasasState *s)
|
||||||
|
{
|
||||||
|
return s->flags & MEGASAS_MASK_USE_MSI;
|
||||||
|
}
|
||||||
|
|
||||||
static bool megasas_use_msix(MegasasState *s)
|
static bool megasas_use_msix(MegasasState *s)
|
||||||
{
|
{
|
||||||
return s->flags & MEGASAS_MASK_USE_MSIX;
|
return s->flags & MEGASAS_MASK_USE_MSIX;
|
||||||
@@ -538,6 +546,9 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context)
|
|||||||
if (msix_enabled(pci_dev)) {
|
if (msix_enabled(pci_dev)) {
|
||||||
trace_megasas_msix_raise(0);
|
trace_megasas_msix_raise(0);
|
||||||
msix_notify(pci_dev, 0);
|
msix_notify(pci_dev, 0);
|
||||||
|
} else if (msi_enabled(pci_dev)) {
|
||||||
|
trace_megasas_msi_raise(0);
|
||||||
|
msi_notify(pci_dev, 0);
|
||||||
} else {
|
} else {
|
||||||
trace_megasas_irq_raise();
|
trace_megasas_irq_raise();
|
||||||
pci_irq_assert(pci_dev);
|
pci_irq_assert(pci_dev);
|
||||||
@@ -717,8 +728,8 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
|
|||||||
snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
|
snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
|
||||||
memcpy(info.image_component[0].name, "APP", 3);
|
memcpy(info.image_component[0].name, "APP", 3);
|
||||||
memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9);
|
memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9);
|
||||||
memcpy(info.image_component[0].build_date, __DATE__, 11);
|
memcpy(info.image_component[0].build_date, "Apr 1 2014", 11);
|
||||||
memcpy(info.image_component[0].build_time, __TIME__, 8);
|
memcpy(info.image_component[0].build_time, "12:34:56", 8);
|
||||||
info.image_component_count = 1;
|
info.image_component_count = 1;
|
||||||
if (pci_dev->has_rom) {
|
if (pci_dev->has_rom) {
|
||||||
uint8_t biosver[32];
|
uint8_t biosver[32];
|
||||||
@@ -1106,6 +1117,21 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
|
|||||||
return MFI_STAT_OK;
|
return MFI_STAT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd)
|
||||||
|
{
|
||||||
|
uint16_t flags;
|
||||||
|
|
||||||
|
/* mbox0 contains flags */
|
||||||
|
flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
|
||||||
|
trace_megasas_dcmd_ld_list_query(cmd->index, flags);
|
||||||
|
if (flags == MR_LD_QUERY_TYPE_ALL ||
|
||||||
|
flags == MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) {
|
||||||
|
return megasas_dcmd_ld_get_list(s, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MFI_STAT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
|
static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
|
||||||
MegasasCmd *cmd)
|
MegasasCmd *cmd)
|
||||||
{
|
{
|
||||||
@@ -1409,6 +1435,8 @@ static const struct dcmd_cmd_tbl_t {
|
|||||||
megasas_dcmd_dummy },
|
megasas_dcmd_dummy },
|
||||||
{ MFI_DCMD_LD_GET_LIST, "LD_GET_LIST",
|
{ MFI_DCMD_LD_GET_LIST, "LD_GET_LIST",
|
||||||
megasas_dcmd_ld_get_list},
|
megasas_dcmd_ld_get_list},
|
||||||
|
{ MFI_DCMD_LD_LIST_QUERY, "LD_LIST_QUERY",
|
||||||
|
megasas_dcmd_ld_list_query },
|
||||||
{ MFI_DCMD_LD_GET_INFO, "LD_GET_INFO",
|
{ MFI_DCMD_LD_GET_INFO, "LD_GET_INFO",
|
||||||
megasas_dcmd_ld_get_info },
|
megasas_dcmd_ld_get_info },
|
||||||
{ MFI_DCMD_LD_GET_PROP, "LD_GET_PROP",
|
{ MFI_DCMD_LD_GET_PROP, "LD_GET_PROP",
|
||||||
@@ -1939,12 +1967,20 @@ static void megasas_mmio_write(void *opaque, hwaddr addr,
|
|||||||
break;
|
break;
|
||||||
case MFI_OMSK:
|
case MFI_OMSK:
|
||||||
s->intr_mask = val;
|
s->intr_mask = val;
|
||||||
if (!megasas_intr_enabled(s) && !msix_enabled(pci_dev)) {
|
if (!megasas_intr_enabled(s) &&
|
||||||
|
!msi_enabled(pci_dev) &&
|
||||||
|
!msix_enabled(pci_dev)) {
|
||||||
trace_megasas_irq_lower();
|
trace_megasas_irq_lower();
|
||||||
pci_irq_deassert(pci_dev);
|
pci_irq_deassert(pci_dev);
|
||||||
}
|
}
|
||||||
if (megasas_intr_enabled(s)) {
|
if (megasas_intr_enabled(s)) {
|
||||||
|
if (msix_enabled(pci_dev)) {
|
||||||
|
trace_megasas_msix_enabled(0);
|
||||||
|
} else if (msi_enabled(pci_dev)) {
|
||||||
|
trace_megasas_msi_enabled(0);
|
||||||
|
} else {
|
||||||
trace_megasas_intr_enabled();
|
trace_megasas_intr_enabled();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
trace_megasas_intr_disabled();
|
trace_megasas_intr_disabled();
|
||||||
}
|
}
|
||||||
@@ -2068,6 +2104,7 @@ static const VMStateDescription vmstate_megasas = {
|
|||||||
.minimum_version_id_old = 0,
|
.minimum_version_id_old = 0,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_PCI_DEVICE(parent_obj, MegasasState),
|
VMSTATE_PCI_DEVICE(parent_obj, MegasasState),
|
||||||
|
VMSTATE_MSIX(parent_obj, MegasasState),
|
||||||
|
|
||||||
VMSTATE_INT32(fw_state, MegasasState),
|
VMSTATE_INT32(fw_state, MegasasState),
|
||||||
VMSTATE_INT32(intr_mask, MegasasState),
|
VMSTATE_INT32(intr_mask, MegasasState),
|
||||||
@@ -2083,9 +2120,12 @@ static void megasas_scsi_uninit(PCIDevice *d)
|
|||||||
{
|
{
|
||||||
MegasasState *s = MEGASAS(d);
|
MegasasState *s = MEGASAS(d);
|
||||||
|
|
||||||
#ifdef USE_MSIX
|
if (megasas_use_msix(s)) {
|
||||||
msix_uninit(d, &s->mmio_io);
|
msix_uninit(d, &s->mmio_io, &s->mmio_io);
|
||||||
#endif
|
}
|
||||||
|
if (megasas_use_msi(s)) {
|
||||||
|
msi_uninit(d);
|
||||||
|
}
|
||||||
memory_region_destroy(&s->mmio_io);
|
memory_region_destroy(&s->mmio_io);
|
||||||
memory_region_destroy(&s->port_io);
|
memory_region_destroy(&s->port_io);
|
||||||
memory_region_destroy(&s->queue_io);
|
memory_region_destroy(&s->queue_io);
|
||||||
@@ -2124,15 +2164,15 @@ static int megasas_scsi_init(PCIDevice *dev)
|
|||||||
memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s,
|
memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s,
|
||||||
"megasas-queue", 0x40000);
|
"megasas-queue", 0x40000);
|
||||||
|
|
||||||
#ifdef USE_MSIX
|
if (megasas_use_msi(s) &&
|
||||||
/* MSI-X support is currently broken */
|
msi_init(dev, 0x50, 1, true, false)) {
|
||||||
|
s->flags &= ~MEGASAS_MASK_USE_MSI;
|
||||||
|
}
|
||||||
if (megasas_use_msix(s) &&
|
if (megasas_use_msix(s) &&
|
||||||
msix_init(dev, 15, &s->mmio_io, 0, 0x2000)) {
|
msix_init(dev, 15, &s->mmio_io, 0, 0x2000,
|
||||||
|
&s->mmio_io, 0, 0x3800, 0x68)) {
|
||||||
s->flags &= ~MEGASAS_MASK_USE_MSIX;
|
s->flags &= ~MEGASAS_MASK_USE_MSIX;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
s->flags &= ~MEGASAS_MASK_USE_MSIX;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64;
|
bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64;
|
||||||
pci_register_bar(dev, 0, bar_type, &s->mmio_io);
|
pci_register_bar(dev, 0, bar_type, &s->mmio_io);
|
||||||
@@ -2164,7 +2204,6 @@ static int megasas_scsi_init(PCIDevice *dev)
|
|||||||
s->fw_cmds = MEGASAS_MAX_FRAMES;
|
s->fw_cmds = MEGASAS_MAX_FRAMES;
|
||||||
}
|
}
|
||||||
trace_megasas_init(s->fw_sge, s->fw_cmds,
|
trace_megasas_init(s->fw_sge, s->fw_cmds,
|
||||||
megasas_use_msix(s) ? "MSI-X" : "INTx",
|
|
||||||
megasas_is_jbod(s) ? "jbod" : "raid");
|
megasas_is_jbod(s) ? "jbod" : "raid");
|
||||||
s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ?
|
s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ?
|
||||||
MAX_SCSI_DEVS : MFI_MAX_LD;
|
MAX_SCSI_DEVS : MFI_MAX_LD;
|
||||||
@@ -2189,6 +2228,13 @@ static int megasas_scsi_init(PCIDevice *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
megasas_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len)
|
||||||
|
{
|
||||||
|
pci_default_write_config(pci, addr, val, len);
|
||||||
|
msi_write_config(pci, addr, val, len);
|
||||||
|
}
|
||||||
|
|
||||||
static Property megasas_properties[] = {
|
static Property megasas_properties[] = {
|
||||||
DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge,
|
DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge,
|
||||||
MEGASAS_DEFAULT_SGE),
|
MEGASAS_DEFAULT_SGE),
|
||||||
@@ -2196,10 +2242,10 @@ static Property megasas_properties[] = {
|
|||||||
MEGASAS_DEFAULT_FRAMES),
|
MEGASAS_DEFAULT_FRAMES),
|
||||||
DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial),
|
DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial),
|
||||||
DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0),
|
DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0),
|
||||||
#ifdef USE_MSIX
|
DEFINE_PROP_BIT("use_msi", MegasasState, flags,
|
||||||
|
MEGASAS_FLAG_USE_MSI, false),
|
||||||
DEFINE_PROP_BIT("use_msix", MegasasState, flags,
|
DEFINE_PROP_BIT("use_msix", MegasasState, flags,
|
||||||
MEGASAS_FLAG_USE_MSIX, false),
|
MEGASAS_FLAG_USE_MSIX, false),
|
||||||
#endif
|
|
||||||
DEFINE_PROP_BIT("use_jbod", MegasasState, flags,
|
DEFINE_PROP_BIT("use_jbod", MegasasState, flags,
|
||||||
MEGASAS_FLAG_USE_JBOD, false),
|
MEGASAS_FLAG_USE_JBOD, false),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
@@ -2222,6 +2268,7 @@ static void megasas_class_init(ObjectClass *oc, void *data)
|
|||||||
dc->vmsd = &vmstate_megasas;
|
dc->vmsd = &vmstate_megasas;
|
||||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||||
dc->desc = "LSI MegaRAID SAS 1078";
|
dc->desc = "LSI MegaRAID SAS 1078";
|
||||||
|
pc->config_write = megasas_write_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo megasas_info = {
|
static const TypeInfo megasas_info = {
|
||||||
|
@@ -164,6 +164,7 @@ typedef enum {
|
|||||||
MFI_DCMD_PD_BLINK = 0x02070100,
|
MFI_DCMD_PD_BLINK = 0x02070100,
|
||||||
MFI_DCMD_PD_UNBLINK = 0x02070200,
|
MFI_DCMD_PD_UNBLINK = 0x02070200,
|
||||||
MFI_DCMD_LD_GET_LIST = 0x03010000,
|
MFI_DCMD_LD_GET_LIST = 0x03010000,
|
||||||
|
MFI_DCMD_LD_LIST_QUERY = 0x03010100,
|
||||||
MFI_DCMD_LD_GET_INFO = 0x03020000,
|
MFI_DCMD_LD_GET_INFO = 0x03020000,
|
||||||
MFI_DCMD_LD_GET_PROP = 0x03030000,
|
MFI_DCMD_LD_GET_PROP = 0x03030000,
|
||||||
MFI_DCMD_LD_SET_PROP = 0x03040000,
|
MFI_DCMD_LD_SET_PROP = 0x03040000,
|
||||||
@@ -411,6 +412,14 @@ typedef enum {
|
|||||||
MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5, /*query for system drives */
|
MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5, /*query for system drives */
|
||||||
} mfi_pd_query_type;
|
} mfi_pd_query_type;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MR_LD_QUERY_TYPE_ALL = 0,
|
||||||
|
MR_LD_QUERY_TYPE_EXPOSED_TO_HOST = 1,
|
||||||
|
MR_LD_QUERY_TYPE_USED_TGT_IDS = 2,
|
||||||
|
MR_LD_QUERY_TYPE_CLUSTER_ACCESS = 3,
|
||||||
|
MR_LD_QUERY_TYPE_CLUSTER_LOCALE = 4,
|
||||||
|
} mfi_ld_query_type;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Other propertities and definitions
|
* Other propertities and definitions
|
||||||
*/
|
*/
|
||||||
|
@@ -938,6 +938,7 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
|||||||
if (cmd->xfer == 0) {
|
if (cmd->xfer == 0) {
|
||||||
cmd->xfer = 256;
|
cmd->xfer = 256;
|
||||||
}
|
}
|
||||||
|
/* fall through */
|
||||||
case WRITE_10:
|
case WRITE_10:
|
||||||
case WRITE_VERIFY_10:
|
case WRITE_VERIFY_10:
|
||||||
case WRITE_12:
|
case WRITE_12:
|
||||||
@@ -952,6 +953,7 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
|||||||
if (cmd->xfer == 0) {
|
if (cmd->xfer == 0) {
|
||||||
cmd->xfer = 256;
|
cmd->xfer = 256;
|
||||||
}
|
}
|
||||||
|
/* fall through */
|
||||||
case READ_10:
|
case READ_10:
|
||||||
case RECOVER_BUFFERED_DATA:
|
case RECOVER_BUFFERED_DATA:
|
||||||
case READ_12:
|
case READ_12:
|
||||||
|
@@ -2458,21 +2458,27 @@ static int scsi_block_initfn(SCSIDevice *dev)
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!s->qdev.conf.bs) {
|
if (!s->qdev.conf.bs) {
|
||||||
error_report("scsi-block: drive property not set");
|
error_report("drive property not set");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check we are using a driver managing SG_IO (version 3 and after) */
|
/* check we are using a driver managing SG_IO (version 3 and after) */
|
||||||
if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
|
rc = bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version);
|
||||||
sg_version < 30000) {
|
if (rc < 0) {
|
||||||
error_report("scsi-block: scsi generic interface too old");
|
error_report("cannot get SG_IO version number: %s. "
|
||||||
|
"Is this a SCSI device?",
|
||||||
|
strerror(-rc));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (sg_version < 30000) {
|
||||||
|
error_report("scsi generic interface too old");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get device type from INQUIRY data */
|
/* get device type from INQUIRY data */
|
||||||
rc = get_device_type(s);
|
rc = get_device_type(s);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
error_report("scsi-block: INQUIRY failed");
|
error_report("INQUIRY failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -394,6 +394,7 @@ static void scsi_destroy(SCSIDevice *s)
|
|||||||
|
|
||||||
static int scsi_generic_initfn(SCSIDevice *s)
|
static int scsi_generic_initfn(SCSIDevice *s)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
int sg_version;
|
int sg_version;
|
||||||
struct sg_scsi_id scsiid;
|
struct sg_scsi_id scsiid;
|
||||||
|
|
||||||
@@ -412,8 +413,11 @@ static int scsi_generic_initfn(SCSIDevice *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* check we are using a driver managing SG_IO (version 3 and after */
|
/* check we are using a driver managing SG_IO (version 3 and after */
|
||||||
if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0) {
|
rc = bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version);
|
||||||
error_report("scsi generic interface not supported");
|
if (rc < 0) {
|
||||||
|
error_report("cannot get SG_IO version number: %s. "
|
||||||
|
"Is this a SCSI device?",
|
||||||
|
strerror(-rc));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (sg_version < 30000) {
|
if (sg_version < 30000) {
|
||||||
|
@@ -498,7 +498,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
|||||||
uint32_t event, uint32_t reason)
|
uint32_t event, uint32_t reason)
|
||||||
{
|
{
|
||||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||||
VirtIOSCSIReq *req = virtio_scsi_pop_req(s, vs->event_vq);
|
VirtIOSCSIReq *req;
|
||||||
VirtIOSCSIEvent *evt;
|
VirtIOSCSIEvent *evt;
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||||
int in_size;
|
int in_size;
|
||||||
@@ -507,6 +507,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req = virtio_scsi_pop_req(s, vs->event_vq);
|
||||||
if (!req) {
|
if (!req) {
|
||||||
s->events_dropped = true;
|
s->events_dropped = true;
|
||||||
return;
|
return;
|
||||||
|
@@ -793,19 +793,46 @@ static const MemoryRegionOps cmos_ops = {
|
|||||||
static void rtc_get_date(Object *obj, Visitor *v, void *opaque,
|
static void rtc_get_date(Object *obj, Visitor *v, void *opaque,
|
||||||
const char *name, Error **errp)
|
const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *err = NULL;
|
||||||
RTCState *s = MC146818_RTC(obj);
|
RTCState *s = MC146818_RTC(obj);
|
||||||
struct tm current_tm;
|
struct tm current_tm;
|
||||||
|
|
||||||
rtc_update_time(s);
|
rtc_update_time(s);
|
||||||
rtc_get_time(s, ¤t_tm);
|
rtc_get_time(s, ¤t_tm);
|
||||||
visit_start_struct(v, NULL, "struct tm", name, 0, errp);
|
visit_start_struct(v, NULL, "struct tm", name, 0, &err);
|
||||||
visit_type_int32(v, ¤t_tm.tm_year, "tm_year", errp);
|
if (err) {
|
||||||
visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", errp);
|
goto out;
|
||||||
visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", errp);
|
}
|
||||||
visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", errp);
|
visit_type_int32(v, ¤t_tm.tm_year, "tm_year", &err);
|
||||||
visit_type_int32(v, ¤t_tm.tm_min, "tm_min", errp);
|
if (err) {
|
||||||
visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", errp);
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_int32(v, ¤t_tm.tm_min, "tm_min", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
out_end:
|
||||||
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
visit_end_struct(v, errp);
|
visit_end_struct(v, errp);
|
||||||
|
out:
|
||||||
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rtc_realizefn(DeviceState *dev, Error **errp)
|
static void rtc_realizefn(DeviceState *dev, Error **errp)
|
||||||
|
@@ -47,6 +47,8 @@ typedef struct USBHIDState {
|
|||||||
USBEndpoint *intr;
|
USBEndpoint *intr;
|
||||||
HIDState hid;
|
HIDState hid;
|
||||||
uint32_t usb_version;
|
uint32_t usb_version;
|
||||||
|
char *display;
|
||||||
|
uint32_t head;
|
||||||
} USBHIDState;
|
} USBHIDState;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -574,6 +576,9 @@ static int usb_hid_initfn(USBDevice *dev, int kind)
|
|||||||
usb_desc_init(dev);
|
usb_desc_init(dev);
|
||||||
us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
||||||
hid_init(&us->hid, kind, usb_hid_changed);
|
hid_init(&us->hid, kind, usb_hid_changed);
|
||||||
|
if (us->display && us->hid.s) {
|
||||||
|
qemu_input_handler_bind(us->hid.s, us->display, us->head, NULL);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,6 +658,8 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data)
|
|||||||
|
|
||||||
static Property usb_tablet_properties[] = {
|
static Property usb_tablet_properties[] = {
|
||||||
DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
|
DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
|
||||||
|
DEFINE_PROP_STRING("display", USBHIDState, display),
|
||||||
|
DEFINE_PROP_UINT32("head", USBHIDState, head, 0),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -696,6 +703,11 @@ static const TypeInfo usb_mouse_info = {
|
|||||||
.class_init = usb_mouse_class_initfn,
|
.class_init = usb_mouse_class_initfn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Property usb_keyboard_properties[] = {
|
||||||
|
DEFINE_PROP_STRING("display", USBHIDState, display),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
|
static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
@@ -706,6 +718,7 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
|
|||||||
uc->product_desc = "QEMU USB Keyboard";
|
uc->product_desc = "QEMU USB Keyboard";
|
||||||
uc->usb_desc = &desc_keyboard;
|
uc->usb_desc = &desc_keyboard;
|
||||||
dc->vmsd = &vmstate_usb_kbd;
|
dc->vmsd = &vmstate_usb_kbd;
|
||||||
|
dc->props = usb_keyboard_properties;
|
||||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -108,6 +108,7 @@ static void balloon_stats_poll_cb(void *opaque)
|
|||||||
static void balloon_stats_get_all(Object *obj, struct Visitor *v,
|
static void balloon_stats_get_all(Object *obj, struct Visitor *v,
|
||||||
void *opaque, const char *name, Error **errp)
|
void *opaque, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *err = NULL;
|
||||||
VirtIOBalloon *s = opaque;
|
VirtIOBalloon *s = opaque;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -116,17 +117,33 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_start_struct(v, NULL, "guest-stats", name, 0, errp);
|
visit_start_struct(v, NULL, "guest-stats", name, 0, &err);
|
||||||
visit_type_int(v, &s->stats_last_update, "last-update", errp);
|
if (err) {
|
||||||
|
goto out;
|
||||||
visit_start_struct(v, NULL, NULL, "stats", 0, errp);
|
}
|
||||||
for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
|
visit_type_int(v, &s->stats_last_update, "last-update", &err);
|
||||||
visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
|
if (err) {
|
||||||
errp);
|
goto out_end;
|
||||||
}
|
}
|
||||||
visit_end_struct(v, errp);
|
|
||||||
|
|
||||||
visit_end_struct(v, errp);
|
visit_start_struct(v, NULL, NULL, "stats", 0, &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) {
|
||||||
|
visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
|
||||||
|
&err);
|
||||||
|
}
|
||||||
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
|
visit_end_struct(v, &err);
|
||||||
|
|
||||||
|
out_end:
|
||||||
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
|
visit_end_struct(v, &err);
|
||||||
|
out:
|
||||||
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v,
|
static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v,
|
||||||
|
@@ -120,6 +120,8 @@ typedef enum {
|
|||||||
/* BDRV_BLOCK_DATA: data is read from bs->file or another file
|
/* BDRV_BLOCK_DATA: data is read from bs->file or another file
|
||||||
* BDRV_BLOCK_ZERO: sectors read as zero
|
* BDRV_BLOCK_ZERO: sectors read as zero
|
||||||
* BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data
|
* BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data
|
||||||
|
* BDRV_BLOCK_ALLOCATED: the content of the block is determined by this
|
||||||
|
* layer (as opposed to the backing file)
|
||||||
* BDRV_BLOCK_RAW: used internally to indicate that the request
|
* BDRV_BLOCK_RAW: used internally to indicate that the request
|
||||||
* was answered by the raw driver and that one
|
* was answered by the raw driver and that one
|
||||||
* should look in bs->file directly.
|
* should look in bs->file directly.
|
||||||
@@ -141,10 +143,11 @@ typedef enum {
|
|||||||
* f t f not allocated or unknown offset, read as zero
|
* f t f not allocated or unknown offset, read as zero
|
||||||
* f f f not allocated or unknown offset, read from backing_hd
|
* f f f not allocated or unknown offset, read from backing_hd
|
||||||
*/
|
*/
|
||||||
#define BDRV_BLOCK_DATA 1
|
#define BDRV_BLOCK_DATA 0x01
|
||||||
#define BDRV_BLOCK_ZERO 2
|
#define BDRV_BLOCK_ZERO 0x02
|
||||||
#define BDRV_BLOCK_OFFSET_VALID 4
|
#define BDRV_BLOCK_OFFSET_VALID 0x04
|
||||||
#define BDRV_BLOCK_RAW 8
|
#define BDRV_BLOCK_RAW 0x08
|
||||||
|
#define BDRV_BLOCK_ALLOCATED 0x10
|
||||||
#define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK
|
#define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@@ -364,6 +364,7 @@ struct BlockDriverState {
|
|||||||
BlockJob *job;
|
BlockJob *job;
|
||||||
|
|
||||||
QDict *options;
|
QDict *options;
|
||||||
|
BlockdevDetectZeroesOptions detect_zeroes;
|
||||||
};
|
};
|
||||||
|
|
||||||
int get_tmp_filename(char *filename, int size);
|
int get_tmp_filename(char *filename, int size);
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
#define QEMU_HID_H
|
#define QEMU_HID_H
|
||||||
|
|
||||||
#include "migration/vmstate.h"
|
#include "migration/vmstate.h"
|
||||||
|
#include "ui/input.h"
|
||||||
|
|
||||||
#define HID_MOUSE 1
|
#define HID_MOUSE 1
|
||||||
#define HID_TABLET 2
|
#define HID_TABLET 2
|
||||||
@@ -22,7 +23,6 @@ typedef void (*HIDEventFunc)(HIDState *s);
|
|||||||
typedef struct HIDMouseState {
|
typedef struct HIDMouseState {
|
||||||
HIDPointerEvent queue[QUEUE_LENGTH];
|
HIDPointerEvent queue[QUEUE_LENGTH];
|
||||||
int mouse_grabbed;
|
int mouse_grabbed;
|
||||||
QEMUPutMouseEntry *eh_entry;
|
|
||||||
} HIDMouseState;
|
} HIDMouseState;
|
||||||
|
|
||||||
typedef struct HIDKeyboardState {
|
typedef struct HIDKeyboardState {
|
||||||
@@ -31,7 +31,6 @@ typedef struct HIDKeyboardState {
|
|||||||
uint8_t leds;
|
uint8_t leds;
|
||||||
uint8_t key[16];
|
uint8_t key[16];
|
||||||
int32_t keys;
|
int32_t keys;
|
||||||
QEMUPutKbdEntry *eh_entry;
|
|
||||||
} HIDKeyboardState;
|
} HIDKeyboardState;
|
||||||
|
|
||||||
struct HIDState {
|
struct HIDState {
|
||||||
@@ -47,6 +46,7 @@ struct HIDState {
|
|||||||
bool idle_pending;
|
bool idle_pending;
|
||||||
QEMUTimer *idle_timer;
|
QEMUTimer *idle_timer;
|
||||||
HIDEventFunc event;
|
HIDEventFunc event;
|
||||||
|
QemuInputHandlerState *s;
|
||||||
};
|
};
|
||||||
|
|
||||||
void hid_init(HIDState *hs, int kind, HIDEventFunc event);
|
void hid_init(HIDState *hs, int kind, HIDEventFunc event);
|
||||||
|
23
include/hw/s390x/adapter.h
Normal file
23
include/hw/s390x/adapter.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* s390 adapter definitions
|
||||||
|
*
|
||||||
|
* Copyright 2013,2014 IBM Corp.
|
||||||
|
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||||
|
* your option) any later version. See the COPYING file in the top-level
|
||||||
|
* directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef S390X_ADAPTER_H
|
||||||
|
#define S390X_ADAPTER_H
|
||||||
|
|
||||||
|
struct AdapterInfo {
|
||||||
|
uint64_t ind_addr;
|
||||||
|
uint64_t summary_addr;
|
||||||
|
uint64_t ind_offset;
|
||||||
|
uint32_t summary_offset;
|
||||||
|
uint32_t adapter_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@@ -1,33 +1,76 @@
|
|||||||
/*
|
/*
|
||||||
* QEMU S390x KVM floating interrupt controller (flic)
|
* QEMU S390x floating interrupt controller (flic)
|
||||||
*
|
*
|
||||||
* Copyright 2014 IBM Corp.
|
* Copyright 2014 IBM Corp.
|
||||||
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
||||||
|
* Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||||
*
|
*
|
||||||
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||||
* your option) any later version. See the COPYING file in the top-level
|
* your option) any later version. See the COPYING file in the top-level
|
||||||
* directory.
|
* directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __KVM_S390_FLIC_H
|
#ifndef __HW_S390_FLIC_H
|
||||||
#define __KVM_S390_FLIC_H
|
#define __HW_S390_FLIC_H
|
||||||
|
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
|
#include "hw/s390x/adapter.h"
|
||||||
|
#include "hw/virtio/virtio.h"
|
||||||
|
|
||||||
#define TYPE_KVM_S390_FLIC "s390-flic"
|
typedef struct AdapterRoutes {
|
||||||
|
AdapterInfo adapter;
|
||||||
|
int num_routes;
|
||||||
|
int gsi[VIRTIO_PCI_QUEUE_MAX];
|
||||||
|
} AdapterRoutes;
|
||||||
|
|
||||||
|
#define TYPE_S390_FLIC_COMMON "s390-flic"
|
||||||
|
#define S390_FLIC_COMMON(obj) \
|
||||||
|
OBJECT_CHECK(S390FLICState, (obj), TYPE_S390_FLIC_COMMON)
|
||||||
|
|
||||||
|
typedef struct S390FLICState {
|
||||||
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
|
} S390FLICState;
|
||||||
|
|
||||||
|
#define S390_FLIC_COMMON_CLASS(klass) \
|
||||||
|
OBJECT_CLASS_CHECK(S390FLICStateClass, (klass), TYPE_S390_FLIC_COMMON)
|
||||||
|
#define S390_FLIC_COMMON_GET_CLASS(obj) \
|
||||||
|
OBJECT_GET_CLASS(S390FLICStateClass, (obj), TYPE_S390_FLIC_COMMON)
|
||||||
|
|
||||||
|
typedef struct S390FLICStateClass {
|
||||||
|
DeviceClass parent_class;
|
||||||
|
|
||||||
|
int (*register_io_adapter)(S390FLICState *fs, uint32_t id, uint8_t isc,
|
||||||
|
bool swap, bool maskable);
|
||||||
|
int (*io_adapter_map)(S390FLICState *fs, uint32_t id, uint64_t map_addr,
|
||||||
|
bool do_map);
|
||||||
|
int (*add_adapter_routes)(S390FLICState *fs, AdapterRoutes *routes);
|
||||||
|
void (*release_adapter_routes)(S390FLICState *fs, AdapterRoutes *routes);
|
||||||
|
} S390FLICStateClass;
|
||||||
|
|
||||||
|
#define TYPE_KVM_S390_FLIC "s390-flic-kvm"
|
||||||
#define KVM_S390_FLIC(obj) \
|
#define KVM_S390_FLIC(obj) \
|
||||||
OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC)
|
OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC)
|
||||||
|
|
||||||
typedef struct KVMS390FLICState {
|
#define TYPE_QEMU_S390_FLIC "s390-flic-qemu"
|
||||||
SysBusDevice parent_obj;
|
#define QEMU_S390_FLIC(obj) \
|
||||||
|
OBJECT_CHECK(QEMUS390FLICState, (obj), TYPE_QEMU_S390_FLIC)
|
||||||
|
|
||||||
uint32_t fd;
|
typedef struct QEMUS390FLICState {
|
||||||
} KVMS390FLICState;
|
S390FLICState parent_obj;
|
||||||
|
} QEMUS390FLICState;
|
||||||
|
|
||||||
|
void s390_flic_init(void);
|
||||||
|
|
||||||
|
S390FLICState *s390_get_flic(void);
|
||||||
|
|
||||||
#ifdef CONFIG_KVM
|
#ifdef CONFIG_KVM
|
||||||
void s390_flic_init(void);
|
DeviceState *s390_flic_kvm_create(void);
|
||||||
#else
|
#else
|
||||||
static inline void s390_flic_init(void) { }
|
static inline DeviceState *s390_flic_kvm_create(void)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __KVM_S390_FLIC_H */
|
#endif /* __HW_S390_FLIC_H */
|
||||||
|
@@ -67,12 +67,6 @@ void error_set_win32(Error **errp, int win32_err, ErrorClass err_class,
|
|||||||
*/
|
*/
|
||||||
void error_setg_file_open(Error **errp, int os_errno, const char *filename);
|
void error_setg_file_open(Error **errp, int os_errno, const char *filename);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if an indirect pointer to an error is pointing to a valid
|
|
||||||
* error object.
|
|
||||||
*/
|
|
||||||
bool error_is_set(Error **errp);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the error class of an error object.
|
* Get the error class of an error object.
|
||||||
*/
|
*/
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
#include "qapi/qmp/qobject.h"
|
#include "qapi/qmp/qobject.h"
|
||||||
#include "qapi/qmp/qlist.h"
|
#include "qapi/qmp/qlist.h"
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define QDICT_BUCKET_MAX 512
|
#define QDICT_BUCKET_MAX 512
|
||||||
@@ -70,4 +71,6 @@ void qdict_flatten(QDict *qdict);
|
|||||||
void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
|
void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
|
||||||
void qdict_array_split(QDict *src, QList **dst);
|
void qdict_array_split(QDict *src, QList **dst);
|
||||||
|
|
||||||
|
void qdict_join(QDict *dest, QDict *src, bool overwrite);
|
||||||
|
|
||||||
#endif /* QDICT_H */
|
#endif /* QDICT_H */
|
||||||
|
@@ -42,13 +42,9 @@ struct Visitor
|
|||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
/* May be NULL */
|
/* May be NULL */
|
||||||
void (*start_optional)(Visitor *v, bool *present, const char *name,
|
void (*optional)(Visitor *v, bool *present, const char *name,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void (*end_optional)(Visitor *v, Error **errp);
|
|
||||||
|
|
||||||
void (*start_handle)(Visitor *v, void **obj, const char *kind,
|
|
||||||
const char *name, Error **errp);
|
|
||||||
void (*end_handle)(Visitor *v, Error **errp);
|
|
||||||
void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
|
void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
|
||||||
void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
|
void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
|
||||||
void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
|
void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
|
||||||
|
@@ -39,9 +39,8 @@ void visit_end_implicit_struct(Visitor *v, Error **errp);
|
|||||||
void visit_start_list(Visitor *v, const char *name, Error **errp);
|
void visit_start_list(Visitor *v, const char *name, Error **errp);
|
||||||
GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
|
GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
|
||||||
void visit_end_list(Visitor *v, Error **errp);
|
void visit_end_list(Visitor *v, Error **errp);
|
||||||
void visit_start_optional(Visitor *v, bool *present, const char *name,
|
void visit_optional(Visitor *v, bool *present, const char *name,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void visit_end_optional(Visitor *v, Error **errp);
|
|
||||||
void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
|
void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
|
||||||
const char *name, Error **errp);
|
const char *name, Error **errp);
|
||||||
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
|
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||||
|
@@ -318,6 +318,7 @@ void qemu_iovec_concat(QEMUIOVector *dst,
|
|||||||
void qemu_iovec_concat_iov(QEMUIOVector *dst,
|
void qemu_iovec_concat_iov(QEMUIOVector *dst,
|
||||||
struct iovec *src_iov, unsigned int src_cnt,
|
struct iovec *src_iov, unsigned int src_cnt,
|
||||||
size_t soffset, size_t sbytes);
|
size_t soffset, size_t sbytes);
|
||||||
|
bool qemu_iovec_is_zero(QEMUIOVector *qiov);
|
||||||
void qemu_iovec_destroy(QEMUIOVector *qiov);
|
void qemu_iovec_destroy(QEMUIOVector *qiov);
|
||||||
void qemu_iovec_reset(QEMUIOVector *qiov);
|
void qemu_iovec_reset(QEMUIOVector *qiov);
|
||||||
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,
|
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,
|
||||||
|
@@ -74,5 +74,6 @@ typedef struct SHPCDevice SHPCDevice;
|
|||||||
typedef struct FWCfgState FWCfgState;
|
typedef struct FWCfgState FWCfgState;
|
||||||
typedef struct PcGuestInfo PcGuestInfo;
|
typedef struct PcGuestInfo PcGuestInfo;
|
||||||
typedef struct Range Range;
|
typedef struct Range Range;
|
||||||
|
typedef struct AdapterInfo AdapterInfo;
|
||||||
|
|
||||||
#endif /* QEMU_TYPEDEFS_H */
|
#endif /* QEMU_TYPEDEFS_H */
|
||||||
|
@@ -300,7 +300,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension);
|
|||||||
}; \
|
}; \
|
||||||
uint64_t args_tmp[] = { __VA_ARGS__ }; \
|
uint64_t args_tmp[] = { __VA_ARGS__ }; \
|
||||||
int i; \
|
int i; \
|
||||||
for (i = 0; i < ARRAY_SIZE(args_tmp) && \
|
for (i = 0; i < (int)ARRAY_SIZE(args_tmp) && \
|
||||||
i < ARRAY_SIZE(cap.args); i++) { \
|
i < ARRAY_SIZE(cap.args); i++) { \
|
||||||
cap.args[i] = args_tmp[i]; \
|
cap.args[i] = args_tmp[i]; \
|
||||||
} \
|
} \
|
||||||
@@ -315,7 +315,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension);
|
|||||||
}; \
|
}; \
|
||||||
uint64_t args_tmp[] = { __VA_ARGS__ }; \
|
uint64_t args_tmp[] = { __VA_ARGS__ }; \
|
||||||
int i; \
|
int i; \
|
||||||
for (i = 0; i < ARRAY_SIZE(args_tmp) && \
|
for (i = 0; i < (int)ARRAY_SIZE(args_tmp) && \
|
||||||
i < ARRAY_SIZE(cap.args); i++) { \
|
i < ARRAY_SIZE(cap.args); i++) { \
|
||||||
cap.args[i] = args_tmp[i]; \
|
cap.args[i] = args_tmp[i]; \
|
||||||
} \
|
} \
|
||||||
@@ -363,6 +363,8 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg);
|
|||||||
int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg);
|
int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg);
|
||||||
void kvm_irqchip_release_virq(KVMState *s, int virq);
|
void kvm_irqchip_release_virq(KVMState *s, int virq);
|
||||||
|
|
||||||
|
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter);
|
||||||
|
|
||||||
int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n,
|
int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n,
|
||||||
EventNotifier *rn, int virq);
|
EventNotifier *rn, int virq);
|
||||||
int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq);
|
int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq);
|
||||||
|
@@ -29,6 +29,9 @@ QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev,
|
|||||||
void qemu_input_handler_activate(QemuInputHandlerState *s);
|
void qemu_input_handler_activate(QemuInputHandlerState *s);
|
||||||
void qemu_input_handler_deactivate(QemuInputHandlerState *s);
|
void qemu_input_handler_deactivate(QemuInputHandlerState *s);
|
||||||
void qemu_input_handler_unregister(QemuInputHandlerState *s);
|
void qemu_input_handler_unregister(QemuInputHandlerState *s);
|
||||||
|
void qemu_input_handler_bind(QemuInputHandlerState *s,
|
||||||
|
const char *device_id, int head,
|
||||||
|
Error **errp);
|
||||||
void qemu_input_event_send(QemuConsole *src, InputEvent *evt);
|
void qemu_input_event_send(QemuConsole *src, InputEvent *evt);
|
||||||
void qemu_input_event_sync(void);
|
void qemu_input_event_sync(void);
|
||||||
|
|
||||||
@@ -36,6 +39,7 @@ InputEvent *qemu_input_event_new_key(KeyValue *key, bool down);
|
|||||||
void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
|
void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
|
||||||
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down);
|
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down);
|
||||||
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
|
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
|
||||||
|
int qemu_input_key_number_to_qcode(uint8_t nr);
|
||||||
int qemu_input_key_value_to_number(const KeyValue *value);
|
int qemu_input_key_value_to_number(const KeyValue *value);
|
||||||
int qemu_input_key_value_to_qcode(const KeyValue *value);
|
int qemu_input_key_value_to_qcode(const KeyValue *value);
|
||||||
int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
|
int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
|
||||||
|
38
kvm-all.c
38
kvm-all.c
@@ -27,6 +27,7 @@
|
|||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/pci/msi.h"
|
#include "hw/pci/msi.h"
|
||||||
|
#include "hw/s390x/adapter.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
@@ -1236,6 +1237,35 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int rfd, int virq,
|
|||||||
return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd);
|
return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
|
||||||
|
{
|
||||||
|
struct kvm_irq_routing_entry kroute;
|
||||||
|
int virq;
|
||||||
|
|
||||||
|
if (!kvm_gsi_routing_enabled()) {
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
virq = kvm_irqchip_get_virq(s);
|
||||||
|
if (virq < 0) {
|
||||||
|
return virq;
|
||||||
|
}
|
||||||
|
|
||||||
|
kroute.gsi = virq;
|
||||||
|
kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER;
|
||||||
|
kroute.flags = 0;
|
||||||
|
kroute.u.adapter.summary_addr = adapter->summary_addr;
|
||||||
|
kroute.u.adapter.ind_addr = adapter->ind_addr;
|
||||||
|
kroute.u.adapter.summary_offset = adapter->summary_offset;
|
||||||
|
kroute.u.adapter.ind_offset = adapter->ind_offset;
|
||||||
|
kroute.u.adapter.adapter_id = adapter->adapter_id;
|
||||||
|
|
||||||
|
kvm_add_routing_entry(s, &kroute);
|
||||||
|
kvm_irqchip_commit_routes(s);
|
||||||
|
|
||||||
|
return virq;
|
||||||
|
}
|
||||||
|
|
||||||
#else /* !KVM_CAP_IRQ_ROUTING */
|
#else /* !KVM_CAP_IRQ_ROUTING */
|
||||||
|
|
||||||
void kvm_init_irq_routing(KVMState *s)
|
void kvm_init_irq_routing(KVMState *s)
|
||||||
@@ -1256,6 +1286,11 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg)
|
|||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign)
|
static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign)
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
@@ -1285,7 +1320,8 @@ static int kvm_irqchip_create(KVMState *s)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!qemu_opt_get_bool(qemu_get_machine_opts(), "kernel_irqchip", true) ||
|
if (!qemu_opt_get_bool(qemu_get_machine_opts(), "kernel_irqchip", true) ||
|
||||||
!kvm_check_extension(s, KVM_CAP_IRQCHIP)) {
|
(!kvm_check_extension(s, KVM_CAP_IRQCHIP) &&
|
||||||
|
(kvm_vm_enable_cap(s, KVM_CAP_S390_IRQCHIP, 0) < 0))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -136,6 +136,11 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg)
|
|||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n,
|
int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n,
|
||||||
EventNotifier *rn, int virq)
|
EventNotifier *rn, int virq)
|
||||||
{
|
{
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
#define __KVM_S390
|
#define __KVM_S390
|
||||||
|
#define __KVM_HAVE_GUEST_DEBUG
|
||||||
|
|
||||||
/* Device control API: s390-specific devices */
|
/* Device control API: s390-specific devices */
|
||||||
#define KVM_DEV_FLIC_GET_ALL_IRQS 1
|
#define KVM_DEV_FLIC_GET_ALL_IRQS 1
|
||||||
@@ -54,6 +55,13 @@ struct kvm_s390_io_adapter_req {
|
|||||||
__u64 addr;
|
__u64 addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* kvm attr_group on vm fd */
|
||||||
|
#define KVM_S390_VM_MEM_CTRL 0
|
||||||
|
|
||||||
|
/* kvm attributes for mem_ctrl */
|
||||||
|
#define KVM_S390_VM_MEM_ENABLE_CMMA 0
|
||||||
|
#define KVM_S390_VM_MEM_CLR_CMMA 1
|
||||||
|
|
||||||
/* for KVM_GET_REGS and KVM_SET_REGS */
|
/* for KVM_GET_REGS and KVM_SET_REGS */
|
||||||
struct kvm_regs {
|
struct kvm_regs {
|
||||||
/* general purpose regs for s390 */
|
/* general purpose regs for s390 */
|
||||||
@@ -72,11 +80,31 @@ struct kvm_fpu {
|
|||||||
__u64 fprs[16];
|
__u64 fprs[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define KVM_GUESTDBG_USE_HW_BP 0x00010000
|
||||||
|
|
||||||
|
#define KVM_HW_BP 1
|
||||||
|
#define KVM_HW_WP_WRITE 2
|
||||||
|
#define KVM_SINGLESTEP 4
|
||||||
|
|
||||||
struct kvm_debug_exit_arch {
|
struct kvm_debug_exit_arch {
|
||||||
|
__u64 addr;
|
||||||
|
__u8 type;
|
||||||
|
__u8 pad[7]; /* Should be set to 0 */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kvm_hw_breakpoint {
|
||||||
|
__u64 addr;
|
||||||
|
__u64 phys_addr;
|
||||||
|
__u64 len;
|
||||||
|
__u8 type;
|
||||||
|
__u8 pad[7]; /* Should be set to 0 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* for KVM_SET_GUEST_DEBUG */
|
/* for KVM_SET_GUEST_DEBUG */
|
||||||
struct kvm_guest_debug_arch {
|
struct kvm_guest_debug_arch {
|
||||||
|
__u32 nr_hw_bp;
|
||||||
|
__u32 pad; /* Should be set to 0 */
|
||||||
|
struct kvm_hw_breakpoint *hw_bp;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define KVM_SYNC_PREFIX (1UL << 0)
|
#define KVM_SYNC_PREFIX (1UL << 0)
|
||||||
|
@@ -416,6 +416,8 @@ struct kvm_s390_psw {
|
|||||||
#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u
|
#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u
|
||||||
#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u
|
#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u
|
||||||
#define KVM_S390_MCHK 0xfffe1000u
|
#define KVM_S390_MCHK 0xfffe1000u
|
||||||
|
#define KVM_S390_INT_CLOCK_COMP 0xffff1004u
|
||||||
|
#define KVM_S390_INT_CPU_TIMER 0xffff1005u
|
||||||
#define KVM_S390_INT_VIRTIO 0xffff2603u
|
#define KVM_S390_INT_VIRTIO 0xffff2603u
|
||||||
#define KVM_S390_INT_SERVICE 0xffff2401u
|
#define KVM_S390_INT_SERVICE 0xffff2401u
|
||||||
#define KVM_S390_INT_EMERGENCY 0xffff1201u
|
#define KVM_S390_INT_EMERGENCY 0xffff1201u
|
||||||
@@ -515,6 +517,7 @@ enum {
|
|||||||
kvm_ioeventfd_flag_nr_pio,
|
kvm_ioeventfd_flag_nr_pio,
|
||||||
kvm_ioeventfd_flag_nr_deassign,
|
kvm_ioeventfd_flag_nr_deassign,
|
||||||
kvm_ioeventfd_flag_nr_virtio_ccw_notify,
|
kvm_ioeventfd_flag_nr_virtio_ccw_notify,
|
||||||
|
kvm_ioeventfd_flag_nr_fast_mmio,
|
||||||
kvm_ioeventfd_flag_nr_max,
|
kvm_ioeventfd_flag_nr_max,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -529,7 +532,7 @@ enum {
|
|||||||
struct kvm_ioeventfd {
|
struct kvm_ioeventfd {
|
||||||
__u64 datamatch;
|
__u64 datamatch;
|
||||||
__u64 addr; /* legal pio/mmio address */
|
__u64 addr; /* legal pio/mmio address */
|
||||||
__u32 len; /* 1, 2, 4, or 8 bytes */
|
__u32 len; /* 1, 2, 4, or 8 bytes; or 0 to ignore length */
|
||||||
__s32 fd;
|
__s32 fd;
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
__u8 pad[36];
|
__u8 pad[36];
|
||||||
@@ -743,6 +746,8 @@ struct kvm_ppc_smmu_info {
|
|||||||
#define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
|
#define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
|
||||||
#define KVM_CAP_ENABLE_CAP_VM 98
|
#define KVM_CAP_ENABLE_CAP_VM 98
|
||||||
#define KVM_CAP_S390_IRQCHIP 99
|
#define KVM_CAP_S390_IRQCHIP 99
|
||||||
|
#define KVM_CAP_IOEVENTFD_NO_LENGTH 100
|
||||||
|
#define KVM_CAP_VM_ATTRIBUTES 101
|
||||||
|
|
||||||
#ifdef KVM_CAP_IRQ_ROUTING
|
#ifdef KVM_CAP_IRQ_ROUTING
|
||||||
|
|
||||||
|
153
monitor.c
153
monitor.c
@@ -4269,6 +4269,55 @@ static const char *next_arg_type(const char *typestr)
|
|||||||
return (p != NULL ? ++p : typestr);
|
return (p != NULL ? ++p : typestr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void add_completion_option(ReadLineState *rs, const char *str,
|
||||||
|
const char *option)
|
||||||
|
{
|
||||||
|
if (!str || !option) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!strncmp(option, str, strlen(str))) {
|
||||||
|
readline_add_completion(rs, option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
ChardevBackendInfoList *list, *start;
|
||||||
|
|
||||||
|
if (nb_args != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
len = strlen(str);
|
||||||
|
readline_set_completion_index(rs, len);
|
||||||
|
|
||||||
|
start = list = qmp_query_chardev_backends(NULL);
|
||||||
|
while (list) {
|
||||||
|
const char *chr_name = list->value->name;
|
||||||
|
|
||||||
|
if (!strncmp(chr_name, str, len)) {
|
||||||
|
readline_add_completion(rs, chr_name);
|
||||||
|
}
|
||||||
|
list = list->next;
|
||||||
|
}
|
||||||
|
qapi_free_ChardevBackendInfoList(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (nb_args != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
len = strlen(str);
|
||||||
|
readline_set_completion_index(rs, len);
|
||||||
|
for (i = 0; NetClientOptionsKind_lookup[i]; i++) {
|
||||||
|
add_completion_option(rs, str, NetClientOptionsKind_lookup[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void device_add_completion(ReadLineState *rs, int nb_args, const char *str)
|
void device_add_completion(ReadLineState *rs, int nb_args, const char *str)
|
||||||
{
|
{
|
||||||
GSList *list, *elt;
|
GSList *list, *elt;
|
||||||
@@ -4339,6 +4388,29 @@ static void device_del_bus_completion(ReadLineState *rs, BusState *bus,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
ChardevInfoList *list, *start;
|
||||||
|
|
||||||
|
if (nb_args != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
len = strlen(str);
|
||||||
|
readline_set_completion_index(rs, len);
|
||||||
|
|
||||||
|
start = list = qmp_query_chardev(NULL);
|
||||||
|
while (list) {
|
||||||
|
ChardevInfo *chr = list->value;
|
||||||
|
|
||||||
|
if (!strncmp(chr->label, str, len)) {
|
||||||
|
readline_add_completion(rs, chr->label);
|
||||||
|
}
|
||||||
|
list = list->next;
|
||||||
|
}
|
||||||
|
qapi_free_ChardevInfoList(start);
|
||||||
|
}
|
||||||
|
|
||||||
void device_del_completion(ReadLineState *rs, int nb_args, const char *str)
|
void device_del_completion(ReadLineState *rs, int nb_args, const char *str)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
@@ -4376,6 +4448,77 @@ void object_del_completion(ReadLineState *rs, int nb_args, const char *str)
|
|||||||
qapi_free_ObjectPropertyInfoList(start);
|
qapi_free_ObjectPropertyInfoList(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendkey_completion(ReadLineState *rs, int nb_args, const char *str)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *sep;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (nb_args != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sep = strrchr(str, '-');
|
||||||
|
if (sep) {
|
||||||
|
str = sep + 1;
|
||||||
|
}
|
||||||
|
len = strlen(str);
|
||||||
|
readline_set_completion_index(rs, len);
|
||||||
|
for (i = 0; i < Q_KEY_CODE_MAX; i++) {
|
||||||
|
if (!strncmp(str, QKeyCode_lookup[i], len)) {
|
||||||
|
readline_add_completion(rs, QKeyCode_lookup[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_link_completion(ReadLineState *rs, int nb_args, const char *str)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = strlen(str);
|
||||||
|
readline_set_completion_index(rs, len);
|
||||||
|
if (nb_args == 2) {
|
||||||
|
NetClientState *ncs[255];
|
||||||
|
int count, i;
|
||||||
|
count = qemu_find_net_clients_except(NULL, ncs,
|
||||||
|
NET_CLIENT_OPTIONS_KIND_NONE, 255);
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
const char *name = ncs[i]->name;
|
||||||
|
if (!strncmp(str, name, len)) {
|
||||||
|
readline_add_completion(rs, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (nb_args == 3) {
|
||||||
|
add_completion_option(rs, str, "on");
|
||||||
|
add_completion_option(rs, str, "off");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)
|
||||||
|
{
|
||||||
|
int len, count, i;
|
||||||
|
NetClientState *ncs[255];
|
||||||
|
|
||||||
|
if (nb_args != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strlen(str);
|
||||||
|
readline_set_completion_index(rs, len);
|
||||||
|
count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC,
|
||||||
|
255);
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
QemuOpts *opts;
|
||||||
|
const char *name = ncs[i]->name;
|
||||||
|
if (strncmp(str, name, len)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), name);
|
||||||
|
if (opts) {
|
||||||
|
readline_add_completion(rs, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void monitor_find_completion_by_table(Monitor *mon,
|
static void monitor_find_completion_by_table(Monitor *mon,
|
||||||
const mon_cmd_t *cmd_table,
|
const mon_cmd_t *cmd_table,
|
||||||
char **args,
|
char **args,
|
||||||
@@ -4444,15 +4587,7 @@ static void monitor_find_completion_by_table(Monitor *mon,
|
|||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
case 'S':
|
case 'S':
|
||||||
if (!strcmp(cmd->name, "sendkey")) {
|
if (!strcmp(cmd->name, "help|?")) {
|
||||||
char *sep = strrchr(str, '-');
|
|
||||||
if (sep)
|
|
||||||
str = sep + 1;
|
|
||||||
readline_set_completion_index(mon->rs, strlen(str));
|
|
||||||
for (i = 0; i < Q_KEY_CODE_MAX; i++) {
|
|
||||||
cmd_completion(mon, str, QKeyCode_lookup[i]);
|
|
||||||
}
|
|
||||||
} else if (!strcmp(cmd->name, "help|?")) {
|
|
||||||
monitor_find_completion_by_table(mon, cmd_table,
|
monitor_find_completion_by_table(mon, cmd_table,
|
||||||
&args[1], nb_args - 1);
|
&args[1], nb_args - 1);
|
||||||
}
|
}
|
||||||
|
@@ -633,7 +633,7 @@ int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
|
|||||||
if (nc->info->type == type) {
|
if (nc->info->type == type) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(nc->name, id)) {
|
if (!id || !strcmp(nc->name, id)) {
|
||||||
if (ret < max) {
|
if (ret < max) {
|
||||||
ncs[ret] = nc;
|
ncs[ret] = nc;
|
||||||
}
|
}
|
||||||
|
@@ -691,7 +691,7 @@
|
|||||||
# Information about current migration process.
|
# Information about current migration process.
|
||||||
#
|
#
|
||||||
# @status: #optional string describing the current migration status.
|
# @status: #optional string describing the current migration status.
|
||||||
# As of 0.14.0 this can be 'active', 'completed', 'failed' or
|
# As of 0.14.0 this can be 'setup', 'active', 'completed', 'failed' or
|
||||||
# 'cancelled'. If this field is not returned, no migration process
|
# 'cancelled'. If this field is not returned, no migration process
|
||||||
# has been initiated
|
# has been initiated
|
||||||
#
|
#
|
||||||
@@ -942,6 +942,8 @@
|
|||||||
# @encryption_key_missing: true if the backing device is encrypted but an
|
# @encryption_key_missing: true if the backing device is encrypted but an
|
||||||
# valid encryption key is missing
|
# valid encryption key is missing
|
||||||
#
|
#
|
||||||
|
# @detect_zeroes: detect and optimize zero writes (Since 2.1)
|
||||||
|
#
|
||||||
# @bps: total throughput limit in bytes per second is specified
|
# @bps: total throughput limit in bytes per second is specified
|
||||||
#
|
#
|
||||||
# @bps_rd: read throughput limit in bytes per second is specified
|
# @bps_rd: read throughput limit in bytes per second is specified
|
||||||
@@ -977,6 +979,7 @@
|
|||||||
'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
|
'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
|
||||||
'*backing_file': 'str', 'backing_file_depth': 'int',
|
'*backing_file': 'str', 'backing_file_depth': 'int',
|
||||||
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
||||||
|
'detect_zeroes': 'BlockdevDetectZeroesOptions',
|
||||||
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
|
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
|
||||||
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
|
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
|
||||||
'image': 'ImageInfo',
|
'image': 'ImageInfo',
|
||||||
@@ -4254,6 +4257,22 @@
|
|||||||
{ 'enum': 'BlockdevDiscardOptions',
|
{ 'enum': 'BlockdevDiscardOptions',
|
||||||
'data': [ 'ignore', 'unmap' ] }
|
'data': [ 'ignore', 'unmap' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevDetectZeroesOptions
|
||||||
|
#
|
||||||
|
# Describes the operation mode for the automatic conversion of plain
|
||||||
|
# zero writes by the OS to driver specific optimized zero write commands.
|
||||||
|
#
|
||||||
|
# @off: Disabled (default)
|
||||||
|
# @on: Enabled
|
||||||
|
# @unmap: Enabled and even try to unmap blocks if possible. This requires
|
||||||
|
# also that @BlockdevDiscardOptions is set to unmap for this device.
|
||||||
|
#
|
||||||
|
# Since: 2.1
|
||||||
|
##
|
||||||
|
{ 'enum': 'BlockdevDetectZeroesOptions',
|
||||||
|
'data': [ 'off', 'on', 'unmap' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevAioOptions
|
# @BlockdevAioOptions
|
||||||
#
|
#
|
||||||
@@ -4320,6 +4339,8 @@
|
|||||||
# (default: enospc)
|
# (default: enospc)
|
||||||
# @read-only: #optional whether the block device should be read-only
|
# @read-only: #optional whether the block device should be read-only
|
||||||
# (default: false)
|
# (default: false)
|
||||||
|
# @detect-zeroes: #optional detect and optimize zero writes (Since 2.1)
|
||||||
|
# (default: off)
|
||||||
#
|
#
|
||||||
# Since: 1.7
|
# Since: 1.7
|
||||||
##
|
##
|
||||||
@@ -4332,7 +4353,8 @@
|
|||||||
'*aio': 'BlockdevAioOptions',
|
'*aio': 'BlockdevAioOptions',
|
||||||
'*rerror': 'BlockdevOnError',
|
'*rerror': 'BlockdevOnError',
|
||||||
'*werror': 'BlockdevOnError',
|
'*werror': 'BlockdevOnError',
|
||||||
'*read-only': 'bool' } }
|
'*read-only': 'bool',
|
||||||
|
'*detect-zeroes': 'BlockdevDetectZeroesOptions' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevOptionsFile
|
# @BlockdevOptionsFile
|
||||||
|
@@ -484,8 +484,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
|
|||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
opts_start_optional(Visitor *v, bool *present, const char *name,
|
opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
|
OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
|
||||||
|
|
||||||
@@ -528,7 +527,7 @@ opts_visitor_new(const QemuOpts *opts)
|
|||||||
/* type_number() is not filled in, but this is not the first visitor to
|
/* type_number() is not filled in, but this is not the first visitor to
|
||||||
* skip some mandatory methods... */
|
* skip some mandatory methods... */
|
||||||
|
|
||||||
ov->visitor.start_optional = &opts_start_optional;
|
ov->visitor.optional = &opts_optional;
|
||||||
|
|
||||||
ov->opts_root = opts;
|
ov->opts_root = opts;
|
||||||
|
|
||||||
|
@@ -17,46 +17,27 @@
|
|||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
#include "qapi/visitor-impl.h"
|
#include "qapi/visitor-impl.h"
|
||||||
|
|
||||||
void visit_start_handle(Visitor *v, void **obj, const char *kind,
|
|
||||||
const char *name, Error **errp)
|
|
||||||
{
|
|
||||||
if (!error_is_set(errp) && v->start_handle) {
|
|
||||||
v->start_handle(v, obj, kind, name, errp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit_end_handle(Visitor *v, Error **errp)
|
|
||||||
{
|
|
||||||
if (!error_is_set(errp) && v->end_handle) {
|
|
||||||
v->end_handle(v, errp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit_start_struct(Visitor *v, void **obj, const char *kind,
|
void visit_start_struct(Visitor *v, void **obj, const char *kind,
|
||||||
const char *name, size_t size, Error **errp)
|
const char *name, size_t size, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
v->start_struct(v, obj, kind, name, size, errp);
|
v->start_struct(v, obj, kind, name, size, errp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_end_struct(Visitor *v, Error **errp)
|
void visit_end_struct(Visitor *v, Error **errp)
|
||||||
{
|
{
|
||||||
assert(!error_is_set(errp));
|
|
||||||
v->end_struct(v, errp);
|
v->end_struct(v, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
|
void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp) && v->start_implicit_struct) {
|
if (v->start_implicit_struct) {
|
||||||
v->start_implicit_struct(v, obj, size, errp);
|
v->start_implicit_struct(v, obj, size, errp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_end_implicit_struct(Visitor *v, Error **errp)
|
void visit_end_implicit_struct(Visitor *v, Error **errp)
|
||||||
{
|
{
|
||||||
assert(!error_is_set(errp));
|
|
||||||
if (v->end_implicit_struct) {
|
if (v->end_implicit_struct) {
|
||||||
v->end_implicit_struct(v, errp);
|
v->end_implicit_struct(v, errp);
|
||||||
}
|
}
|
||||||
@@ -64,45 +45,31 @@ void visit_end_implicit_struct(Visitor *v, Error **errp)
|
|||||||
|
|
||||||
void visit_start_list(Visitor *v, const char *name, Error **errp)
|
void visit_start_list(Visitor *v, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
v->start_list(v, name, errp);
|
v->start_list(v, name, errp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
|
GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
return v->next_list(v, list, errp);
|
return v->next_list(v, list, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit_end_list(Visitor *v, Error **errp)
|
void visit_end_list(Visitor *v, Error **errp)
|
||||||
{
|
{
|
||||||
assert(!error_is_set(errp));
|
|
||||||
v->end_list(v, errp);
|
v->end_list(v, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_start_optional(Visitor *v, bool *present, const char *name,
|
void visit_optional(Visitor *v, bool *present, const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp) && v->start_optional) {
|
if (v->optional) {
|
||||||
v->start_optional(v, present, name, errp);
|
v->optional(v, present, name, errp);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit_end_optional(Visitor *v, Error **errp)
|
|
||||||
{
|
|
||||||
if (!error_is_set(errp) && v->end_optional) {
|
|
||||||
v->end_optional(v, errp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
|
void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
|
||||||
const char *name, Error **errp)
|
const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp) && v->get_next_type) {
|
if (v->get_next_type) {
|
||||||
v->get_next_type(v, obj, qtypes, name, errp);
|
v->get_next_type(v, obj, qtypes, name, errp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,22 +77,18 @@ void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
|
|||||||
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
|
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||||
const char *kind, const char *name, Error **errp)
|
const char *kind, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
v->type_enum(v, obj, strings, kind, name, errp);
|
v->type_enum(v, obj, strings, kind, name, errp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
|
void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
v->type_int(v, obj, name, errp);
|
v->type_int(v, obj, name, errp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
|
void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t value;
|
int64_t value;
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_uint8) {
|
if (v->type_uint8) {
|
||||||
v->type_uint8(v, obj, name, errp);
|
v->type_uint8(v, obj, name, errp);
|
||||||
} else {
|
} else {
|
||||||
@@ -139,12 +102,11 @@ void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
|
|||||||
*obj = value;
|
*obj = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp)
|
void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t value;
|
int64_t value;
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_uint16) {
|
if (v->type_uint16) {
|
||||||
v->type_uint16(v, obj, name, errp);
|
v->type_uint16(v, obj, name, errp);
|
||||||
} else {
|
} else {
|
||||||
@@ -158,12 +120,11 @@ void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp
|
|||||||
*obj = value;
|
*obj = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp)
|
void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t value;
|
int64_t value;
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_uint32) {
|
if (v->type_uint32) {
|
||||||
v->type_uint32(v, obj, name, errp);
|
v->type_uint32(v, obj, name, errp);
|
||||||
} else {
|
} else {
|
||||||
@@ -177,12 +138,11 @@ void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp
|
|||||||
*obj = value;
|
*obj = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
|
void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t value;
|
int64_t value;
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_uint64) {
|
if (v->type_uint64) {
|
||||||
v->type_uint64(v, obj, name, errp);
|
v->type_uint64(v, obj, name, errp);
|
||||||
} else {
|
} else {
|
||||||
@@ -191,12 +151,11 @@ void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp
|
|||||||
*obj = value;
|
*obj = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
|
void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t value;
|
int64_t value;
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_int8) {
|
if (v->type_int8) {
|
||||||
v->type_int8(v, obj, name, errp);
|
v->type_int8(v, obj, name, errp);
|
||||||
} else {
|
} else {
|
||||||
@@ -210,12 +169,11 @@ void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
|
|||||||
*obj = value;
|
*obj = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
|
void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t value;
|
int64_t value;
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_int16) {
|
if (v->type_int16) {
|
||||||
v->type_int16(v, obj, name, errp);
|
v->type_int16(v, obj, name, errp);
|
||||||
} else {
|
} else {
|
||||||
@@ -229,12 +187,11 @@ void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
|
|||||||
*obj = value;
|
*obj = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
|
void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t value;
|
int64_t value;
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_int32) {
|
if (v->type_int32) {
|
||||||
v->type_int32(v, obj, name, errp);
|
v->type_int32(v, obj, name, errp);
|
||||||
} else {
|
} else {
|
||||||
@@ -248,23 +205,20 @@ void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
|
|||||||
*obj = value;
|
*obj = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
|
void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_int64) {
|
if (v->type_int64) {
|
||||||
v->type_int64(v, obj, name, errp);
|
v->type_int64(v, obj, name, errp);
|
||||||
} else {
|
} else {
|
||||||
v->type_int(v, obj, name, errp);
|
v->type_int(v, obj, name, errp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
|
void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t value;
|
int64_t value;
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
if (v->type_size) {
|
if (v->type_size) {
|
||||||
v->type_size(v, obj, name, errp);
|
v->type_size(v, obj, name, errp);
|
||||||
} else if (v->type_uint64) {
|
} else if (v->type_uint64) {
|
||||||
@@ -275,28 +229,21 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
|
|||||||
*obj = value;
|
*obj = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
|
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
v->type_bool(v, obj, name, errp);
|
v->type_bool(v, obj, name, errp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
|
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
v->type_str(v, obj, name, errp);
|
v->type_str(v, obj, name, errp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
|
void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
v->type_number(v, obj, name, errp);
|
v->type_number(v, obj, name, errp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void output_type_enum(Visitor *v, int *obj, const char *strings[],
|
void output_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||||
const char *kind, const char *name,
|
const char *kind, const char *name,
|
||||||
@@ -321,13 +268,15 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[],
|
|||||||
const char *kind, const char *name,
|
const char *kind, const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
int64_t value = 0;
|
int64_t value = 0;
|
||||||
char *enum_str;
|
char *enum_str;
|
||||||
|
|
||||||
assert(strings);
|
assert(strings);
|
||||||
|
|
||||||
visit_type_str(v, &enum_str, name, errp);
|
visit_type_str(v, &enum_str, name, &local_err);
|
||||||
if (error_is_set(errp)) {
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -286,8 +286,8 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qmp_input_start_optional(Visitor *v, bool *present,
|
static void qmp_input_optional(Visitor *v, bool *present, const char *name,
|
||||||
const char *name, Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QmpInputVisitor *qiv = to_qiv(v);
|
QmpInputVisitor *qiv = to_qiv(v);
|
||||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||||
@@ -329,7 +329,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
|
|||||||
v->visitor.type_bool = qmp_input_type_bool;
|
v->visitor.type_bool = qmp_input_type_bool;
|
||||||
v->visitor.type_str = qmp_input_type_str;
|
v->visitor.type_str = qmp_input_type_str;
|
||||||
v->visitor.type_number = qmp_input_type_number;
|
v->visitor.type_number = qmp_input_type_number;
|
||||||
v->visitor.start_optional = qmp_input_start_optional;
|
v->visitor.optional = qmp_input_optional;
|
||||||
v->visitor.get_next_type = qmp_input_get_next_type;
|
v->visitor.get_next_type = qmp_input_get_next_type;
|
||||||
|
|
||||||
qmp_input_push(v, obj, NULL);
|
qmp_input_push(v, obj, NULL);
|
||||||
|
@@ -120,8 +120,8 @@ static void parse_type_number(Visitor *v, double *obj, const char *name,
|
|||||||
*obj = val;
|
*obj = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_start_optional(Visitor *v, bool *present,
|
static void parse_optional(Visitor *v, bool *present, const char *name,
|
||||||
const char *name, Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ StringInputVisitor *string_input_visitor_new(const char *str)
|
|||||||
v->visitor.type_bool = parse_type_bool;
|
v->visitor.type_bool = parse_type_bool;
|
||||||
v->visitor.type_str = parse_type_str;
|
v->visitor.type_str = parse_type_str;
|
||||||
v->visitor.type_number = parse_type_number;
|
v->visitor.type_number = parse_type_number;
|
||||||
v->visitor.start_optional = parse_start_optional;
|
v->visitor.optional = parse_optional;
|
||||||
|
|
||||||
v->string = str;
|
v->string = str;
|
||||||
return v;
|
return v;
|
||||||
|
32
qemu-char.c
32
qemu-char.c
@@ -3204,6 +3204,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
|
|||||||
void (*init)(struct CharDriverState *s),
|
void (*init)(struct CharDriverState *s),
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
CharDriver *cd;
|
CharDriver *cd;
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
GSList *i;
|
GSList *i;
|
||||||
@@ -3245,13 +3246,14 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
|
|||||||
chr = NULL;
|
chr = NULL;
|
||||||
backend->kind = cd->kind;
|
backend->kind = cd->kind;
|
||||||
if (cd->parse) {
|
if (cd->parse) {
|
||||||
cd->parse(opts, backend, errp);
|
cd->parse(opts, backend, &local_err);
|
||||||
if (error_is_set(errp)) {
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
goto qapi_out;
|
goto qapi_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = qmp_chardev_add(bid ? bid : id, backend, errp);
|
ret = qmp_chardev_add(bid ? bid : id, backend, errp);
|
||||||
if (error_is_set(errp)) {
|
if (!ret) {
|
||||||
goto qapi_out;
|
goto qapi_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3263,7 +3265,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
|
|||||||
backend->kind = CHARDEV_BACKEND_KIND_MUX;
|
backend->kind = CHARDEV_BACKEND_KIND_MUX;
|
||||||
backend->mux->chardev = g_strdup(bid);
|
backend->mux->chardev = g_strdup(bid);
|
||||||
ret = qmp_chardev_add(id, backend, errp);
|
ret = qmp_chardev_add(id, backend, errp);
|
||||||
if (error_is_set(errp)) {
|
if (!ret) {
|
||||||
chr = qemu_chr_find(bid);
|
chr = qemu_chr_find(bid);
|
||||||
qemu_chr_delete(chr);
|
qemu_chr_delete(chr);
|
||||||
chr = NULL;
|
chr = NULL;
|
||||||
@@ -3620,18 +3622,18 @@ static int qmp_chardev_open_file_source(char *src, int flags,
|
|||||||
|
|
||||||
static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
|
static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
|
||||||
{
|
{
|
||||||
int flags, in = -1, out = -1;
|
int flags, in = -1, out;
|
||||||
|
|
||||||
flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY;
|
flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY;
|
||||||
out = qmp_chardev_open_file_source(file->out, flags, errp);
|
out = qmp_chardev_open_file_source(file->out, flags, errp);
|
||||||
if (error_is_set(errp)) {
|
if (out < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file->has_in) {
|
if (file->has_in) {
|
||||||
flags = O_RDONLY;
|
flags = O_RDONLY;
|
||||||
in = qmp_chardev_open_file_source(file->in, flags, errp);
|
in = qmp_chardev_open_file_source(file->in, flags, errp);
|
||||||
if (error_is_set(errp)) {
|
if (in < 0) {
|
||||||
qemu_close(out);
|
qemu_close(out);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -3647,7 +3649,7 @@ static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
|
|||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
|
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
|
||||||
if (error_is_set(errp)) {
|
if (fd < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
qemu_set_nonblock(fd);
|
qemu_set_nonblock(fd);
|
||||||
@@ -3665,7 +3667,7 @@ static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
|
|||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
|
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
|
||||||
if (error_is_set(errp)) {
|
if (fd < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return qemu_chr_open_pp_fd(fd);
|
return qemu_chr_open_pp_fd(fd);
|
||||||
@@ -3692,7 +3694,7 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
|
|||||||
} else {
|
} else {
|
||||||
fd = socket_connect(addr, errp, NULL, NULL);
|
fd = socket_connect(addr, errp, NULL, NULL);
|
||||||
}
|
}
|
||||||
if (error_is_set(errp)) {
|
if (fd < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen,
|
return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen,
|
||||||
@@ -3705,7 +3707,7 @@ static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
|
|||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = socket_dgram(udp->remote, udp->local, errp);
|
fd = socket_dgram(udp->remote, udp->local, errp);
|
||||||
if (error_is_set(errp)) {
|
if (fd < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return qemu_chr_open_udp_fd(fd);
|
return qemu_chr_open_udp_fd(fd);
|
||||||
@@ -3796,7 +3798,13 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chr == NULL && !error_is_set(errp)) {
|
/*
|
||||||
|
* Character backend open hasn't been fully converted to the Error
|
||||||
|
* API. Some opens fail without setting an error. Set a generic
|
||||||
|
* error then.
|
||||||
|
* TODO full conversion to Error API
|
||||||
|
*/
|
||||||
|
if (chr == NULL && errp && !*errp) {
|
||||||
error_setg(errp, "Failed to create chardev");
|
error_setg(errp, "Failed to create chardev");
|
||||||
}
|
}
|
||||||
if (chr) {
|
if (chr) {
|
||||||
|
@@ -70,12 +70,9 @@ static void add_format_to_seq(void *opaque, const char *fmt_name)
|
|||||||
{
|
{
|
||||||
GSequence *seq = opaque;
|
GSequence *seq = opaque;
|
||||||
|
|
||||||
if (!g_sequence_lookup(seq, (gpointer)fmt_name,
|
|
||||||
compare_data, NULL)) {
|
|
||||||
g_sequence_insert_sorted(seq, (gpointer)fmt_name,
|
g_sequence_insert_sorted(seq, (gpointer)fmt_name,
|
||||||
compare_data, NULL);
|
compare_data, NULL);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...)
|
static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
@@ -414,6 +414,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive,
|
|||||||
" [,serial=s][,addr=A][,rerror=ignore|stop|report]\n"
|
" [,serial=s][,addr=A][,rerror=ignore|stop|report]\n"
|
||||||
" [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n"
|
" [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n"
|
||||||
" [,readonly=on|off][,copy-on-read=on|off]\n"
|
" [,readonly=on|off][,copy-on-read=on|off]\n"
|
||||||
|
" [,detect-zeroes=on|off|unmap]\n"
|
||||||
" [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]]\n"
|
" [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]]\n"
|
||||||
" [[,iops=i]|[[,iops_rd=r][,iops_wr=w]]]\n"
|
" [[,iops=i]|[[,iops_rd=r][,iops_wr=w]]]\n"
|
||||||
" [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n"
|
" [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n"
|
||||||
@@ -475,6 +476,11 @@ Open drive @option{file} as read-only. Guest write attempts will fail.
|
|||||||
@item copy-on-read=@var{copy-on-read}
|
@item copy-on-read=@var{copy-on-read}
|
||||||
@var{copy-on-read} is "on" or "off" and enables whether to copy read backing
|
@var{copy-on-read} is "on" or "off" and enables whether to copy read backing
|
||||||
file sectors into the image file.
|
file sectors into the image file.
|
||||||
|
@item detect-zeroes=@var{detect-zeroes}
|
||||||
|
@var{detect-zeroes} is "off", "on" or "unmap" and enables the automatic
|
||||||
|
conversion of plain zero writes by the OS to driver specific optimized
|
||||||
|
zero write commands. You may even choose "unmap" if @var{discard} is set
|
||||||
|
to "unmap" to allow a zero write to be converted to an UNMAP operation.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
By default, the @option{cache=writeback} mode is used. It will report data
|
By default, the @option{cache=writeback} mode is used. It will report data
|
||||||
@@ -2191,6 +2197,74 @@ qemu-system-x86_64 --drive file=gluster://192.0.2.1/testvol/a.img
|
|||||||
@end example
|
@end example
|
||||||
|
|
||||||
See also @url{http://www.gluster.org}.
|
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.
|
||||||
|
|
||||||
|
Syntax using a single filename:
|
||||||
|
@example
|
||||||
|
<protocol>://[<username>[:<password>]@@]<host>/<path>
|
||||||
|
@end example
|
||||||
|
|
||||||
|
where:
|
||||||
|
@table @option
|
||||||
|
@item protocol
|
||||||
|
'http', 'https', 'ftp', 'ftps', or 'tftp'.
|
||||||
|
|
||||||
|
@item username
|
||||||
|
Optional username for authentication to the remote server.
|
||||||
|
|
||||||
|
@item password
|
||||||
|
Optional password for authentication to the remote server.
|
||||||
|
|
||||||
|
@item host
|
||||||
|
Address of the remote server.
|
||||||
|
|
||||||
|
@item path
|
||||||
|
Path on the remote server, including any query string.
|
||||||
|
@end table
|
||||||
|
|
||||||
|
The following options are also supported:
|
||||||
|
@table @option
|
||||||
|
@item url
|
||||||
|
The full URL when passing options to the driver explicitly.
|
||||||
|
|
||||||
|
@item readahead
|
||||||
|
The amount of data to read ahead with each range request to the remote server.
|
||||||
|
This value may optionally have the suffix 'T', 'G', 'M', 'K', 'k' or 'b'. If it
|
||||||
|
does not have a suffix, it will be assumed to be in bytes. The value must be a
|
||||||
|
multiple of 512 bytes. It defaults to 256k.
|
||||||
|
|
||||||
|
@item sslverify
|
||||||
|
Whether to verify the remote server's certificate when connecting over SSL. It
|
||||||
|
can have the value 'on' or 'off'. It defaults to 'on'.
|
||||||
|
@end table
|
||||||
|
|
||||||
|
Note that when passing options to qemu explicitly, @option{driver} is the value
|
||||||
|
of <protocol>.
|
||||||
|
|
||||||
|
Example: boot from a remote Fedora 20 live ISO image
|
||||||
|
@example
|
||||||
|
qemu-system-x86_64 --drive media=cdrom,file=http://dl.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly
|
||||||
|
|
||||||
|
qemu-system-x86_64 --drive media=cdrom,file.driver=http,file.url=http://dl.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Example: boot from a remote Fedora 20 cloud image using a local overlay for
|
||||||
|
writes, copy-on-read, and a readahead of 64k
|
||||||
|
@example
|
||||||
|
qemu-img create -f qcow2 -o backing_file='json:@{"file.driver":"http",, "file.url":"https://dl.fedoraproject.org/pub/fedora/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2",, "file.readahead":"64k"@}' /tmp/Fedora-x86_64-20-20131211.1-sda.qcow2
|
||||||
|
|
||||||
|
qemu-system-x86_64 -drive file=/tmp/Fedora-x86_64-20-20131211.1-sda.qcow2,copy-on-read=on
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Example: boot from an image stored on a VMware vSphere server with a self-signed
|
||||||
|
certificate using a local overlay for writes and a readahead of 64k
|
||||||
|
@example
|
||||||
|
qemu-img create -f qcow2 -o backing_file='json:@{"file.driver":"https",, "file.url":"https://user:password@@vsphere.example.com/folder/test/test-flat.vmdk?dcPath=Datacenter&dsName=datastore1",, "file.sslverify":"off",, "file.readahead":"64k"@}' /tmp/test.qcow2
|
||||||
|
|
||||||
|
qemu-system-x86_64 -drive file=/tmp/test.qcow2
|
||||||
|
@end example
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
|
@@ -2032,6 +2032,8 @@ Each json-object contain the following:
|
|||||||
- "iops_rd_max": read I/O operations max (json-int)
|
- "iops_rd_max": read I/O operations max (json-int)
|
||||||
- "iops_wr_max": write I/O operations max (json-int)
|
- "iops_wr_max": write I/O operations max (json-int)
|
||||||
- "iops_size": I/O size when limiting by iops (json-int)
|
- "iops_size": I/O size when limiting by iops (json-int)
|
||||||
|
- "detect_zeroes": detect and optimize zero writing (json-string)
|
||||||
|
- Possible values: "off", "on", "unmap"
|
||||||
- "image": the detail of the image, it is a json-object containing
|
- "image": the detail of the image, it is a json-object containing
|
||||||
the following:
|
the following:
|
||||||
- "filename": image file name (json-string)
|
- "filename": image file name (json-string)
|
||||||
@@ -2108,6 +2110,7 @@ Example:
|
|||||||
"iops_rd_max": 0,
|
"iops_rd_max": 0,
|
||||||
"iops_wr_max": 0,
|
"iops_wr_max": 0,
|
||||||
"iops_size": 0,
|
"iops_size": 0,
|
||||||
|
"detect_zeroes": "on",
|
||||||
"image":{
|
"image":{
|
||||||
"filename":"disks/test.qcow2",
|
"filename":"disks/test.qcow2",
|
||||||
"format":"qcow2",
|
"format":"qcow2",
|
||||||
@@ -2937,7 +2940,7 @@ block migration status.
|
|||||||
The main json-object contains the following:
|
The main json-object contains the following:
|
||||||
|
|
||||||
- "status": migration status (json-string)
|
- "status": migration status (json-string)
|
||||||
- Possible values: "active", "completed", "failed", "cancelled"
|
- Possible values: "setup", "active", "completed", "failed", "cancelled"
|
||||||
- "total-time": total amount of ms since migration started. If
|
- "total-time": total amount of ms since migration started. If
|
||||||
migration has ended, it returns the total migration
|
migration has ended, it returns the total migration
|
||||||
time (json-int)
|
time (json-int)
|
||||||
|
@@ -665,3 +665,35 @@ void qdict_array_split(QDict *src, QList **dst)
|
|||||||
qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict));
|
qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qdict_join(): Absorb the src QDict into the dest QDict, that is, move all
|
||||||
|
* elements from src to dest.
|
||||||
|
*
|
||||||
|
* If an element from src has a key already present in dest, it will not be
|
||||||
|
* moved unless overwrite is true.
|
||||||
|
*
|
||||||
|
* If overwrite is true, the conflicting values in dest will be discarded and
|
||||||
|
* replaced by the corresponding values from src.
|
||||||
|
*
|
||||||
|
* Therefore, with overwrite being true, the src QDict will always be empty when
|
||||||
|
* this function returns. If overwrite is false, the src QDict will be empty
|
||||||
|
* iff there were no conflicts.
|
||||||
|
*/
|
||||||
|
void qdict_join(QDict *dest, QDict *src, bool overwrite)
|
||||||
|
{
|
||||||
|
const QDictEntry *entry, *next;
|
||||||
|
|
||||||
|
entry = qdict_first(src);
|
||||||
|
while (entry) {
|
||||||
|
next = qdict_next(src, entry);
|
||||||
|
|
||||||
|
if (overwrite || !qdict_haskey(dest, entry->key)) {
|
||||||
|
qobject_incref(entry->value);
|
||||||
|
qdict_put_obj(dest, entry->key, entry->value);
|
||||||
|
qdict_del(src, entry->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -2,16 +2,19 @@
|
|||||||
# QAPI command marshaller generator
|
# QAPI command marshaller generator
|
||||||
#
|
#
|
||||||
# Copyright IBM, Corp. 2011
|
# Copyright IBM, Corp. 2011
|
||||||
|
# Copyright (C) 2014 Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# Authors:
|
# Authors:
|
||||||
# Anthony Liguori <aliguori@us.ibm.com>
|
# Anthony Liguori <aliguori@us.ibm.com>
|
||||||
# Michael Roth <mdroth@linux.vnet.ibm.com>
|
# Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||||
|
# Markus Armbruster <armbru@redhat.com>
|
||||||
#
|
#
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2.
|
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
from ordereddict import OrderedDict
|
from ordereddict import OrderedDict
|
||||||
from qapi import *
|
from qapi import *
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import getopt
|
import getopt
|
||||||
@@ -37,6 +40,15 @@ def generate_command_decl(name, args, ret_type):
|
|||||||
''',
|
''',
|
||||||
ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip()
|
ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip()
|
||||||
|
|
||||||
|
def gen_err_check(errvar):
|
||||||
|
if errvar:
|
||||||
|
return mcgen('''
|
||||||
|
if (local_err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
return ''
|
||||||
|
|
||||||
def gen_sync_call(name, args, ret_type, indent=0):
|
def gen_sync_call(name, args, ret_type, indent=0):
|
||||||
ret = ""
|
ret = ""
|
||||||
arglist=""
|
arglist=""
|
||||||
@@ -49,15 +61,14 @@ def gen_sync_call(name, args, ret_type, indent=0):
|
|||||||
arglist += "%s, " % (c_var(argname))
|
arglist += "%s, " % (c_var(argname))
|
||||||
push_indent(indent)
|
push_indent(indent)
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
%(retval)sqmp_%(name)s(%(args)serrp);
|
%(retval)sqmp_%(name)s(%(args)s&local_err);
|
||||||
|
|
||||||
''',
|
''',
|
||||||
name=c_fun(name), args=arglist, retval=retval).rstrip()
|
name=c_fun(name), args=arglist, retval=retval).rstrip()
|
||||||
if ret_type:
|
if ret_type:
|
||||||
|
ret += "\n" + gen_err_check('local_err')
|
||||||
ret += "\n" + mcgen(''''
|
ret += "\n" + mcgen(''''
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
%(marshal_output_call)s
|
%(marshal_output_call)s
|
||||||
}
|
|
||||||
''',
|
''',
|
||||||
marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip()
|
marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip()
|
||||||
pop_indent(indent)
|
pop_indent(indent)
|
||||||
@@ -67,18 +78,19 @@ if (!error_is_set(errp)) {
|
|||||||
def gen_marshal_output_call(name, ret_type):
|
def gen_marshal_output_call(name, ret_type):
|
||||||
if not ret_type:
|
if not ret_type:
|
||||||
return ""
|
return ""
|
||||||
return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name)
|
return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_fun(name)
|
||||||
|
|
||||||
def gen_visitor_input_containers_decl(args):
|
def gen_visitor_input_containers_decl(args, obj):
|
||||||
ret = ""
|
ret = ""
|
||||||
|
|
||||||
push_indent()
|
push_indent()
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
QmpInputVisitor *mi;
|
QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s);
|
||||||
QapiDeallocVisitor *md;
|
QapiDeallocVisitor *md;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
''')
|
''',
|
||||||
|
obj=obj)
|
||||||
pop_indent()
|
pop_indent()
|
||||||
|
|
||||||
return ret.rstrip()
|
return ret.rstrip()
|
||||||
@@ -99,16 +111,17 @@ bool has_%(argname)s = false;
|
|||||||
argname=c_var(argname), argtype=c_type(argtype))
|
argname=c_var(argname), argtype=c_type(argtype))
|
||||||
else:
|
else:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
%(argtype)s %(argname)s;
|
%(argtype)s %(argname)s = {0};
|
||||||
''',
|
''',
|
||||||
argname=c_var(argname), argtype=c_type(argtype))
|
argname=c_var(argname), argtype=c_type(argtype))
|
||||||
|
|
||||||
pop_indent()
|
pop_indent()
|
||||||
return ret.rstrip()
|
return ret.rstrip()
|
||||||
|
|
||||||
def gen_visitor_input_block(args, obj, dealloc=False):
|
def gen_visitor_input_block(args, dealloc=False):
|
||||||
ret = ""
|
ret = ""
|
||||||
errparg = 'errp'
|
errparg = '&local_err'
|
||||||
|
errarg = 'local_err'
|
||||||
|
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
return ret
|
return ret
|
||||||
@@ -117,44 +130,44 @@ def gen_visitor_input_block(args, obj, dealloc=False):
|
|||||||
|
|
||||||
if dealloc:
|
if dealloc:
|
||||||
errparg = 'NULL'
|
errparg = 'NULL'
|
||||||
|
errarg = None;
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
qmp_input_visitor_cleanup(mi);
|
||||||
md = qapi_dealloc_visitor_new();
|
md = qapi_dealloc_visitor_new();
|
||||||
v = qapi_dealloc_get_visitor(md);
|
v = qapi_dealloc_get_visitor(md);
|
||||||
''')
|
''')
|
||||||
else:
|
else:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
mi = qmp_input_visitor_new_strict(%(obj)s);
|
|
||||||
v = qmp_input_get_visitor(mi);
|
v = qmp_input_get_visitor(mi);
|
||||||
''',
|
''')
|
||||||
obj=obj)
|
|
||||||
|
|
||||||
for argname, argtype, optional, structured in parse_args(args):
|
for argname, argtype, optional, structured in parse_args(args):
|
||||||
if optional:
|
if optional:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
|
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
|
||||||
if (has_%(c_name)s) {
|
|
||||||
''',
|
''',
|
||||||
c_name=c_var(argname), name=argname, errp=errparg)
|
c_name=c_var(argname), name=argname, errp=errparg)
|
||||||
|
ret += gen_err_check(errarg)
|
||||||
|
ret += mcgen('''
|
||||||
|
if (has_%(c_name)s) {
|
||||||
|
''',
|
||||||
|
c_name=c_var(argname))
|
||||||
push_indent()
|
push_indent()
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
|
%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
|
||||||
''',
|
''',
|
||||||
c_name=c_var(argname), name=argname, argtype=argtype,
|
c_name=c_var(argname), name=argname, argtype=argtype,
|
||||||
visitor=type_visitor(argtype), errp=errparg)
|
visitor=type_visitor(argtype), errp=errparg)
|
||||||
|
ret += gen_err_check(errarg)
|
||||||
if optional:
|
if optional:
|
||||||
pop_indent()
|
pop_indent()
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
}
|
}
|
||||||
visit_end_optional(v, %(errp)s);
|
''')
|
||||||
''', errp=errparg)
|
|
||||||
|
|
||||||
if dealloc:
|
if dealloc:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
qapi_dealloc_visitor_cleanup(md);
|
qapi_dealloc_visitor_cleanup(md);
|
||||||
''')
|
|
||||||
else:
|
|
||||||
ret += mcgen('''
|
|
||||||
qmp_input_visitor_cleanup(mi);
|
|
||||||
''')
|
''')
|
||||||
pop_indent()
|
pop_indent()
|
||||||
return ret.rstrip()
|
return ret.rstrip()
|
||||||
@@ -166,16 +179,22 @@ def gen_marshal_output(name, args, ret_type, middle_mode):
|
|||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
|
static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
|
||||||
{
|
{
|
||||||
QapiDeallocVisitor *md = qapi_dealloc_visitor_new();
|
Error *local_err = NULL;
|
||||||
QmpOutputVisitor *mo = qmp_output_visitor_new();
|
QmpOutputVisitor *mo = qmp_output_visitor_new();
|
||||||
|
QapiDeallocVisitor *md;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
|
|
||||||
v = qmp_output_get_visitor(mo);
|
v = qmp_output_get_visitor(mo);
|
||||||
%(visitor)s(v, &ret_in, "unused", errp);
|
%(visitor)s(v, &ret_in, "unused", &local_err);
|
||||||
if (!error_is_set(errp)) {
|
if (local_err) {
|
||||||
*ret_out = qmp_output_get_qobject(mo);
|
goto out;
|
||||||
}
|
}
|
||||||
|
*ret_out = qmp_output_get_qobject(mo);
|
||||||
|
|
||||||
|
out:
|
||||||
|
error_propagate(errp, local_err);
|
||||||
qmp_output_visitor_cleanup(mo);
|
qmp_output_visitor_cleanup(mo);
|
||||||
|
md = qapi_dealloc_visitor_new();
|
||||||
v = qapi_dealloc_get_visitor(md);
|
v = qapi_dealloc_get_visitor(md);
|
||||||
%(visitor)s(v, &ret_in, "unused", NULL);
|
%(visitor)s(v, &ret_in, "unused", NULL);
|
||||||
qapi_dealloc_visitor_cleanup(md);
|
qapi_dealloc_visitor_cleanup(md);
|
||||||
@@ -200,13 +219,12 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
|
|||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
%(header)s
|
%(header)s
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
''',
|
''',
|
||||||
header=hdr)
|
header=hdr)
|
||||||
|
|
||||||
if middle_mode:
|
if middle_mode:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
Error *local_err = NULL;
|
|
||||||
Error **errp = &local_err;
|
|
||||||
QDict *args = (QDict *)qdict;
|
QDict *args = (QDict *)qdict;
|
||||||
''')
|
''')
|
||||||
|
|
||||||
@@ -228,29 +246,32 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
|
|||||||
%(visitor_input_block)s
|
%(visitor_input_block)s
|
||||||
|
|
||||||
''',
|
''',
|
||||||
visitor_input_containers_decl=gen_visitor_input_containers_decl(args),
|
visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"),
|
||||||
visitor_input_vars_decl=gen_visitor_input_vars_decl(args),
|
visitor_input_vars_decl=gen_visitor_input_vars_decl(args),
|
||||||
visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)"))
|
visitor_input_block=gen_visitor_input_block(args))
|
||||||
else:
|
else:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
|
||||||
(void)args;
|
(void)args;
|
||||||
''')
|
''')
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
if (error_is_set(errp)) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
%(sync_call)s
|
%(sync_call)s
|
||||||
''',
|
''',
|
||||||
sync_call=gen_sync_call(name, args, ret_type, indent=4))
|
sync_call=gen_sync_call(name, args, ret_type, indent=4))
|
||||||
|
if re.search('^ *goto out\\;', ret, re.MULTILINE):
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
''')
|
||||||
|
if not middle_mode:
|
||||||
|
ret += mcgen('''
|
||||||
|
error_propagate(errp, local_err);
|
||||||
''')
|
''')
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
%(visitor_input_block_cleanup)s
|
%(visitor_input_block_cleanup)s
|
||||||
''',
|
''',
|
||||||
visitor_input_block_cleanup=gen_visitor_input_block(args, None,
|
visitor_input_block_cleanup=gen_visitor_input_block(args,
|
||||||
dealloc=True))
|
dealloc=True))
|
||||||
|
|
||||||
if middle_mode:
|
if middle_mode:
|
||||||
|
@@ -2,21 +2,47 @@
|
|||||||
# QAPI visitor generator
|
# QAPI visitor generator
|
||||||
#
|
#
|
||||||
# Copyright IBM, Corp. 2011
|
# Copyright IBM, Corp. 2011
|
||||||
|
# Copyright (C) 2014 Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# Authors:
|
# Authors:
|
||||||
# Anthony Liguori <aliguori@us.ibm.com>
|
# Anthony Liguori <aliguori@us.ibm.com>
|
||||||
# Michael Roth <mdroth@linux.vnet.ibm.com>
|
# Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||||
|
# Markus Armbruster <armbru@redhat.com>
|
||||||
#
|
#
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2.
|
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
from ordereddict import OrderedDict
|
from ordereddict import OrderedDict
|
||||||
from qapi import *
|
from qapi import *
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import getopt
|
import getopt
|
||||||
import errno
|
import errno
|
||||||
|
|
||||||
|
implicit_structs = []
|
||||||
|
|
||||||
|
def generate_visit_implicit_struct(type):
|
||||||
|
global implicit_structs
|
||||||
|
if type in implicit_structs:
|
||||||
|
return ''
|
||||||
|
implicit_structs.append(type)
|
||||||
|
return mcgen('''
|
||||||
|
|
||||||
|
static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
|
||||||
|
{
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err);
|
||||||
|
if (!err) {
|
||||||
|
visit_type_%(c_type)s_fields(m, obj, errp);
|
||||||
|
visit_end_implicit_struct(m, &err);
|
||||||
|
}
|
||||||
|
error_propagate(errp, err);
|
||||||
|
}
|
||||||
|
''',
|
||||||
|
c_type=type_name(type))
|
||||||
|
|
||||||
def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
|
def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
|
||||||
substructs = []
|
substructs = []
|
||||||
ret = ''
|
ret = ''
|
||||||
@@ -35,6 +61,19 @@ def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base =
|
|||||||
nested_field_prefix = "%s%s." % (field_prefix, argname)
|
nested_field_prefix = "%s%s." % (field_prefix, argname)
|
||||||
ret += generate_visit_struct_fields(name, nested_field_prefix,
|
ret += generate_visit_struct_fields(name, nested_field_prefix,
|
||||||
nested_fn_prefix, argentry)
|
nested_fn_prefix, argentry)
|
||||||
|
ret += mcgen('''
|
||||||
|
|
||||||
|
static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp)
|
||||||
|
{
|
||||||
|
''',
|
||||||
|
name=name, full_name=full_name, c_name=c_var(argname))
|
||||||
|
ret += generate_visit_struct_body(full_name, argname, argentry)
|
||||||
|
ret += mcgen('''
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
|
||||||
|
if base:
|
||||||
|
ret += generate_visit_implicit_struct(base)
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
|
||||||
@@ -47,12 +86,9 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error *
|
|||||||
|
|
||||||
if base:
|
if base:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(type)s), &err);
|
visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err);
|
||||||
if (!err) {
|
if (err) {
|
||||||
visit_type_%(type)s_fields(m, &(*obj)->%(c_prefix)s%(c_name)s, &err);
|
goto out;
|
||||||
error_propagate(errp, err);
|
|
||||||
err = NULL;
|
|
||||||
visit_end_implicit_struct(m, &err);
|
|
||||||
}
|
}
|
||||||
''',
|
''',
|
||||||
c_prefix=c_var(field_prefix),
|
c_prefix=c_var(field_prefix),
|
||||||
@@ -61,15 +97,18 @@ if (!err) {
|
|||||||
for argname, argentry, optional, structured in parse_args(members):
|
for argname, argentry, optional, structured in parse_args(members):
|
||||||
if optional:
|
if optional:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
visit_start_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err);
|
visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err);
|
||||||
if ((*obj)->%(prefix)shas_%(c_name)s) {
|
if (!err && (*obj)->%(prefix)shas_%(c_name)s) {
|
||||||
''',
|
''',
|
||||||
c_prefix=c_var(field_prefix), prefix=field_prefix,
|
c_prefix=c_var(field_prefix), prefix=field_prefix,
|
||||||
c_name=c_var(argname), name=argname)
|
c_name=c_var(argname), name=argname)
|
||||||
push_indent()
|
push_indent()
|
||||||
|
|
||||||
if structured:
|
if structured:
|
||||||
ret += generate_visit_struct_body(full_name, argname, argentry)
|
ret += mcgen('''
|
||||||
|
visit_type_%(full_name)s_field_%(c_name)s(m, obj, &err);
|
||||||
|
''',
|
||||||
|
full_name=full_name, c_name=c_var(argname))
|
||||||
else:
|
else:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err);
|
visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err);
|
||||||
@@ -82,12 +121,20 @@ visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err);
|
|||||||
pop_indent()
|
pop_indent()
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
}
|
}
|
||||||
visit_end_optional(m, &err);
|
''')
|
||||||
|
ret += mcgen('''
|
||||||
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
''')
|
''')
|
||||||
|
|
||||||
pop_indent()
|
pop_indent()
|
||||||
|
if re.search('^ *goto out\\;', ret, re.MULTILINE):
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
|
||||||
|
out:
|
||||||
|
''')
|
||||||
|
ret += mcgen('''
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
''')
|
''')
|
||||||
@@ -96,9 +143,9 @@ visit_end_optional(m, &err);
|
|||||||
|
|
||||||
def generate_visit_struct_body(field_prefix, name, members):
|
def generate_visit_struct_body(field_prefix, name, members):
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
if (!error_is_set(errp)) {
|
Error *err = NULL;
|
||||||
|
|
||||||
''')
|
''')
|
||||||
push_indent()
|
|
||||||
|
|
||||||
if not field_prefix:
|
if not field_prefix:
|
||||||
full_name = name
|
full_name = name
|
||||||
@@ -107,14 +154,11 @@ if (!error_is_set(errp)) {
|
|||||||
|
|
||||||
if len(field_prefix):
|
if len(field_prefix):
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
Error **errp = &err; /* from outer scope */
|
|
||||||
Error *err = NULL;
|
|
||||||
visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
|
visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
|
||||||
''',
|
''',
|
||||||
name=name)
|
name=name)
|
||||||
else:
|
else:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
Error *err = NULL;
|
|
||||||
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
|
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
|
||||||
''',
|
''',
|
||||||
name=name)
|
name=name)
|
||||||
@@ -122,21 +166,14 @@ visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
|
|||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
if (!err) {
|
if (!err) {
|
||||||
if (*obj) {
|
if (*obj) {
|
||||||
visit_type_%(name)s_fields(m, obj, &err);
|
visit_type_%(name)s_fields(m, obj, errp);
|
||||||
error_propagate(errp, err);
|
|
||||||
err = NULL;
|
|
||||||
}
|
}
|
||||||
''',
|
|
||||||
name=full_name)
|
|
||||||
|
|
||||||
pop_indent()
|
|
||||||
ret += mcgen('''
|
|
||||||
/* Always call end_struct if start_struct succeeded. */
|
|
||||||
visit_end_struct(m, &err);
|
visit_end_struct(m, &err);
|
||||||
}
|
}
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
''',
|
||||||
''')
|
name=full_name)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def generate_visit_struct(expr):
|
def generate_visit_struct(expr):
|
||||||
@@ -154,9 +191,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
|
|||||||
''',
|
''',
|
||||||
name=name)
|
name=name)
|
||||||
|
|
||||||
push_indent()
|
|
||||||
ret += generate_visit_struct_body("", name, members)
|
ret += generate_visit_struct_body("", name, members)
|
||||||
pop_indent()
|
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
}
|
}
|
||||||
@@ -168,25 +203,27 @@ def generate_visit_list(name, members):
|
|||||||
|
|
||||||
void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
|
void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
GenericList *i, **prev = (GenericList **)obj;
|
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
GenericList *i, **prev;
|
||||||
|
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
visit_start_list(m, name, &err);
|
visit_start_list(m, name, &err);
|
||||||
if (!err) {
|
if (err) {
|
||||||
for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) {
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (prev = (GenericList **)obj;
|
||||||
|
!err && (i = visit_next_list(m, prev, &err)) != NULL;
|
||||||
|
prev = &i) {
|
||||||
%(name)sList *native_i = (%(name)sList *)i;
|
%(name)sList *native_i = (%(name)sList *)i;
|
||||||
visit_type_%(name)s(m, &native_i->value, NULL, &err);
|
visit_type_%(name)s(m, &native_i->value, NULL, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
err = NULL;
|
err = NULL;
|
||||||
|
|
||||||
/* Always call end_list if start_list succeeded. */
|
|
||||||
visit_end_list(m, &err);
|
visit_end_list(m, &err);
|
||||||
}
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
''',
|
''',
|
||||||
name=name)
|
name=name)
|
||||||
|
|
||||||
@@ -207,9 +244,14 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
|
|||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
|
visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
|
||||||
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
|
visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
switch ((*obj)->kind) {
|
switch ((*obj)->kind) {
|
||||||
''',
|
''',
|
||||||
name=name)
|
name=name)
|
||||||
@@ -237,10 +279,12 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
|
|||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
out_end:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
err = NULL;
|
err = NULL;
|
||||||
visit_end_implicit_struct(m, &err);
|
visit_end_implicit_struct(m, &err);
|
||||||
}
|
out:
|
||||||
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
''')
|
''')
|
||||||
|
|
||||||
@@ -277,39 +321,42 @@ def generate_visit_union(expr):
|
|||||||
del base_fields[discriminator]
|
del base_fields[discriminator]
|
||||||
ret += generate_visit_struct_fields(name, "", "", base_fields)
|
ret += generate_visit_struct_fields(name, "", "", base_fields)
|
||||||
|
|
||||||
|
if discriminator:
|
||||||
|
for key in members:
|
||||||
|
ret += generate_visit_implicit_struct(members[key])
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
|
||||||
void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
|
void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
if (!error_is_set(errp)) {
|
|
||||||
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
|
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
|
||||||
if (!err) {
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
if (*obj) {
|
if (*obj) {
|
||||||
''',
|
''',
|
||||||
name=name)
|
name=name)
|
||||||
|
|
||||||
|
|
||||||
push_indent()
|
|
||||||
push_indent()
|
|
||||||
push_indent()
|
|
||||||
|
|
||||||
if base:
|
if base:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
visit_type_%(name)s_fields(m, obj, &err);
|
visit_type_%(name)s_fields(m, obj, &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_obj;
|
||||||
|
}
|
||||||
''',
|
''',
|
||||||
name=name)
|
name=name)
|
||||||
|
|
||||||
pop_indent()
|
|
||||||
|
|
||||||
if not discriminator:
|
if not discriminator:
|
||||||
disc_key = "type"
|
disc_key = "type"
|
||||||
else:
|
else:
|
||||||
disc_key = discriminator
|
disc_key = discriminator
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err);
|
visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err);
|
||||||
if (!err) {
|
if (err) {
|
||||||
|
goto out_obj;
|
||||||
|
}
|
||||||
switch ((*obj)->kind) {
|
switch ((*obj)->kind) {
|
||||||
''',
|
''',
|
||||||
disc_type = disc_type,
|
disc_type = disc_type,
|
||||||
@@ -319,13 +366,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
|
|||||||
if not discriminator:
|
if not discriminator:
|
||||||
fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
|
fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
|
||||||
else:
|
else:
|
||||||
fmt = '''visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(c_type)s), &err);
|
fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
|
||||||
if (!err) {
|
|
||||||
visit_type_%(c_type)s_fields(m, &(*obj)->%(c_name)s, &err);
|
|
||||||
error_propagate(errp, err);
|
|
||||||
err = NULL;
|
|
||||||
visit_end_implicit_struct(m, &err);
|
|
||||||
}'''
|
|
||||||
|
|
||||||
enum_full_value = generate_enum_full_value(disc_type, key)
|
enum_full_value = generate_enum_full_value(disc_type, key)
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
@@ -341,23 +382,14 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
|
|||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
out_obj:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
err = NULL;
|
err = NULL;
|
||||||
}
|
}
|
||||||
''')
|
|
||||||
pop_indent()
|
|
||||||
ret += mcgen('''
|
|
||||||
/* Always call end_struct if start_struct succeeded. */
|
|
||||||
visit_end_struct(m, &err);
|
visit_end_struct(m, &err);
|
||||||
}
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
''')
|
|
||||||
|
|
||||||
pop_indent();
|
|
||||||
ret += mcgen('''
|
|
||||||
}
|
|
||||||
''')
|
''')
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
@@ -476,7 +508,7 @@ fdecl.write(mcgen('''
|
|||||||
/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* schema-defined QAPI visitor function
|
* schema-defined QAPI visitor functions
|
||||||
*
|
*
|
||||||
* Copyright IBM, Corp. 2011
|
* Copyright IBM, Corp. 2011
|
||||||
*
|
*
|
||||||
|
@@ -73,13 +73,18 @@ class QAPIExprError(Exception):
|
|||||||
|
|
||||||
class QAPISchema:
|
class QAPISchema:
|
||||||
|
|
||||||
def __init__(self, fp, input_relname=None, include_hist=[], parent_info=None):
|
def __init__(self, fp, input_relname=None, include_hist=[],
|
||||||
|
previously_included=[], parent_info=None):
|
||||||
|
""" include_hist is a stack used to detect inclusion cycles
|
||||||
|
previously_included is a global state used to avoid multiple
|
||||||
|
inclusions of the same file"""
|
||||||
input_fname = os.path.abspath(fp.name)
|
input_fname = os.path.abspath(fp.name)
|
||||||
if input_relname is None:
|
if input_relname is None:
|
||||||
input_relname = fp.name
|
input_relname = fp.name
|
||||||
self.input_dir = os.path.dirname(input_fname)
|
self.input_dir = os.path.dirname(input_fname)
|
||||||
self.input_file = input_relname
|
self.input_file = input_relname
|
||||||
self.include_hist = include_hist + [(input_relname, input_fname)]
|
self.include_hist = include_hist + [(input_relname, input_fname)]
|
||||||
|
previously_included.append(input_fname)
|
||||||
self.parent_info = parent_info
|
self.parent_info = parent_info
|
||||||
self.src = fp.read()
|
self.src = fp.read()
|
||||||
if self.src == '' or self.src[-1] != '\n':
|
if self.src == '' or self.src[-1] != '\n':
|
||||||
@@ -106,13 +111,16 @@ class QAPISchema:
|
|||||||
for elem in self.include_hist):
|
for elem in self.include_hist):
|
||||||
raise QAPIExprError(expr_info, "Inclusion loop for %s"
|
raise QAPIExprError(expr_info, "Inclusion loop for %s"
|
||||||
% include)
|
% include)
|
||||||
|
# skip multiple include of the same file
|
||||||
|
if include_path in previously_included:
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
fobj = open(include_path, 'r')
|
fobj = open(include_path, 'r')
|
||||||
except IOError as e:
|
except IOError, e:
|
||||||
raise QAPIExprError(expr_info,
|
raise QAPIExprError(expr_info,
|
||||||
'%s: %s' % (e.strerror, include))
|
'%s: %s' % (e.strerror, include))
|
||||||
exprs_include = QAPISchema(fobj, include,
|
exprs_include = QAPISchema(fobj, include, self.include_hist,
|
||||||
self.include_hist, expr_info)
|
previously_included, expr_info)
|
||||||
self.exprs.extend(exprs_include.exprs)
|
self.exprs.extend(exprs_include.exprs)
|
||||||
else:
|
else:
|
||||||
expr_elem = {'expr': expr,
|
expr_elem = {'expr': expr,
|
||||||
|
@@ -86,6 +86,7 @@ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
|
|||||||
int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f,
|
int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f,
|
||||||
CPUState *cpu, void *opaque);
|
CPUState *cpu, void *opaque);
|
||||||
hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
||||||
|
hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr);
|
||||||
int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
|
|
||||||
|
@@ -489,6 +489,18 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
|
|||||||
return raddr;
|
return raddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr)
|
||||||
|
{
|
||||||
|
hwaddr phys_addr;
|
||||||
|
target_ulong page;
|
||||||
|
|
||||||
|
page = vaddr & TARGET_PAGE_MASK;
|
||||||
|
phys_addr = cpu_get_phys_page_debug(cs, page);
|
||||||
|
phys_addr += (vaddr & ~TARGET_PAGE_MASK);
|
||||||
|
|
||||||
|
return phys_addr;
|
||||||
|
}
|
||||||
|
|
||||||
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
|
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
|
||||||
{
|
{
|
||||||
if (mask & PSW_MASK_WAIT) {
|
if (mask & PSW_MASK_WAIT) {
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
#include "sysemu/device_tree.h"
|
#include "sysemu/device_tree.h"
|
||||||
#include "qapi/qmp/qjson.h"
|
#include "qapi/qmp/qjson.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
|
#include "exec/gdbstub.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
/* #define DEBUG_KVM */
|
/* #define DEBUG_KVM */
|
||||||
@@ -86,6 +87,14 @@
|
|||||||
#define ICPT_CPU_STOP 0x28
|
#define ICPT_CPU_STOP 0x28
|
||||||
#define ICPT_IO 0x40
|
#define ICPT_IO 0x40
|
||||||
|
|
||||||
|
static CPUWatchpoint hw_watchpoint;
|
||||||
|
/*
|
||||||
|
* We don't use a list because this structure is also used to transmit the
|
||||||
|
* hardware breakpoints to the kernel.
|
||||||
|
*/
|
||||||
|
static struct kvm_hw_breakpoint *hw_breakpoints;
|
||||||
|
static int nb_hw_breakpoints;
|
||||||
|
|
||||||
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||||
KVM_CAP_LAST_INFO
|
KVM_CAP_LAST_INFO
|
||||||
};
|
};
|
||||||
@@ -320,12 +329,16 @@ static void *legacy_s390_alloc(size_t size)
|
|||||||
return mem == MAP_FAILED ? NULL : mem;
|
return mem == MAP_FAILED ? NULL : mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
/* DIAG 501 is used for sw breakpoints */
|
||||||
{
|
|
||||||
static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
|
static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
|
||||||
|
|
||||||
if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) ||
|
int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
||||||
cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501, 4, 1)) {
|
{
|
||||||
|
|
||||||
|
if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn,
|
||||||
|
sizeof(diag_501), 0) ||
|
||||||
|
cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501,
|
||||||
|
sizeof(diag_501), 1)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -333,38 +346,140 @@ int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
|||||||
|
|
||||||
int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
||||||
{
|
{
|
||||||
uint8_t t[4];
|
uint8_t t[sizeof(diag_501)];
|
||||||
static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
|
|
||||||
|
|
||||||
if (cpu_memory_rw_debug(cs, bp->pc, t, 4, 0)) {
|
if (cpu_memory_rw_debug(cs, bp->pc, t, sizeof(diag_501), 0)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else if (memcmp(t, diag_501, 4)) {
|
} else if (memcmp(t, diag_501, sizeof(diag_501))) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) {
|
} else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn,
|
||||||
|
sizeof(diag_501), 1)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct kvm_hw_breakpoint *find_hw_breakpoint(target_ulong addr,
|
||||||
|
int len, int type)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for (n = 0; n < nb_hw_breakpoints; n++) {
|
||||||
|
if (hw_breakpoints[n].addr == addr && hw_breakpoints[n].type == type &&
|
||||||
|
(hw_breakpoints[n].len == len || len == -1)) {
|
||||||
|
return &hw_breakpoints[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_hw_breakpoint(target_ulong addr, int len, int type)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (find_hw_breakpoint(addr, len, type)) {
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = (nb_hw_breakpoints + 1) * sizeof(struct kvm_hw_breakpoint);
|
||||||
|
|
||||||
|
if (!hw_breakpoints) {
|
||||||
|
nb_hw_breakpoints = 0;
|
||||||
|
hw_breakpoints = (struct kvm_hw_breakpoint *)g_try_malloc(size);
|
||||||
|
} else {
|
||||||
|
hw_breakpoints =
|
||||||
|
(struct kvm_hw_breakpoint *)g_try_realloc(hw_breakpoints, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hw_breakpoints) {
|
||||||
|
nb_hw_breakpoints = 0;
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
hw_breakpoints[nb_hw_breakpoints].addr = addr;
|
||||||
|
hw_breakpoints[nb_hw_breakpoints].len = len;
|
||||||
|
hw_breakpoints[nb_hw_breakpoints].type = type;
|
||||||
|
|
||||||
|
nb_hw_breakpoints++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_arch_insert_hw_breakpoint(target_ulong addr,
|
int kvm_arch_insert_hw_breakpoint(target_ulong addr,
|
||||||
target_ulong len, int type)
|
target_ulong len, int type)
|
||||||
{
|
{
|
||||||
|
switch (type) {
|
||||||
|
case GDB_BREAKPOINT_HW:
|
||||||
|
type = KVM_HW_BP;
|
||||||
|
break;
|
||||||
|
case GDB_WATCHPOINT_WRITE:
|
||||||
|
if (len < 1) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
type = KVM_HW_WP_WRITE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
return insert_hw_breakpoint(addr, len, type);
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_arch_remove_hw_breakpoint(target_ulong addr,
|
int kvm_arch_remove_hw_breakpoint(target_ulong addr,
|
||||||
target_ulong len, int type)
|
target_ulong len, int type)
|
||||||
{
|
{
|
||||||
return -ENOSYS;
|
int size;
|
||||||
|
struct kvm_hw_breakpoint *bp = find_hw_breakpoint(addr, len, type);
|
||||||
|
|
||||||
|
if (bp == NULL) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
nb_hw_breakpoints--;
|
||||||
|
if (nb_hw_breakpoints > 0) {
|
||||||
|
/*
|
||||||
|
* In order to trim the array, move the last element to the position to
|
||||||
|
* be removed - if necessary.
|
||||||
|
*/
|
||||||
|
if (bp != &hw_breakpoints[nb_hw_breakpoints]) {
|
||||||
|
*bp = hw_breakpoints[nb_hw_breakpoints];
|
||||||
|
}
|
||||||
|
size = nb_hw_breakpoints * sizeof(struct kvm_hw_breakpoint);
|
||||||
|
hw_breakpoints =
|
||||||
|
(struct kvm_hw_breakpoint *)g_realloc(hw_breakpoints, size);
|
||||||
|
} else {
|
||||||
|
g_free(hw_breakpoints);
|
||||||
|
hw_breakpoints = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arch_remove_all_hw_breakpoints(void)
|
void kvm_arch_remove_all_hw_breakpoints(void)
|
||||||
{
|
{
|
||||||
|
nb_hw_breakpoints = 0;
|
||||||
|
g_free(hw_breakpoints);
|
||||||
|
hw_breakpoints = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
|
void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (nb_hw_breakpoints > 0) {
|
||||||
|
dbg->arch.nr_hw_bp = nb_hw_breakpoints;
|
||||||
|
dbg->arch.hw_bp = hw_breakpoints;
|
||||||
|
|
||||||
|
for (i = 0; i < nb_hw_breakpoints; ++i) {
|
||||||
|
hw_breakpoints[i].phys_addr = s390_cpu_get_phys_addr_debug(cpu,
|
||||||
|
hw_breakpoints[i].addr);
|
||||||
|
}
|
||||||
|
dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
|
||||||
|
} else {
|
||||||
|
dbg->arch.nr_hw_bp = 0;
|
||||||
|
dbg->arch.hw_bp = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run)
|
void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run)
|
||||||
@@ -579,6 +694,22 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
|
|||||||
handle_diag_308(&cpu->env, r1, r3);
|
handle_diag_308(&cpu->env, r1, r3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handle_sw_breakpoint(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
unsigned long pc;
|
||||||
|
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
|
||||||
|
pc = env->psw.addr - 4;
|
||||||
|
if (kvm_find_sw_breakpoint(CPU(cpu), pc)) {
|
||||||
|
env->psw.addr = pc;
|
||||||
|
return EXCP_DEBUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
#define DIAG_KVM_CODE_MASK 0x000000000000ffff
|
#define DIAG_KVM_CODE_MASK 0x000000000000ffff
|
||||||
|
|
||||||
static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
|
static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
|
||||||
@@ -599,7 +730,7 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
|
|||||||
r = handle_hypercall(cpu, run);
|
r = handle_hypercall(cpu, run);
|
||||||
break;
|
break;
|
||||||
case DIAG_KVM_BREAKPOINT:
|
case DIAG_KVM_BREAKPOINT:
|
||||||
sleep(10);
|
r = handle_sw_breakpoint(cpu, run);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code);
|
DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code);
|
||||||
@@ -701,7 +832,7 @@ out:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
static int handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
||||||
{
|
{
|
||||||
unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00);
|
unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00);
|
||||||
uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff;
|
uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff;
|
||||||
@@ -728,8 +859,11 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
r = 0;
|
||||||
enter_pgmcheck(cpu, 0x0001);
|
enter_pgmcheck(cpu, 0x0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_special_wait_psw(CPUState *cs)
|
static bool is_special_wait_psw(CPUState *cs)
|
||||||
@@ -749,7 +883,7 @@ static int handle_intercept(S390CPU *cpu)
|
|||||||
(long)cs->kvm_run->psw_addr);
|
(long)cs->kvm_run->psw_addr);
|
||||||
switch (icpt_code) {
|
switch (icpt_code) {
|
||||||
case ICPT_INSTRUCTION:
|
case ICPT_INSTRUCTION:
|
||||||
handle_instruction(cpu, run);
|
r = handle_instruction(cpu, run);
|
||||||
break;
|
break;
|
||||||
case ICPT_WAITPSW:
|
case ICPT_WAITPSW:
|
||||||
/* disabled wait, since enabled wait is handled in kernel */
|
/* disabled wait, since enabled wait is handled in kernel */
|
||||||
@@ -830,7 +964,36 @@ static int handle_tsch(S390CPU *cpu)
|
|||||||
|
|
||||||
static int kvm_arch_handle_debug_exit(S390CPU *cpu)
|
static int kvm_arch_handle_debug_exit(S390CPU *cpu)
|
||||||
{
|
{
|
||||||
return -ENOSYS;
|
CPUState *cs = CPU(cpu);
|
||||||
|
struct kvm_run *run = cs->kvm_run;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
struct kvm_debug_exit_arch *arch_info = &run->debug.arch;
|
||||||
|
|
||||||
|
switch (arch_info->type) {
|
||||||
|
case KVM_HW_WP_WRITE:
|
||||||
|
if (find_hw_breakpoint(arch_info->addr, -1, arch_info->type)) {
|
||||||
|
cs->watchpoint_hit = &hw_watchpoint;
|
||||||
|
hw_watchpoint.vaddr = arch_info->addr;
|
||||||
|
hw_watchpoint.flags = BP_MEM_WRITE;
|
||||||
|
ret = EXCP_DEBUG;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KVM_HW_BP:
|
||||||
|
if (find_hw_breakpoint(arch_info->addr, -1, arch_info->type)) {
|
||||||
|
ret = EXCP_DEBUG;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KVM_SINGLESTEP:
|
||||||
|
if (cs->singlestep_enabled) {
|
||||||
|
ret = EXCP_DEBUG;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||||
@@ -911,6 +1074,16 @@ void kvm_s390_enable_css_support(S390CPU *cpu)
|
|||||||
|
|
||||||
void kvm_arch_init_irq_routing(KVMState *s)
|
void kvm_arch_init_irq_routing(KVMState *s)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Note that while irqchip capabilities generally imply that cpustates
|
||||||
|
* are handled in-kernel, it is not true for s390 (yet); therefore, we
|
||||||
|
* have to override the common code kvm_halt_in_kernel_allowed setting.
|
||||||
|
*/
|
||||||
|
if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) {
|
||||||
|
kvm_irqfds_allowed = true;
|
||||||
|
kvm_gsi_routing_allowed = true;
|
||||||
|
kvm_halt_in_kernel_allowed = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
|
int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
|
||||||
|
@@ -193,7 +193,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
|
|||||||
flat-union-string-discriminator.json \
|
flat-union-string-discriminator.json \
|
||||||
include-simple.json include-relpath.json include-format-err.json \
|
include-simple.json include-relpath.json include-format-err.json \
|
||||||
include-non-file.json include-no-file.json include-before-err.json \
|
include-non-file.json include-no-file.json include-before-err.json \
|
||||||
include-nested-err.json include-self-cycle.json include-cycle.json)
|
include-nested-err.json include-self-cycle.json include-cycle.json \
|
||||||
|
include-repetition.json)
|
||||||
|
|
||||||
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
|
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
|
||||||
|
|
||||||
|
@@ -444,6 +444,92 @@ static void qdict_array_split_test(void)
|
|||||||
QDECREF(test_dict);
|
QDECREF(test_dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qdict_join_test(void)
|
||||||
|
{
|
||||||
|
QDict *dict1, *dict2;
|
||||||
|
bool overwrite = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dict1 = qdict_new();
|
||||||
|
dict2 = qdict_new();
|
||||||
|
|
||||||
|
|
||||||
|
/* Test everything once without overwrite and once with */
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* Test empty dicts */
|
||||||
|
qdict_join(dict1, dict2, overwrite);
|
||||||
|
|
||||||
|
g_assert(qdict_size(dict1) == 0);
|
||||||
|
g_assert(qdict_size(dict2) == 0);
|
||||||
|
|
||||||
|
|
||||||
|
/* First iteration: Test movement */
|
||||||
|
/* Second iteration: Test empty source and non-empty destination */
|
||||||
|
qdict_put(dict2, "foo", qint_from_int(42));
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
qdict_join(dict1, dict2, overwrite);
|
||||||
|
|
||||||
|
g_assert(qdict_size(dict1) == 1);
|
||||||
|
g_assert(qdict_size(dict2) == 0);
|
||||||
|
|
||||||
|
g_assert(qdict_get_int(dict1, "foo") == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Test non-empty source and destination without conflict */
|
||||||
|
qdict_put(dict2, "bar", qint_from_int(23));
|
||||||
|
|
||||||
|
qdict_join(dict1, dict2, overwrite);
|
||||||
|
|
||||||
|
g_assert(qdict_size(dict1) == 2);
|
||||||
|
g_assert(qdict_size(dict2) == 0);
|
||||||
|
|
||||||
|
g_assert(qdict_get_int(dict1, "foo") == 42);
|
||||||
|
g_assert(qdict_get_int(dict1, "bar") == 23);
|
||||||
|
|
||||||
|
|
||||||
|
/* Test conflict */
|
||||||
|
qdict_put(dict2, "foo", qint_from_int(84));
|
||||||
|
|
||||||
|
qdict_join(dict1, dict2, overwrite);
|
||||||
|
|
||||||
|
g_assert(qdict_size(dict1) == 2);
|
||||||
|
g_assert(qdict_size(dict2) == !overwrite);
|
||||||
|
|
||||||
|
g_assert(qdict_get_int(dict1, "foo") == overwrite ? 84 : 42);
|
||||||
|
g_assert(qdict_get_int(dict1, "bar") == 23);
|
||||||
|
|
||||||
|
if (!overwrite) {
|
||||||
|
g_assert(qdict_get_int(dict2, "foo") == 84);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check the references */
|
||||||
|
g_assert(qdict_get(dict1, "foo")->refcnt == 1);
|
||||||
|
g_assert(qdict_get(dict1, "bar")->refcnt == 1);
|
||||||
|
|
||||||
|
if (!overwrite) {
|
||||||
|
g_assert(qdict_get(dict2, "foo")->refcnt == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Clean up */
|
||||||
|
qdict_del(dict1, "foo");
|
||||||
|
qdict_del(dict1, "bar");
|
||||||
|
|
||||||
|
if (!overwrite) {
|
||||||
|
qdict_del(dict2, "foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (overwrite ^= true);
|
||||||
|
|
||||||
|
|
||||||
|
QDECREF(dict1);
|
||||||
|
QDECREF(dict2);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Errors test-cases
|
* Errors test-cases
|
||||||
*/
|
*/
|
||||||
@@ -584,6 +670,7 @@ int main(int argc, char **argv)
|
|||||||
g_test_add_func("/public/iterapi", qdict_iterapi_test);
|
g_test_add_func("/public/iterapi", qdict_iterapi_test);
|
||||||
g_test_add_func("/public/flatten", qdict_flatten_test);
|
g_test_add_func("/public/flatten", qdict_flatten_test);
|
||||||
g_test_add_func("/public/array_split", qdict_array_split_test);
|
g_test_add_func("/public/array_split", qdict_array_split_test);
|
||||||
|
g_test_add_func("/public/join", qdict_join_test);
|
||||||
|
|
||||||
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
|
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
|
||||||
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
|
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
|
||||||
|
2
tests/qapi-schema/include-repetition-sub.json
Normal file
2
tests/qapi-schema/include-repetition-sub.json
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
{ 'include': 'comments.json' }
|
||||||
|
{ 'include': 'comments.json' }
|
0
tests/qapi-schema/include-repetition.err
Normal file
0
tests/qapi-schema/include-repetition.err
Normal file
1
tests/qapi-schema/include-repetition.exit
Normal file
1
tests/qapi-schema/include-repetition.exit
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0
|
3
tests/qapi-schema/include-repetition.json
Normal file
3
tests/qapi-schema/include-repetition.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{ 'include': 'comments.json' }
|
||||||
|
{ 'include': 'include-repetition-sub.json' }
|
||||||
|
{ 'include': 'comments.json' }
|
3
tests/qapi-schema/include-repetition.out
Normal file
3
tests/qapi-schema/include-repetition.out
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
|
||||||
|
[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
|
||||||
|
[]
|
@@ -35,7 +35,7 @@ class TestSingleDrive(iotests.QMPTestCase):
|
|||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
|
||||||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
|
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
|
||||||
qemu_io('-c', 'write -P 0x1 0 512', backing_img)
|
qemu_io('-c', 'write -P 0x1 0 512', backing_img)
|
||||||
self.vm = iotests.VM().add_drive(test_img)
|
self.vm = iotests.VM().add_drive("blkdebug::" + test_img)
|
||||||
self.vm.launch()
|
self.vm.launch()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
@@ -47,6 +47,11 @@ _supported_os Linux
|
|||||||
_default_cache_mode "writethrough"
|
_default_cache_mode "writethrough"
|
||||||
_supported_cache_modes "writethrough"
|
_supported_cache_modes "writethrough"
|
||||||
|
|
||||||
|
_no_dump_exec()
|
||||||
|
{
|
||||||
|
(ulimit -c 0; exec "$@")
|
||||||
|
}
|
||||||
|
|
||||||
size=128M
|
size=128M
|
||||||
|
|
||||||
echo
|
echo
|
||||||
@@ -67,10 +72,7 @@ echo "== Creating a dirty image file =="
|
|||||||
IMGOPTS="compat=1.1,lazy_refcounts=on"
|
IMGOPTS="compat=1.1,lazy_refcounts=on"
|
||||||
_make_test_img $size
|
_make_test_img $size
|
||||||
|
|
||||||
old_ulimit=$(ulimit -c)
|
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
|
||||||
ulimit -c 0 # do not produce a core dump on abort(3)
|
|
||||||
$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
|
|
||||||
ulimit -c "$old_ulimit"
|
|
||||||
|
|
||||||
# The dirty bit must be set
|
# The dirty bit must be set
|
||||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
@@ -103,10 +105,7 @@ echo "== Opening a dirty image read/write should repair it =="
|
|||||||
IMGOPTS="compat=1.1,lazy_refcounts=on"
|
IMGOPTS="compat=1.1,lazy_refcounts=on"
|
||||||
_make_test_img $size
|
_make_test_img $size
|
||||||
|
|
||||||
old_ulimit=$(ulimit -c)
|
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
|
||||||
ulimit -c 0 # do not produce a core dump on abort(3)
|
|
||||||
$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
|
|
||||||
ulimit -c "$old_ulimit"
|
|
||||||
|
|
||||||
# The dirty bit must be set
|
# The dirty bit must be set
|
||||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
@@ -122,10 +121,7 @@ echo "== Creating an image file with lazy_refcounts=off =="
|
|||||||
IMGOPTS="compat=1.1,lazy_refcounts=off"
|
IMGOPTS="compat=1.1,lazy_refcounts=off"
|
||||||
_make_test_img $size
|
_make_test_img $size
|
||||||
|
|
||||||
old_ulimit=$(ulimit -c)
|
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
|
||||||
ulimit -c 0 # do not produce a core dump on abort(3)
|
|
||||||
$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io
|
|
||||||
ulimit -c "$old_ulimit"
|
|
||||||
|
|
||||||
# The dirty bit must not be set since lazy_refcounts=off
|
# The dirty bit must not be set since lazy_refcounts=off
|
||||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
@@ -11,6 +11,7 @@ No errors were found on the image.
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
./039: Aborted ( ulimit -c 0; exec "$@" )
|
||||||
incompatible_features 0x1
|
incompatible_features 0x1
|
||||||
ERROR cluster 5 refcount=0 reference=1
|
ERROR cluster 5 refcount=0 reference=1
|
||||||
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
|
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
|
||||||
@@ -42,6 +43,7 @@ read 512/512 bytes at offset 0
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
./039: Aborted ( ulimit -c 0; exec "$@" )
|
||||||
incompatible_features 0x1
|
incompatible_features 0x1
|
||||||
Repairing cluster 5 refcount=0 reference=1
|
Repairing cluster 5 refcount=0 reference=1
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
@@ -52,6 +54,7 @@ incompatible_features 0x0
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
./039: Aborted ( ulimit -c 0; exec "$@" )
|
||||||
incompatible_features 0x0
|
incompatible_features 0x0
|
||||||
No errors were found on the image.
|
No errors were found on the image.
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
|||||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0
|
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0
|
||||||
QMP_VERSION
|
QMP_VERSION
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
|
||||||
@@ -24,7 +24,7 @@ QMP_VERSION
|
|||||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
|
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
|
||||||
QMP_VERSION
|
QMP_VERSION
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
@@ -44,7 +44,7 @@ Testing:
|
|||||||
QMP_VERSION
|
QMP_VERSION
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": "OK\r\n"}
|
{"return": "OK\r\n"}
|
||||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
@@ -64,14 +64,14 @@ Testing:
|
|||||||
QMP_VERSION
|
QMP_VERSION
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
|
||||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
|
||||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
|
||||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||||
|
@@ -72,6 +72,13 @@ echo "=== Verify open image read-only succeeds after log replay ==="
|
|||||||
$QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | _filter_testdir \
|
$QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | _filter_testdir \
|
||||||
| _filter_qemu_io
|
| _filter_qemu_io
|
||||||
|
|
||||||
|
_cleanup_test_img
|
||||||
|
_use_sample_img test-disk2vhd.vhdx.bz2
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Verify image created by Disk2VHD can be opened ==="
|
||||||
|
$QEMU_IMG info "$TEST_IMG" 2>&1 | _filter_testdir | _filter_qemu
|
||||||
|
|
||||||
# success, all done
|
# success, all done
|
||||||
echo "*** done"
|
echo "*** done"
|
||||||
rm -f $seq.full
|
rm -f $seq.full
|
||||||
|
@@ -18,4 +18,11 @@ No errors were found on the image.
|
|||||||
=== Verify open image read-only succeeds after log replay ===
|
=== Verify open image read-only succeeds after log replay ===
|
||||||
read 18874368/18874368 bytes at offset 0
|
read 18874368/18874368 bytes at offset 0
|
||||||
18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
=== Verify image created by Disk2VHD can be opened ===
|
||||||
|
image: TEST_DIR/test-disk2vhd.vhdx
|
||||||
|
file format: vhdx
|
||||||
|
virtual size: 256M (268435456 bytes)
|
||||||
|
disk size: 260M
|
||||||
|
cluster_size: 2097152
|
||||||
*** done
|
*** done
|
||||||
|
130
tests/qemu-iotests/089
Executable file
130
tests/qemu-iotests/089
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test case for support of JSON filenames
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=mreitz@redhat.com
|
||||||
|
|
||||||
|
seq="$(basename $0)"
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here="$PWD"
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
|
||||||
|
_supported_fmt qcow2
|
||||||
|
_supported_proto file
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
# Using an image filename containing quotation marks will render the JSON data
|
||||||
|
# below invalid. In that case, we have little choice but simply not to run this
|
||||||
|
# test.
|
||||||
|
case $TEST_IMG in
|
||||||
|
*'"'*)
|
||||||
|
_notrun "image filename may not contain quotation marks"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
IMG_SIZE=64M
|
||||||
|
|
||||||
|
# Taken from test 072
|
||||||
|
echo
|
||||||
|
echo "=== Testing nested image formats ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
|
||||||
|
|
||||||
|
$QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \
|
||||||
|
-c 'write -P 66 1024 512' "$TEST_IMG.base" | _filter_qemu_io
|
||||||
|
|
||||||
|
$QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG"
|
||||||
|
|
||||||
|
$QEMU_IO -c 'read -P 42 0 512' -c 'read -P 23 512 512' \
|
||||||
|
-c 'read -P 66 1024 512' "json:{
|
||||||
|
\"driver\": \"$IMGFMT\",
|
||||||
|
\"file\": {
|
||||||
|
\"driver\": \"$IMGFMT\",
|
||||||
|
\"file\": {
|
||||||
|
\"filename\": \"$TEST_IMG\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}" | _filter_qemu_io
|
||||||
|
|
||||||
|
# This should fail (see test 072)
|
||||||
|
$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
|
||||||
|
# Taken from test 071
|
||||||
|
echo
|
||||||
|
echo "=== Testing blkdebug ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
_make_test_img $IMG_SIZE
|
||||||
|
|
||||||
|
$QEMU_IO -c 'write -P 42 0x38000 512' "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
# The "image.filename" part tests whether "a": { "b": "c" } and "a.b": "c" do
|
||||||
|
# the same (which they should).
|
||||||
|
$QEMU_IO -c 'read -P 42 0x38000 512' "json:{
|
||||||
|
\"driver\": \"$IMGFMT\",
|
||||||
|
\"file\": {
|
||||||
|
\"driver\": \"blkdebug\",
|
||||||
|
\"inject-error\": [{
|
||||||
|
\"event\": \"l2_load\"
|
||||||
|
}],
|
||||||
|
\"image.filename\": \"$TEST_IMG\"
|
||||||
|
}
|
||||||
|
}" | _filter_qemu_io
|
||||||
|
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing qemu-img info output ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
$QEMU_IMG info "json:{\"driver\":\"qcow2\",\"file.filename\":\"$TEST_IMG\"}" \
|
||||||
|
| _filter_testdir | _filter_imgfmt
|
||||||
|
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing option merging ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Both options given directly and those given in the filename should be used
|
||||||
|
$QEMU_IO -c "open -o driver=qcow2 json:{\"file.filename\":\"$TEST_IMG\"}" \
|
||||||
|
-c "info" 2>&1 | _filter_testdir | _filter_imgfmt
|
||||||
|
|
||||||
|
# Options given directly should be prioritized over those given in the filename
|
||||||
|
$QEMU_IO -c "open -o driver=qcow2 json:{\"driver\":\"raw\",\"file.filename\":\"$TEST_IMG\"}" \
|
||||||
|
-c "info" 2>&1 | _filter_testdir | _filter_imgfmt
|
||||||
|
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
54
tests/qemu-iotests/089.out
Normal file
54
tests/qemu-iotests/089.out
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
QA output created by 089
|
||||||
|
|
||||||
|
=== Testing nested image formats ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 512/512 bytes at offset 512
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 512/512 bytes at offset 1024
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 512
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 512/512 bytes at offset 1024
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
Pattern verification failed at offset 0, 512 bytes
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
=== Testing blkdebug ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
wrote 512/512 bytes at offset 229376
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read failed: Input/output error
|
||||||
|
|
||||||
|
=== Testing qemu-img info output ===
|
||||||
|
|
||||||
|
image: TEST_DIR/t.IMGFMT
|
||||||
|
file format: IMGFMT
|
||||||
|
virtual size: 64M (67108864 bytes)
|
||||||
|
disk size: 324K
|
||||||
|
cluster_size: 65536
|
||||||
|
Format specific information:
|
||||||
|
compat: 1.1
|
||||||
|
lazy refcounts: false
|
||||||
|
|
||||||
|
=== Testing option merging ===
|
||||||
|
|
||||||
|
format name: IMGFMT
|
||||||
|
cluster size: 64 KiB
|
||||||
|
vm state offset: 512 MiB
|
||||||
|
Format specific information:
|
||||||
|
compat: 1.1
|
||||||
|
lazy refcounts: false
|
||||||
|
format name: IMGFMT
|
||||||
|
cluster size: 64 KiB
|
||||||
|
vm state offset: 512 MiB
|
||||||
|
Format specific information:
|
||||||
|
compat: 1.1
|
||||||
|
lazy refcounts: false
|
||||||
|
*** done
|
98
tests/qemu-iotests/092
Executable file
98
tests/qemu-iotests/092
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# qcow1 format input validation tests
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=kwolf@redhat.com
|
||||||
|
|
||||||
|
seq=`basename $0`
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here=`pwd`
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
rm -f $TEST_IMG.snap
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
|
||||||
|
_supported_fmt qcow
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
offset_backing_file_offset=8
|
||||||
|
offset_backing_file_size=16
|
||||||
|
offset_size=24
|
||||||
|
offset_cluster_bits=32
|
||||||
|
offset_l2_bits=33
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "== Invalid cluster size =="
|
||||||
|
_make_test_img 64M
|
||||||
|
poke_file "$TEST_IMG" "$offset_cluster_bits" "\xff"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
poke_file "$TEST_IMG" "$offset_cluster_bits" "\x1f"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
poke_file "$TEST_IMG" "$offset_cluster_bits" "\x08"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
poke_file "$TEST_IMG" "$offset_cluster_bits" "\x11"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "== Invalid L2 table size =="
|
||||||
|
_make_test_img 64M
|
||||||
|
poke_file "$TEST_IMG" "$offset_l2_bits" "\xff"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
poke_file "$TEST_IMG" "$offset_l2_bits" "\x05"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
poke_file "$TEST_IMG" "$offset_l2_bits" "\x0e"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
|
||||||
|
# 1 << 0x1b = 2^31 / L2_CACHE_SIZE
|
||||||
|
poke_file "$TEST_IMG" "$offset_l2_bits" "\x1b"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "== Invalid size =="
|
||||||
|
_make_test_img 64M
|
||||||
|
poke_file "$TEST_IMG" "$offset_size" "\xee\xee\xee\xee\xee\xee\xee\xee"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
poke_file "$TEST_IMG" "$offset_size" "\x7f\xff\xff\xff\xff\xff\xff\xff"
|
||||||
|
{ $QEMU_IO -c "write 0 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "== Invalid backing file length =="
|
||||||
|
_make_test_img 64M
|
||||||
|
poke_file "$TEST_IMG" "$offset_backing_file_offset" "\x00\x00\x00\xff"
|
||||||
|
poke_file "$TEST_IMG" "$offset_backing_file_size" "\xff\xff\xff\xff"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
poke_file "$TEST_IMG" "$offset_backing_file_size" "\x7f\xff\xff\xff"
|
||||||
|
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
38
tests/qemu-iotests/092.out
Normal file
38
tests/qemu-iotests/092.out
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
QA output created by 092
|
||||||
|
|
||||||
|
== Invalid cluster size ==
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k
|
||||||
|
no file open, try 'help open'
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k
|
||||||
|
no file open, try 'help open'
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k
|
||||||
|
no file open, try 'help open'
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k
|
||||||
|
no file open, try 'help open'
|
||||||
|
|
||||||
|
== Invalid L2 table size ==
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k
|
||||||
|
no file open, try 'help open'
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k
|
||||||
|
no file open, try 'help open'
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k
|
||||||
|
no file open, try 'help open'
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k
|
||||||
|
no file open, try 'help open'
|
||||||
|
|
||||||
|
== Invalid size ==
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: Image too large
|
||||||
|
no file open, try 'help open'
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: Image too large
|
||||||
|
no file open, try 'help open'
|
||||||
|
|
||||||
|
== Invalid backing file length ==
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long
|
||||||
|
no file open, try 'help open'
|
||||||
|
qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long
|
||||||
|
no file open, try 'help open'
|
||||||
|
*** done
|
@@ -150,6 +150,7 @@ _filter_win32()
|
|||||||
_filter_qemu_io()
|
_filter_qemu_io()
|
||||||
{
|
{
|
||||||
_filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \
|
_filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \
|
||||||
|
-e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\)/:\1/" \
|
||||||
-e "s/qemu-io> //g"
|
-e "s/qemu-io> //g"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -95,5 +95,7 @@
|
|||||||
086 rw auto quick
|
086 rw auto quick
|
||||||
087 rw auto
|
087 rw auto
|
||||||
088 rw auto
|
088 rw auto
|
||||||
|
089 rw auto quick
|
||||||
090 rw auto quick
|
090 rw auto quick
|
||||||
091 rw auto
|
091 rw auto
|
||||||
|
092 rw auto quick
|
||||||
|
BIN
tests/qemu-iotests/sample_images/test-disk2vhd.vhdx.bz2
Normal file
BIN
tests/qemu-iotests/sample_images/test-disk2vhd.vhdx.bz2
Normal file
Binary file not shown.
@@ -72,14 +72,30 @@ typedef struct TestStruct
|
|||||||
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
|
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
|
||||||
const char *name, Error **errp)
|
const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
|
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
|
||||||
errp);
|
&err);
|
||||||
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
visit_type_int(v, &(*obj)->integer, "integer", errp);
|
visit_type_int(v, &(*obj)->integer, "integer", &err);
|
||||||
visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
|
if (err) {
|
||||||
visit_type_str(v, &(*obj)->string, "string", errp);
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_str(v, &(*obj)->string, "string", &err);
|
||||||
|
|
||||||
visit_end_struct(v, errp);
|
out_end:
|
||||||
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
|
visit_end_struct(v, &err);
|
||||||
|
out:
|
||||||
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_validate_struct(TestInputVisitorData *data,
|
static void test_validate_struct(TestInputVisitorData *data,
|
||||||
|
@@ -199,16 +199,24 @@ static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
|
|||||||
|
|
||||||
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
|
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
|
||||||
&err);
|
&err);
|
||||||
if (!err) {
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
visit_type_int(v, &(*obj)->integer, "integer", &err);
|
visit_type_int(v, &(*obj)->integer, "integer", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
|
visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
visit_type_str(v, &(*obj)->string, "string", &err);
|
visit_type_str(v, &(*obj)->string, "string", &err);
|
||||||
|
|
||||||
/* Always call end_struct if start_struct succeeded. */
|
out_end:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
err = NULL;
|
err = NULL;
|
||||||
visit_end_struct(v, &err);
|
visit_end_struct(v, &err);
|
||||||
}
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -176,14 +176,30 @@ typedef struct TestStruct
|
|||||||
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
|
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
|
||||||
const char *name, Error **errp)
|
const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
|
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
|
||||||
errp);
|
&err);
|
||||||
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
visit_type_int(v, &(*obj)->integer, "integer", errp);
|
visit_type_int(v, &(*obj)->integer, "integer", &err);
|
||||||
visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
|
if (err) {
|
||||||
visit_type_str(v, &(*obj)->string, "string", errp);
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_str(v, &(*obj)->string, "string", &err);
|
||||||
|
|
||||||
visit_end_struct(v, errp);
|
out_end:
|
||||||
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
|
visit_end_struct(v, &err);
|
||||||
|
out:
|
||||||
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_visitor_out_struct(TestOutputVisitorData *data,
|
static void test_visitor_out_struct(TestOutputVisitorData *data,
|
||||||
|
@@ -195,13 +195,29 @@ typedef struct TestStruct
|
|||||||
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
|
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
|
||||||
const char *name, Error **errp)
|
const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), errp);
|
Error *err = NULL;
|
||||||
|
|
||||||
visit_type_int(v, &(*obj)->integer, "integer", errp);
|
visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err);
|
||||||
visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
|
if (err) {
|
||||||
visit_type_str(v, &(*obj)->string, "string", errp);
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
visit_end_struct(v, errp);
|
visit_type_int(v, &(*obj)->integer, "integer", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
|
||||||
|
if (err) {
|
||||||
|
goto out_end;
|
||||||
|
}
|
||||||
|
visit_type_str(v, &(*obj)->string, "string", &err);
|
||||||
|
|
||||||
|
out_end:
|
||||||
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
|
visit_end_struct(v, &err);
|
||||||
|
out:
|
||||||
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TestStruct *struct_create(void)
|
static TestStruct *struct_create(void)
|
||||||
|
@@ -688,17 +688,21 @@ megasas_dcmd_ld_get_list(int cmd, int num, int max) "scmd %d: DCMD LD get list:
|
|||||||
megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: DCMD LD get info for dev %d"
|
megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: DCMD LD get info for dev %d"
|
||||||
megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d"
|
megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d"
|
||||||
megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x"
|
megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x"
|
||||||
|
megasas_dcmd_ld_list_query(int cmd, int flags) "scmd %d: DCMD LD list query flags %x"
|
||||||
megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld"
|
megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld"
|
||||||
megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: aborting frame %x"
|
megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: aborting frame %x"
|
||||||
megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 ""
|
megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 ""
|
||||||
megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x"
|
megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x"
|
||||||
megasas_reset(void) "Reset"
|
megasas_reset(void) "Reset"
|
||||||
megasas_init(int sges, int cmds, const char *intr, const char *mode) "Using %d sges, %d cmds, %s, %s mode"
|
megasas_init(int sges, int cmds, const char *mode) "Using %d sges, %d cmds, %s mode"
|
||||||
megasas_msix_raise(int vector) "vector %d"
|
megasas_msix_raise(int vector) "vector %d"
|
||||||
|
megasas_msi_raise(int vector) "vector %d"
|
||||||
megasas_irq_lower(void) "INTx"
|
megasas_irq_lower(void) "INTx"
|
||||||
megasas_irq_raise(void) "INTx"
|
megasas_irq_raise(void) "INTx"
|
||||||
megasas_intr_enabled(void) "Interrupts enabled"
|
megasas_intr_enabled(void) "Interrupts enabled"
|
||||||
megasas_intr_disabled(void) "Interrupts disabled"
|
megasas_intr_disabled(void) "Interrupts disabled"
|
||||||
|
megasas_msix_enabled(int vector) "vector %d"
|
||||||
|
megasas_msi_enabled(int vector) "vector %d"
|
||||||
megasas_mmio_readl(unsigned long addr, uint32_t val) "addr 0x%lx: 0x%x"
|
megasas_mmio_readl(unsigned long addr, uint32_t val) "addr 0x%lx: 0x%x"
|
||||||
megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx"
|
megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx"
|
||||||
megasas_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x"
|
megasas_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x"
|
||||||
@@ -1046,7 +1050,7 @@ gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d"
|
|||||||
gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)"
|
gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)"
|
||||||
|
|
||||||
# ui/input.c
|
# ui/input.c
|
||||||
input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%x, down %d"
|
input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d"
|
||||||
input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d"
|
input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d"
|
||||||
input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d"
|
input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d"
|
||||||
input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d"
|
input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d"
|
||||||
|
@@ -288,8 +288,8 @@ static void curses_refresh(DisplayChangeListener *dcl)
|
|||||||
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
|
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_input_event_send_key_number(NULL, keycode, true);
|
qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
|
||||||
qemu_input_event_send_key_number(NULL, keycode, false);
|
qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
|
||||||
|
|
||||||
if (keycode & ALTGR) {
|
if (keycode & ALTGR) {
|
||||||
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
|
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
|
||||||
|
@@ -13,6 +13,8 @@ static const int qcode_to_number[] = {
|
|||||||
[Q_KEY_CODE_CTRL] = 0x1d,
|
[Q_KEY_CODE_CTRL] = 0x1d,
|
||||||
[Q_KEY_CODE_CTRL_R] = 0x9d,
|
[Q_KEY_CODE_CTRL_R] = 0x9d,
|
||||||
|
|
||||||
|
[Q_KEY_CODE_META_L] = 0xdb,
|
||||||
|
[Q_KEY_CODE_META_R] = 0xdc,
|
||||||
[Q_KEY_CODE_MENU] = 0xdd,
|
[Q_KEY_CODE_MENU] = 0xdd,
|
||||||
|
|
||||||
[Q_KEY_CODE_ESC] = 0x01,
|
[Q_KEY_CODE_ESC] = 0x01,
|
||||||
@@ -129,7 +131,7 @@ static const int qcode_to_number[] = {
|
|||||||
[Q_KEY_CODE_MAX] = 0,
|
[Q_KEY_CODE_MAX] = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int number_to_qcode[0xff];
|
static int number_to_qcode[0x100];
|
||||||
|
|
||||||
int qemu_input_key_value_to_number(const KeyValue *value)
|
int qemu_input_key_value_to_number(const KeyValue *value)
|
||||||
{
|
{
|
||||||
@@ -141,7 +143,7 @@ int qemu_input_key_value_to_number(const KeyValue *value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int qemu_input_key_value_to_qcode(const KeyValue *value)
|
int qemu_input_key_number_to_qcode(uint8_t nr)
|
||||||
{
|
{
|
||||||
static int first = true;
|
static int first = true;
|
||||||
|
|
||||||
@@ -155,11 +157,16 @@ int qemu_input_key_value_to_qcode(const KeyValue *value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return number_to_qcode[nr];
|
||||||
|
}
|
||||||
|
|
||||||
|
int qemu_input_key_value_to_qcode(const KeyValue *value)
|
||||||
|
{
|
||||||
if (value->kind == KEY_VALUE_KIND_QCODE) {
|
if (value->kind == KEY_VALUE_KIND_QCODE) {
|
||||||
return value->qcode;
|
return value->qcode;
|
||||||
} else {
|
} else {
|
||||||
assert(value->kind == KEY_VALUE_KIND_NUMBER);
|
assert(value->kind == KEY_VALUE_KIND_NUMBER);
|
||||||
return number_to_qcode[value->number];
|
return qemu_input_key_number_to_qcode(value->number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
49
ui/input.c
49
ui/input.c
@@ -1,3 +1,4 @@
|
|||||||
|
#include "hw/qdev.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qapi-types.h"
|
#include "qapi-types.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
@@ -10,6 +11,7 @@ struct QemuInputHandlerState {
|
|||||||
QemuInputHandler *handler;
|
QemuInputHandler *handler;
|
||||||
int id;
|
int id;
|
||||||
int events;
|
int events;
|
||||||
|
QemuConsole *con;
|
||||||
QTAILQ_ENTRY(QemuInputHandlerState) node;
|
QTAILQ_ENTRY(QemuInputHandlerState) node;
|
||||||
};
|
};
|
||||||
static QTAILQ_HEAD(, QemuInputHandlerState) handlers =
|
static QTAILQ_HEAD(, QemuInputHandlerState) handlers =
|
||||||
@@ -53,12 +55,46 @@ void qemu_input_handler_unregister(QemuInputHandlerState *s)
|
|||||||
qemu_input_check_mode_change();
|
qemu_input_check_mode_change();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qemu_input_handler_bind(QemuInputHandlerState *s,
|
||||||
|
const char *device_id, int head,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
DeviceState *dev;
|
||||||
|
QemuConsole *con;
|
||||||
|
|
||||||
|
dev = qdev_find_recursive(sysbus_get_default(), device_id);
|
||||||
|
if (dev == NULL) {
|
||||||
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
con = qemu_console_lookup_by_device(dev, head);
|
||||||
|
if (con == NULL) {
|
||||||
|
error_setg(errp, "Device %s is not bound to a QemuConsole", device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->con = con;
|
||||||
|
}
|
||||||
|
|
||||||
static QemuInputHandlerState*
|
static QemuInputHandlerState*
|
||||||
qemu_input_find_handler(uint32_t mask)
|
qemu_input_find_handler(uint32_t mask, QemuConsole *con)
|
||||||
{
|
{
|
||||||
QemuInputHandlerState *s;
|
QemuInputHandlerState *s;
|
||||||
|
|
||||||
QTAILQ_FOREACH(s, &handlers, node) {
|
QTAILQ_FOREACH(s, &handlers, node) {
|
||||||
|
if (s->con == NULL || s->con != con) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mask & s->handler->mask) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(s, &handlers, node) {
|
||||||
|
if (s->con != NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (mask & s->handler->mask) {
|
if (mask & s->handler->mask) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -94,7 +130,7 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt)
|
|||||||
static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
|
static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
int idx = -1;
|
int qcode, idx = -1;
|
||||||
|
|
||||||
if (src) {
|
if (src) {
|
||||||
idx = qemu_console_get_index(src);
|
idx = qemu_console_get_index(src);
|
||||||
@@ -103,8 +139,10 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
|
|||||||
case INPUT_EVENT_KIND_KEY:
|
case INPUT_EVENT_KIND_KEY:
|
||||||
switch (evt->key->key->kind) {
|
switch (evt->key->key->kind) {
|
||||||
case KEY_VALUE_KIND_NUMBER:
|
case KEY_VALUE_KIND_NUMBER:
|
||||||
|
qcode = qemu_input_key_number_to_qcode(evt->key->key->number);
|
||||||
|
name = QKeyCode_lookup[qcode];
|
||||||
trace_input_event_key_number(idx, evt->key->key->number,
|
trace_input_event_key_number(idx, evt->key->key->number,
|
||||||
evt->key->down);
|
name, evt->key->down);
|
||||||
break;
|
break;
|
||||||
case KEY_VALUE_KIND_QCODE:
|
case KEY_VALUE_KIND_QCODE:
|
||||||
name = QKeyCode_lookup[evt->key->key->qcode];
|
name = QKeyCode_lookup[evt->key->key->qcode];
|
||||||
@@ -149,7 +187,7 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* send event */
|
/* send event */
|
||||||
s = qemu_input_find_handler(1 << evt->kind);
|
s = qemu_input_find_handler(1 << evt->kind, src);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -250,7 +288,8 @@ bool qemu_input_is_absolute(void)
|
|||||||
{
|
{
|
||||||
QemuInputHandlerState *s;
|
QemuInputHandlerState *s;
|
||||||
|
|
||||||
s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS);
|
s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS,
|
||||||
|
NULL);
|
||||||
return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
|
return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
ui/sdl2.c
21
ui/sdl2.c
@@ -190,30 +190,33 @@ static void sdl_switch(DisplayChangeListener *dcl,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset_keys(void)
|
static void reset_keys(struct sdl2_state *scon)
|
||||||
{
|
{
|
||||||
|
QemuConsole *con = scon ? scon->dcl.con : NULL;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
if (modifiers_state[i]) {
|
if (modifiers_state[i]) {
|
||||||
int qcode = sdl2_scancode_to_qcode[i];
|
int qcode = sdl2_scancode_to_qcode[i];
|
||||||
qemu_input_event_send_key_qcode(NULL, qcode, false);
|
qemu_input_event_send_key_qcode(con, qcode, false);
|
||||||
modifiers_state[i] = 0;
|
modifiers_state[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdl_process_key(SDL_KeyboardEvent *ev)
|
static void sdl_process_key(struct sdl2_state *scon,
|
||||||
|
SDL_KeyboardEvent *ev)
|
||||||
{
|
{
|
||||||
int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
|
int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
|
||||||
|
QemuConsole *con = scon ? scon->dcl.con : NULL;
|
||||||
|
|
||||||
switch (ev->keysym.scancode) {
|
switch (ev->keysym.scancode) {
|
||||||
#if 0
|
#if 0
|
||||||
case SDL_SCANCODE_NUMLOCKCLEAR:
|
case SDL_SCANCODE_NUMLOCKCLEAR:
|
||||||
case SDL_SCANCODE_CAPSLOCK:
|
case SDL_SCANCODE_CAPSLOCK:
|
||||||
/* SDL does not send the key up event, so we generate it */
|
/* SDL does not send the key up event, so we generate it */
|
||||||
qemu_input_event_send_key_qcode(NULL, qcode, true);
|
qemu_input_event_send_key_qcode(con, qcode, true);
|
||||||
qemu_input_event_send_key_qcode(NULL, qcode, false);
|
qemu_input_event_send_key_qcode(con, qcode, false);
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
case SDL_SCANCODE_LCTRL:
|
case SDL_SCANCODE_LCTRL:
|
||||||
@@ -231,7 +234,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev)
|
|||||||
}
|
}
|
||||||
/* fall though */
|
/* fall though */
|
||||||
default:
|
default:
|
||||||
qemu_input_event_send_key_qcode(NULL, qcode,
|
qemu_input_event_send_key_qcode(con, qcode,
|
||||||
ev->type == SDL_KEYDOWN);
|
ev->type == SDL_KEYDOWN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -506,7 +509,7 @@ static void handle_keydown(SDL_Event *ev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!gui_keysym) {
|
if (!gui_keysym) {
|
||||||
sdl_process_key(&ev->key);
|
sdl_process_key(scon, &ev->key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,13 +534,13 @@ static void handle_keyup(SDL_Event *ev)
|
|||||||
}
|
}
|
||||||
/* SDL does not send back all the modifiers key, so we must
|
/* SDL does not send back all the modifiers key, so we must
|
||||||
* correct it. */
|
* correct it. */
|
||||||
reset_keys();
|
reset_keys(scon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gui_keysym = 0;
|
gui_keysym = 0;
|
||||||
}
|
}
|
||||||
if (!gui_keysym) {
|
if (!gui_keysym) {
|
||||||
sdl_process_key(&ev->key);
|
sdl_process_key(scon, &ev->key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -142,11 +142,6 @@ Error *error_copy(const Error *err)
|
|||||||
return err_new;
|
return err_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool error_is_set(Error **errp)
|
|
||||||
{
|
|
||||||
return (errp && *errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorClass error_get_class(const Error *err)
|
ErrorClass error_get_class(const Error *err)
|
||||||
{
|
{
|
||||||
return err->err_class;
|
return err->err_class;
|
||||||
|
21
util/iov.c
21
util/iov.c
@@ -335,6 +335,27 @@ void qemu_iovec_concat(QEMUIOVector *dst,
|
|||||||
qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes);
|
qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the contents of the iovecs are all zero
|
||||||
|
*/
|
||||||
|
bool qemu_iovec_is_zero(QEMUIOVector *qiov)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < qiov->niov; i++) {
|
||||||
|
size_t offs = QEMU_ALIGN_DOWN(qiov->iov[i].iov_len, 4 * sizeof(long));
|
||||||
|
uint8_t *ptr = qiov->iov[i].iov_base;
|
||||||
|
if (offs && !buffer_is_zero(qiov->iov[i].iov_base, offs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (; offs < qiov->iov[i].iov_len; offs++) {
|
||||||
|
if (ptr[offs]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void qemu_iovec_destroy(QEMUIOVector *qiov)
|
void qemu_iovec_destroy(QEMUIOVector *qiov)
|
||||||
{
|
{
|
||||||
assert(qiov->nalloc != -1);
|
assert(qiov->nalloc != -1);
|
||||||
|
@@ -354,6 +354,7 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
|
|||||||
int inet_connect_opts(QemuOpts *opts, Error **errp,
|
int inet_connect_opts(QemuOpts *opts, Error **errp,
|
||||||
NonBlockingConnectHandler *callback, void *opaque)
|
NonBlockingConnectHandler *callback, void *opaque)
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
struct addrinfo *res, *e;
|
struct addrinfo *res, *e;
|
||||||
int sock = -1;
|
int sock = -1;
|
||||||
bool in_progress;
|
bool in_progress;
|
||||||
@@ -372,24 +373,27 @@ int inet_connect_opts(QemuOpts *opts, Error **errp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (e = res; e != NULL; e = e->ai_next) {
|
for (e = res; e != NULL; e = e->ai_next) {
|
||||||
if (error_is_set(errp)) {
|
error_free(local_err);
|
||||||
error_free(*errp);
|
local_err = NULL;
|
||||||
*errp = NULL;
|
|
||||||
}
|
|
||||||
if (connect_state != NULL) {
|
if (connect_state != NULL) {
|
||||||
connect_state->current_addr = e;
|
connect_state->current_addr = e;
|
||||||
}
|
}
|
||||||
sock = inet_connect_addr(e, &in_progress, connect_state, errp);
|
sock = inet_connect_addr(e, &in_progress, connect_state, &local_err);
|
||||||
if (in_progress) {
|
if (sock >= 0) {
|
||||||
return sock;
|
|
||||||
} else if (sock >= 0) {
|
|
||||||
/* non blocking socket immediate success, call callback */
|
|
||||||
if (callback != NULL) {
|
|
||||||
callback(sock, opaque);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sock < 0) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
} else if (in_progress) {
|
||||||
|
/* wait_for_connect() will do the rest */
|
||||||
|
return sock;
|
||||||
|
} else {
|
||||||
|
if (callback) {
|
||||||
|
callback(sock, opaque);
|
||||||
|
}
|
||||||
|
}
|
||||||
g_free(connect_state);
|
g_free(connect_state);
|
||||||
freeaddrinfo(res);
|
freeaddrinfo(res);
|
||||||
return sock;
|
return sock;
|
||||||
|
Reference in New Issue
Block a user