Compare commits
	
		
			95 Commits
		
	
	
		
			pull-input
			...
			pull-usb-7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8d1bd3c901 | ||
|  | 56a9f18051 | ||
|  | b664b80f19 | ||
|  | 19e8393170 | ||
|  | 22513a9b44 | ||
|  | 68206d7342 | ||
|  | 36084d7e31 | ||
|  | f995523582 | ||
|  | 058fdcf52c | ||
|  | 463c534db5 | ||
|  | 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: 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 | ||||
| M: Peter Crosthwaite <peter.crosthwaite@xilinx.com> | ||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> | ||||
|   | ||||
							
								
								
									
										60
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								block.c
									
									
									
									
									
								
							| @@ -1274,6 +1274,33 @@ out: | ||||
|     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, ...) | ||||
|  * | ||||
| @@ -1337,6 +1364,20 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, | ||||
|         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; | ||||
|     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); | ||||
|  | ||||
|     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) { | ||||
|         /* Do nothing, write notifier decided to fail this request */ | ||||
|     } 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) { | ||||
|         *pnum = nb_sectors; | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|         ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; | ||||
|         if (bs->drv->protocol_name) { | ||||
|             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); | ||||
|     } | ||||
|  | ||||
|     if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { | ||||
|         ret |= BDRV_BLOCK_ALLOCATED; | ||||
|     } | ||||
|  | ||||
|     if (!(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO)) { | ||||
|         if (bdrv_unallocated_blocks_are_zero(bs)) { | ||||
|             ret |= BDRV_BLOCK_ZERO; | ||||
| @@ -3959,9 +4013,7 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|     return | ||||
|         (ret & BDRV_BLOCK_DATA) || | ||||
|         ((ret & BDRV_BLOCK_ZERO) && !bdrv_has_zero_init(bs)); | ||||
|     return (ret & BDRV_BLOCK_ALLOCATED); | ||||
| } | ||||
|  | ||||
| /* | ||||
|   | ||||
							
								
								
									
										79
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -23,6 +23,7 @@ | ||||
|  */ | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qapi/qmp/qbool.h" | ||||
| #include <curl/curl.h> | ||||
|  | ||||
| // #define DEBUG | ||||
| @@ -37,6 +38,21 @@ | ||||
| #if LIBCURL_VERSION_NUM >= 0x071000 | ||||
| /* The multi interface timer callback was introduced in 7.16.0 */ | ||||
| #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 | ||||
|  | ||||
| #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ | ||||
| @@ -46,12 +62,16 @@ | ||||
| #define CURL_NUM_STATES 8 | ||||
| #define CURL_NUM_ACB    8 | ||||
| #define SECTOR_SIZE     512 | ||||
| #define READ_AHEAD_SIZE (256 * 1024) | ||||
| #define READ_AHEAD_DEFAULT (256 * 1024) | ||||
|  | ||||
| #define FIND_RET_NONE   0 | ||||
| #define FIND_RET_OK     1 | ||||
| #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; | ||||
|  | ||||
| typedef struct CURLAIOCB { | ||||
| @@ -88,6 +108,7 @@ typedef struct BDRVCURLState { | ||||
|     CURLState states[CURL_NUM_STATES]; | ||||
|     char *url; | ||||
|     size_t readahead_size; | ||||
|     bool sslverify; | ||||
|     bool accept_range; | ||||
| } BDRVCURLState; | ||||
|  | ||||
| @@ -354,6 +375,8 @@ static CURLState *curl_init_state(BDRVCURLState *s) | ||||
|             return NULL; | ||||
|         } | ||||
|         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_WRITEFUNCTION, | ||||
|                          (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, | ||||
|                                 Error **errp) | ||||
| { | ||||
|  | ||||
|     #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); | ||||
|     qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename)); | ||||
| } | ||||
|  | ||||
| static QemuOptsList runtime_opts = { | ||||
| @@ -440,15 +427,20 @@ static QemuOptsList runtime_opts = { | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), | ||||
|     .desc = { | ||||
|         { | ||||
|             .name = "url", | ||||
|             .name = CURL_BLOCK_OPT_URL, | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "URL to open", | ||||
|         }, | ||||
|         { | ||||
|             .name = "readahead", | ||||
|             .name = CURL_BLOCK_OPT_READAHEAD, | ||||
|             .type = QEMU_OPT_SIZE, | ||||
|             .help = "Readahead size", | ||||
|         }, | ||||
|         { | ||||
|             .name = CURL_BLOCK_OPT_SSLVERIFY, | ||||
|             .type = QEMU_OPT_BOOL, | ||||
|             .help = "Verify SSL certificate" | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
| @@ -477,14 +469,17 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         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) { | ||||
|         error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", | ||||
|                    s->readahead_size); | ||||
|         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) { | ||||
|         error_setg(errp, "curl block driver requires an 'url' option"); | ||||
|         goto out_noclean; | ||||
|   | ||||
							
								
								
									
										317
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										317
									
								
								block/iscsi.c
									
									
									
									
									
								
							| @@ -30,6 +30,8 @@ | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/config-file.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/bitops.h" | ||||
| #include "qemu/bitmap.h" | ||||
| #include "block/block_int.h" | ||||
| #include "trace.h" | ||||
| #include "block/scsi.h" | ||||
| @@ -59,6 +61,8 @@ typedef struct IscsiLun { | ||||
|     struct scsi_inquiry_logical_block_provisioning lbp; | ||||
|     struct scsi_inquiry_block_limits bl; | ||||
|     unsigned char *zeroblock; | ||||
|     unsigned long *allocationmap; | ||||
|     int cluster_sectors; | ||||
| } IscsiLun; | ||||
|  | ||||
| typedef struct IscsiTask { | ||||
| @@ -92,6 +96,15 @@ typedef struct IscsiAIOCB { | ||||
| #define MAX_NOP_FAILURES 3 | ||||
| #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 | ||||
| iscsi_bh_cb(void *p) | ||||
| { | ||||
| @@ -273,6 +286,32 @@ static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors, | ||||
|     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, | ||||
|                                         int64_t sector_num, int nb_sectors, | ||||
|                                         QEMUIOVector *iov) | ||||
| @@ -336,9 +375,125 @@ retry: | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors); | ||||
|  | ||||
|     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, | ||||
|                                        int64_t sector_num, int nb_sectors, | ||||
|                                        QEMUIOVector *iov) | ||||
| @@ -355,6 +510,22 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, | ||||
|         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); | ||||
|     num_sectors = sector_qemu2lun(nb_sectors, iscsilun); | ||||
|  | ||||
| @@ -643,101 +814,6 @@ iscsi_getlength(BlockDriverState *bs) | ||||
|     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 | ||||
| coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num, | ||||
|                                    int nb_sectors) | ||||
| @@ -791,6 +867,8 @@ retry: | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -809,13 +887,14 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) { | ||||
|         /* WRITE SAME without UNMAP is not supported by the target */ | ||||
|         return -ENOTSUP; | ||||
|     if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) { | ||||
|         /* WRITE SAME with UNMAP is not supported by the target, | ||||
|          * fall back and try WRITE SAME without UNMAP */ | ||||
|         flags &= ~BDRV_REQ_MAY_UNMAP; | ||||
|     } | ||||
|  | ||||
|     if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) { | ||||
|         /* WRITE SAME with UNMAP is not supported by the target */ | ||||
|     if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) { | ||||
|         /* WRITE SAME without UNMAP is not supported by the target */ | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|  | ||||
| @@ -864,6 +943,12 @@ retry: | ||||
|         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; | ||||
| } | ||||
|  | ||||
| @@ -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); | ||||
| #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: | ||||
|     qemu_opts_del(opts); | ||||
|     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); | ||||
|     iscsi_destroy_context(iscsi); | ||||
|     g_free(iscsilun->zeroblock); | ||||
|     g_free(iscsilun->allocationmap); | ||||
|     memset(iscsilun, 0, sizeof(IscsiLun)); | ||||
| } | ||||
|  | ||||
| @@ -1388,6 +1490,13 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset) | ||||
|         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; | ||||
| } | ||||
|  | ||||
| @@ -1450,13 +1559,7 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz; | ||||
|     bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws; | ||||
|     /* 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 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; | ||||
|     } | ||||
|     bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -50,6 +50,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs) | ||||
|     } | ||||
|  | ||||
|     info->backing_file_depth = bdrv_get_backing_file_depth(bs); | ||||
|     info->detect_zeroes = bs->detect_zeroes; | ||||
|  | ||||
|     if (bs->io_limits_enabled) { | ||||
|         ThrottleConfig cfg; | ||||
|   | ||||
							
								
								
									
										44
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								block/qcow.c
									
									
									
									
									
								
							| @@ -48,9 +48,10 @@ typedef struct QCowHeader { | ||||
|     uint64_t size; /* in bytes */ | ||||
|     uint8_t cluster_bits; | ||||
|     uint8_t l2_bits; | ||||
|     uint16_t padding; | ||||
|     uint32_t crypt_method; | ||||
|     uint64_t l1_table_offset; | ||||
| } QCowHeader; | ||||
| } QEMU_PACKED QCowHeader; | ||||
|  | ||||
| #define L2_CACHE_SIZE 16 | ||||
|  | ||||
| @@ -60,7 +61,7 @@ typedef struct BDRVQcowState { | ||||
|     int cluster_sectors; | ||||
|     int l2_bits; | ||||
|     int l2_size; | ||||
|     int l1_size; | ||||
|     unsigned int l1_size; | ||||
|     uint64_t cluster_offset_mask; | ||||
|     uint64_t l1_table_offset; | ||||
|     uint64_t *l1_table; | ||||
| @@ -96,7 +97,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                      Error **errp) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int len, i, shift, ret; | ||||
|     unsigned int len, i, shift; | ||||
|     int ret; | ||||
|     QCowHeader 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; | ||||
|     } | ||||
|  | ||||
|     if (header.size <= 1 || header.cluster_bits < 9) { | ||||
|         error_setg(errp, "invalid value in qcow header"); | ||||
|     if (header.size <= 1) { | ||||
|         error_setg(errp, "Image size is too small (must be at least 2 bytes)"); | ||||
|         ret = -EINVAL; | ||||
|         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) { | ||||
|         error_setg(errp, "invalid encryption method in qcow header"); | ||||
|         ret = -EINVAL; | ||||
| @@ -151,7 +167,19 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|  | ||||
|     /* read the level 1 table */ | ||||
|     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 = 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) { | ||||
|         len = header.backing_file_size; | ||||
|         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, | ||||
|                    bs->backing_file, len); | ||||
|   | ||||
| @@ -472,10 +472,17 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, | ||||
|             s->curr_header = 0; | ||||
|         } else if (h2_seq > h1_seq) { | ||||
|             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 { | ||||
|                 goto fail; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     vhdx_region_register(s, s->headers[s->curr_header]->log_offset, | ||||
|                             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) | ||||
| { | ||||
|     if (throttle_conflicting(cfg)) { | ||||
| @@ -324,6 +343,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, | ||||
|     QemuOpts *opts; | ||||
|     const char *id; | ||||
|     bool has_driver_specific_opts; | ||||
|     BlockdevDetectZeroesOptions detect_zeroes; | ||||
|     BlockDriver *drv = NULL; | ||||
|  | ||||
|     /* 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 */ | ||||
|     dinfo = g_malloc0(sizeof(*dinfo)); | ||||
|     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->read_only = ro; | ||||
|     dinfo->bdrv->detect_zeroes = detect_zeroes; | ||||
|     dinfo->refcount = 1; | ||||
|     if (serial != NULL) { | ||||
|         dinfo->serial = g_strdup(serial); | ||||
| @@ -2455,6 +2494,10 @@ QemuOptsList qemu_common_drive_opts = { | ||||
|             .name = "copy-on-read", | ||||
|             .type = QEMU_OPT_BOOL, | ||||
|             .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 */ } | ||||
|     }, | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| CONFIG_VIRTIO=y | ||||
| CONFIG_SCLPCONSOLE=y | ||||
| CONFIG_S390_FLIC=$(CONFIG_KVM) | ||||
| CONFIG_S390_FLIC=y | ||||
| CONFIG_S390_FLIC_KVM=$(CONFIG_KVM) | ||||
|   | ||||
| @@ -48,7 +48,7 @@ The QAPI schema definitions can be modularized using the 'include' directive: | ||||
|  { 'include': 'path/to/file.json'} | ||||
|  | ||||
| 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 === | ||||
| @@ -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 | ||||
| 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', | ||||
|       'data': { 'integer': 'int', 'string': 'str' } } | ||||
|  | ||||
|     { 'command': 'my-command', | ||||
|       'data':    {'arg1': 'UserDefOne'}, | ||||
|       'returns': 'UserDefOne' } | ||||
|     mdroth@illuin:~/w/qemu2.git$ | ||||
|  | ||||
| === scripts/qapi-types.py === | ||||
|  | ||||
| @@ -255,14 +254,25 @@ created code. | ||||
|  | ||||
| Example: | ||||
|  | ||||
|     mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \ | ||||
|       --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json | ||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c | ||||
|     /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ | ||||
|     $ python scripts/qapi-types.py --output-dir="qapi-generated" \ | ||||
|     --prefix="example-" --input-file=example-schema.json | ||||
|     $ cat qapi-generated/example-qapi-types.c | ||||
| [Uninteresting stuff omitted...] | ||||
|  | ||||
|     #include "qapi/qapi-dealloc-visitor.h" | ||||
|     #include "example-qapi-types.h" | ||||
|     #include "example-qapi-visit.h" | ||||
|     void qapi_free_UserDefOneList(UserDefOneList * obj) | ||||
|     { | ||||
|         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) | ||||
|     { | ||||
| @@ -279,32 +289,38 @@ Example: | ||||
|         qapi_dealloc_visitor_cleanup(md); | ||||
|     } | ||||
|  | ||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h | ||||
|     /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ | ||||
|     #ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES | ||||
|     #define QAPI_GENERATED_EXAMPLE_QAPI_TYPES | ||||
|     $ cat qapi-generated/example-qapi-types.h | ||||
| [Uninteresting stuff omitted...] | ||||
|  | ||||
|     #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 UserDefOneList | ||||
|     { | ||||
|         union { | ||||
|             UserDefOne *value; | ||||
|             uint64_t padding; | ||||
|         }; | ||||
|         struct UserDefOneList *next; | ||||
|     } UserDefOneList; | ||||
|  | ||||
| [Functions on builtin types omitted...] | ||||
|  | ||||
|     struct UserDefOne | ||||
|     { | ||||
|         int64_t integer; | ||||
|         char * string; | ||||
|     }; | ||||
|  | ||||
|     void qapi_free_UserDefOneList(UserDefOneList * obj); | ||||
|     void qapi_free_UserDefOne(UserDefOne * obj); | ||||
|  | ||||
|     #endif | ||||
|  | ||||
|  | ||||
| === scripts/qapi-visit.py === | ||||
|  | ||||
| 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: | ||||
|  | ||||
|     mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \ | ||||
|         --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json | ||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c | ||||
|     /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | ||||
|     $ python scripts/qapi-visit.py --output-dir="qapi-generated" | ||||
|     --prefix="example-" --input-file=example-schema.json | ||||
|     $ cat qapi-generated/example-qapi-visit.c | ||||
| [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) | ||||
|     { | ||||
|         visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp); | ||||
|         visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp); | ||||
|         visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp); | ||||
|         visit_end_struct(m, errp); | ||||
|         Error *err = NULL; | ||||
|  | ||||
|         visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err); | ||||
|         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) | ||||
|     { | ||||
|         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; | ||||
|             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 | ||||
|     /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | ||||
|     $ python scripts/qapi-commands.py --output-dir="qapi-generated" \ | ||||
|     --prefix="example-" --input-file=example-schema.json | ||||
|     $ cat qapi-generated/example-qapi-visit.h | ||||
| [Uninteresting stuff omitted...] | ||||
|  | ||||
|     #ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT | ||||
|     #define QAPI_GENERATED_EXAMPLE_QAPI_VISIT | ||||
|     #ifndef EXAMPLE_QAPI_VISIT_H | ||||
|     #define EXAMPLE_QAPI_VISIT_H | ||||
|  | ||||
|     #include "qapi/qapi-visit-core.h" | ||||
|     #include "example-qapi-types.h" | ||||
| [Visitors for builtin types omitted...] | ||||
|  | ||||
|     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); | ||||
|  | ||||
|     #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 === | ||||
|  | ||||
| @@ -390,77 +433,80 @@ $(prefix)qmp-commands.h: Function prototypes for the QMP commands | ||||
|  | ||||
| Example: | ||||
|  | ||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c | ||||
|     /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | ||||
|     $ cat qapi-generated/example-qmp-marshal.c | ||||
| [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) | ||||
|     { | ||||
|         QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); | ||||
|         Error *local_err = NULL; | ||||
|         QmpOutputVisitor *mo = qmp_output_visitor_new(); | ||||
|         QapiDeallocVisitor *md; | ||||
|         Visitor *v; | ||||
|  | ||||
|         v = qmp_output_get_visitor(mo); | ||||
|         visit_type_UserDefOne(v, &ret_in, "unused", errp); | ||||
|         v = qapi_dealloc_get_visitor(md); | ||||
|         visit_type_UserDefOne(v, &ret_in, "unused", errp); | ||||
|         qapi_dealloc_visitor_cleanup(md); | ||||
|  | ||||
|  | ||||
|         visit_type_UserDefOne(v, &ret_in, "unused", &local_err); | ||||
|         if (local_err) { | ||||
|             goto out; | ||||
|         } | ||||
|         *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; | ||||
|         QmpInputVisitor *mi; | ||||
|         QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); | ||||
|         QapiDeallocVisitor *md; | ||||
|         Visitor *v; | ||||
|         UserDefOne * arg1 = NULL; | ||||
|  | ||||
|         mi = qmp_input_visitor_new(QOBJECT(args)); | ||||
|         v = qmp_input_get_visitor(mi); | ||||
|         visit_type_UserDefOne(v, &arg1, "arg1", errp); | ||||
|  | ||||
|         if (error_is_set(errp)) { | ||||
|         visit_type_UserDefOne(v, &arg1, "arg1", &local_err); | ||||
|         if (local_err) { | ||||
|             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: | ||||
|         error_propagate(errp, local_err); | ||||
|         qmp_input_visitor_cleanup(mi); | ||||
|         md = qapi_dealloc_visitor_new(); | ||||
|         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); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h | ||||
|     /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | ||||
|     $ cat qapi-generated/example-qmp-commands.h | ||||
| [Uninteresting stuff omitted...] | ||||
|  | ||||
|     #ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS | ||||
|     #define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS | ||||
|     #ifndef EXAMPLE_QMP_COMMANDS_H | ||||
|     #define EXAMPLE_QMP_COMMANDS_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); | ||||
|  | ||||
|     #endif | ||||
|     mdroth@illuin:~/w/qemu2.git$ | ||||
|   | ||||
| @@ -556,6 +556,7 @@ ETEXI | ||||
|         .params     = "keys [hold_ms]", | ||||
|         .help       = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", | ||||
|         .mhandler.cmd = hmp_send_key, | ||||
|         .command_completion = sendkey_completion, | ||||
|     }, | ||||
|  | ||||
| STEXI | ||||
| @@ -1233,9 +1234,10 @@ ETEXI | ||||
|     { | ||||
|         .name       = "netdev_add", | ||||
|         .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", | ||||
|         .mhandler.cmd = hmp_netdev_add, | ||||
|         .command_completion = netdev_add_completion, | ||||
|     }, | ||||
|  | ||||
| STEXI | ||||
| @@ -1250,6 +1252,7 @@ ETEXI | ||||
|         .params     = "id", | ||||
|         .help       = "remove host network device", | ||||
|         .mhandler.cmd = hmp_netdev_del, | ||||
|         .command_completion = netdev_del_completion, | ||||
|     }, | ||||
|  | ||||
| STEXI | ||||
| @@ -1339,6 +1342,7 @@ ETEXI | ||||
|         .params     = "name on|off", | ||||
|         .help       = "change the link status of a network adapter", | ||||
|         .mhandler.cmd = hmp_set_link, | ||||
|         .command_completion = set_link_completion, | ||||
|     }, | ||||
|  | ||||
| STEXI | ||||
| @@ -1622,6 +1626,7 @@ ETEXI | ||||
|         .params     = "args", | ||||
|         .help       = "add chardev", | ||||
|         .mhandler.cmd = hmp_chardev_add, | ||||
|         .command_completion = chardev_add_completion, | ||||
|     }, | ||||
|  | ||||
| STEXI | ||||
| @@ -1638,6 +1643,7 @@ ETEXI | ||||
|         .params     = "id", | ||||
|         .help       = "remove chardev", | ||||
|         .mhandler.cmd = hmp_chardev_remove, | ||||
|         .command_completion = chardev_remove_completion, | ||||
|     }, | ||||
|  | ||||
| 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); | ||||
|         } | ||||
|  | ||||
|         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 | ||||
|             || info->value->inserted->bps_rd | ||||
|             || 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) | ||||
| { | ||||
|     Error *err = NULL; | ||||
|     Error *err_end = NULL; | ||||
|     QemuOpts *opts; | ||||
|     char *type = NULL; | ||||
|     char *id = NULL; | ||||
| @@ -1411,24 +1417,23 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) | ||||
|     qdict_del(pdict, "qom-type"); | ||||
|     visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err); | ||||
|     if (err) { | ||||
|         goto out_clean; | ||||
|         goto out_end; | ||||
|     } | ||||
|  | ||||
|     qdict_del(pdict, "id"); | ||||
|     visit_type_str(opts_get_visitor(ov), &id, "id", &err); | ||||
|     if (err) { | ||||
|         goto out_clean; | ||||
|         goto out_end; | ||||
|     } | ||||
|  | ||||
|     object_add(type, id, pdict, opts_get_visitor(ov), &err); | ||||
|     if (err) { | ||||
|         goto out_clean; | ||||
|     } | ||||
|     visit_end_struct(opts_get_visitor(ov), &err); | ||||
|     if (err) { | ||||
|  | ||||
| out_end: | ||||
|     visit_end_struct(opts_get_visitor(ov), &err_end); | ||||
|     if (!err && err_end) { | ||||
|         qmp_object_del(id, NULL); | ||||
|     } | ||||
|  | ||||
|     error_propagate(&err, err_end); | ||||
| out_clean: | ||||
|     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 device_add_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 | ||||
|   | ||||
| @@ -245,7 +245,7 @@ static void intel_hda_update_int_sts(IntelHDAState *d) | ||||
|  | ||||
|     /* update global status */ | ||||
|     if (sts & d->int_ctl) { | ||||
|         sts |= (1 << 31); | ||||
|         sts |= (1U << 31); | ||||
|     } | ||||
|  | ||||
|     d->int_sts = sts; | ||||
| @@ -257,7 +257,7 @@ static void intel_hda_update_irq(IntelHDAState *d) | ||||
|     int level; | ||||
|  | ||||
|     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; | ||||
|     } else { | ||||
|         level = 0; | ||||
| @@ -574,7 +574,7 @@ static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint3 | ||||
|     if (st->ctl & 0x01) { | ||||
|         /* reset */ | ||||
|         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)) { | ||||
|         uint32_t stnr = (st->ctl >> 20) & 0x0f; | ||||
| @@ -829,6 +829,7 @@ static const struct IntelHDAReg regtab[] = { | ||||
|         .wclear   = 0x1c000000,                                       \ | ||||
|         .offset   = offsetof(IntelHDAState, st[_i].ctl),              \ | ||||
|         .whandler = intel_hda_set_st_ctl,                             \ | ||||
|         .reset    = SD_STS_FIFO_READY << 24                           \ | ||||
|     },                                                                \ | ||||
|     [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = {                              \ | ||||
|         .stream   = _i,                                               \ | ||||
|   | ||||
| @@ -26,3 +26,4 @@ obj-$(CONFIG_XICS) += xics.o | ||||
| obj-$(CONFIG_XICS_KVM) += xics_kvm.o | ||||
| obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.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. | ||||
|  * 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 "trace.h" | ||||
|  | ||||
| #define FLIC_SAVE_INITIAL_SIZE getpagesize() | ||||
| #define FLIC_FAILED (-1UL) | ||||
| #define FLIC_SAVEVM_VERSION 1 | ||||
| S390FLICState *s390_get_flic(void) | ||||
| { | ||||
|     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) | ||||
| { | ||||
|     DeviceState *dev; | ||||
|     int r; | ||||
|  | ||||
|     if (kvm_enabled()) { | ||||
|         dev = qdev_create(NULL, "s390-flic"); | ||||
|         object_property_add_child(qdev_get_machine(), "s390-flic", | ||||
|     dev = s390_flic_kvm_create(); | ||||
|     if (!dev) { | ||||
|         dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC); | ||||
|         object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC, | ||||
|                                   OBJECT(dev), NULL); | ||||
|     } | ||||
|     r = qdev_init(dev); | ||||
|     if (r) { | ||||
|         error_report("flic: couldn't create qdev"); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 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) | ||||
| static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, | ||||
|                                          uint8_t isc, bool swap, | ||||
|                                          bool is_maskable) | ||||
| { | ||||
|     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; | ||||
|     /* nothing to do */ | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| 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 = { | ||||
|         .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"); | ||||
|     } | ||||
|     /* nothing to do */ | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void flic_disable_wait_pfault(KVMS390FLICState *flic) | ||||
| static int qemu_s390_add_adapter_routes(S390FLICState *fs, | ||||
|                                         AdapterRoutes *routes) | ||||
| { | ||||
|     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"); | ||||
|     } | ||||
|     return -ENOSYS; | ||||
| } | ||||
|  | ||||
| /** 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) | ||||
| static void qemu_s390_release_adapter_routes(S390FLICState *fs, | ||||
|                                              AdapterRoutes *routes) | ||||
| { | ||||
|     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) | ||||
| static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
|     int r; | ||||
|     S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); | ||||
|  | ||||
|     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; | ||||
|     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; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 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, | ||||
|     .instance_size = sizeof(KVMS390FLICState), | ||||
|     .class_init    = kvm_s390_flic_class_init, | ||||
| 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, | ||||
| }; | ||||
|  | ||||
| static void kvm_s390_flic_register_types(void) | ||||
| static const TypeInfo s390_flic_common_info = { | ||||
|     .name          = TYPE_S390_FLIC_COMMON, | ||||
|     .parent        = TYPE_SYS_BUS_DEVICE, | ||||
|     .instance_size = sizeof(S390FLICState), | ||||
|     .class_size    = sizeof(S390FLICStateClass), | ||||
| }; | ||||
|  | ||||
| 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 "css.h" | ||||
| #include "trace.h" | ||||
| #include "hw/s390x/s390_flic.h" | ||||
|  | ||||
| typedef struct CrwContainer { | ||||
|     CRW crw; | ||||
| @@ -39,6 +40,13 @@ typedef struct CssImage { | ||||
|     ChpInfo chpids[MAX_CHPID + 1]; | ||||
| } CssImage; | ||||
|  | ||||
| typedef struct IoAdapter { | ||||
|     uint32_t id; | ||||
|     uint8_t type; | ||||
|     uint8_t isc; | ||||
|     QTAILQ_ENTRY(IoAdapter) sibling; | ||||
| } IoAdapter; | ||||
|  | ||||
| typedef struct ChannelSubSys { | ||||
|     QTAILQ_HEAD(, CrwContainer) pending_crws; | ||||
|     bool do_crw_mchk; | ||||
| @@ -49,6 +57,7 @@ typedef struct ChannelSubSys { | ||||
|     uint64_t chnmon_area; | ||||
|     CssImage *css[MAX_CSSID + 1]; | ||||
|     uint8_t default_cssid; | ||||
|     QTAILQ_HEAD(, IoAdapter) io_adapters; | ||||
| } ChannelSubSys; | ||||
|  | ||||
| static ChannelSubSys *channel_subsys; | ||||
| @@ -69,6 +78,46 @@ int css_create_css_image(uint8_t cssid, bool default_image) | ||||
|     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) | ||||
| { | ||||
|     if (channel_subsys->max_cssid > 0) { | ||||
| @@ -1235,6 +1284,7 @@ static void css_init(void) | ||||
|     channel_subsys->do_crw_mchk = true; | ||||
|     channel_subsys->crws_lost = false; | ||||
|     channel_subsys->chnmon_active = false; | ||||
|     QTAILQ_INIT(&channel_subsys->io_adapters); | ||||
| } | ||||
| 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); | ||||
| void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); | ||||
| 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 | ||||
|   | ||||
| @@ -21,12 +21,77 @@ | ||||
| #include "hw/sysbus.h" | ||||
| #include "qemu/bitops.h" | ||||
| #include "hw/virtio/virtio-bus.h" | ||||
| #include "hw/s390x/adapter.h" | ||||
| #include "hw/s390x/s390_flic.h" | ||||
|  | ||||
| #include "ioinst.h" | ||||
| #include "css.h" | ||||
| #include "virtio-ccw.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, | ||||
|                                VirtioCcwDevice *dev); | ||||
|  | ||||
| @@ -445,7 +510,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) | ||||
|             ret = -EFAULT; | ||||
|         } else { | ||||
|             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); | ||||
|             ret = 0; | ||||
|         } | ||||
| @@ -465,7 +530,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) | ||||
|             ret = -EFAULT; | ||||
|         } else { | ||||
|             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); | ||||
|             ret = 0; | ||||
|         } | ||||
| @@ -517,13 +582,20 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) | ||||
|                 ret = -EFAULT; | ||||
|             } else { | ||||
|                 len = hw_len; | ||||
|                 dev->summary_indicator = thinint->summary_indicator; | ||||
|                 dev->indicators = thinint->device_indicator; | ||||
|                 dev->summary_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->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); | ||||
|                 sch->thinint_active = ((dev->indicators != 0) && | ||||
|                                        (dev->summary_indicator != 0)); | ||||
|                 ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, | ||||
|                                               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; | ||||
|                 ret = 0; | ||||
|             } | ||||
| @@ -554,7 +626,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) | ||||
|     sch->driver_data = dev; | ||||
|     dev->sch = sch; | ||||
|  | ||||
|     dev->indicators = 0; | ||||
|     dev->indicators = NULL; | ||||
|  | ||||
|     /* Initialize subchannel structure. */ | ||||
|     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); | ||||
|         g_free(sch); | ||||
|     } | ||||
|     dev->indicators = 0; | ||||
|     if (dev->indicators) { | ||||
|         release_indicator(&dev->routes.adapter, dev->indicators); | ||||
|         dev->indicators = NULL; | ||||
|     } | ||||
|     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 | ||||
|              * endian notation. | ||||
|              */ | ||||
|             virtio_set_ind_atomic(sch, dev->indicators + | ||||
|                                   (dev->ind_bit + vector) / 8, | ||||
|                                   0x80 >> ((dev->ind_bit + vector) % 8)); | ||||
|             if (!virtio_set_ind_atomic(sch, dev->summary_indicator, | ||||
|             uint64_t ind_bit = dev->routes.adapter.ind_offset; | ||||
|  | ||||
|             virtio_set_ind_atomic(sch, dev->indicators->addr + | ||||
|                                   (ind_bit + vector) / 8, | ||||
|                                   0x80 >> ((ind_bit + vector) % 8)); | ||||
|             if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr, | ||||
|                                        0x01)) { | ||||
|                 css_adapter_interrupt(dev->thinint_isc); | ||||
|             } | ||||
|         } else { | ||||
|             indicators = ldq_phys(&address_space_memory, dev->indicators); | ||||
|             indicators = ldq_phys(&address_space_memory, dev->indicators->addr); | ||||
|             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); | ||||
|         } | ||||
|     } else { | ||||
| @@ -968,9 +1045,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) | ||||
|             return; | ||||
|         } | ||||
|         vector = 0; | ||||
|         indicators = ldq_phys(&address_space_memory, dev->indicators2); | ||||
|         indicators = ldq_phys(&address_space_memory, dev->indicators2->addr); | ||||
|         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); | ||||
|     } | ||||
| } | ||||
| @@ -991,9 +1068,18 @@ static void virtio_ccw_reset(DeviceState *d) | ||||
|     virtio_ccw_stop_ioeventfd(dev); | ||||
|     virtio_reset(vdev); | ||||
|     css_reset_sch(dev->sch); | ||||
|     dev->indicators = 0; | ||||
|     dev->indicators2 = 0; | ||||
|     dev->summary_indicator = 0; | ||||
|     if (dev->indicators) { | ||||
|         release_indicator(&dev->routes.adapter, dev->indicators); | ||||
|         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) | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
| 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, | ||||
|                                          bool assign, bool with_irqfd) | ||||
| { | ||||
| @@ -1042,11 +1201,17 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, | ||||
|             return r; | ||||
|         } | ||||
|         virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); | ||||
|         /* We do not support irqfd for classic I/O interrupts, because the | ||||
|          * classic interrupts are intermixed with the subchannel status, that | ||||
|          * is queried with test subchannel. We want to use vhost, though. | ||||
|          * Lets make sure to have vhost running and wire up the irq fd to | ||||
|          * land in qemu (and only the irq fd) in this code. | ||||
|         if (with_irqfd) { | ||||
|             r = virtio_ccw_add_irqfd(dev, n); | ||||
|             if (r) { | ||||
|                 virtio_queue_set_guest_notifier_fd_handler(vq, false, | ||||
|                                                            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) { | ||||
|             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) { | ||||
|             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); | ||||
|         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); | ||||
|     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); | ||||
|     bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled(); | ||||
|     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++) { | ||||
|         if (!virtio_queue_get_num(vdev, n)) { | ||||
|             break; | ||||
|         } | ||||
|         /* false -> true, as soon as irqfd works */ | ||||
|         r = virtio_ccw_set_guest_notifier(dev, n, assigned, false); | ||||
|         r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd); | ||||
|         if (r < 0) { | ||||
|             goto assign_error; | ||||
|         } | ||||
|     } | ||||
|     if (with_irqfd && !assigned) { | ||||
|         /* release irq routes after irqfds have been released */ | ||||
|         virtio_ccw_release_irqroutes(dev, nvqs); | ||||
|     } | ||||
|     return 0; | ||||
|  | ||||
| assign_error: | ||||
|     while (--n >= 0) { | ||||
|         virtio_ccw_set_guest_notifier(dev, n, !assigned, false); | ||||
|     } | ||||
| irqroute_error: | ||||
|     if (with_irqfd && assigned) { | ||||
|         virtio_ccw_release_irqroutes(dev, nvqs); | ||||
|     } | ||||
|     return r; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
| #include <hw/virtio/virtio-balloon.h> | ||||
| #include <hw/virtio/virtio-rng.h> | ||||
| #include <hw/virtio/virtio-bus.h> | ||||
| #include <hw/s390x/s390_flic.h> | ||||
|  | ||||
| #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   (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 { | ||||
|     DeviceState parent_obj; | ||||
|     SubchDev *sch; | ||||
| @@ -85,10 +94,11 @@ struct VirtioCcwDevice { | ||||
|     bool ioeventfd_disabled; | ||||
|     uint32_t flags; | ||||
|     uint8_t thinint_isc; | ||||
|     AdapterRoutes routes; | ||||
|     /* Guest provided values: */ | ||||
|     hwaddr indicators; | ||||
|     hwaddr indicators2; | ||||
|     hwaddr summary_indicator; | ||||
|     IndAddr *indicators; | ||||
|     IndAddr *indicators2; | ||||
|     IndAddr *summary_indicator; | ||||
|     uint64_t ind_bit; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
| #include "hw/hw.h" | ||||
| #include "hw/pci/pci.h" | ||||
| #include "sysemu/dma.h" | ||||
| #include "hw/pci/msi.h" | ||||
| #include "hw/pci/msix.h" | ||||
| #include "qemu/iov.h" | ||||
| #include "hw/scsi/scsi.h" | ||||
| @@ -43,9 +44,11 @@ | ||||
|  | ||||
| #define MEGASAS_FLAG_USE_JBOD      0 | ||||
| #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_FLAG_USE_QUEUE64   2 | ||||
| #define MEGASAS_FLAG_USE_QUEUE64   3 | ||||
| #define MEGASAS_MASK_USE_QUEUE64   (1 << MEGASAS_FLAG_USE_QUEUE64) | ||||
|  | ||||
| static const char *mfi_frame_desc[] = { | ||||
| @@ -132,6 +135,11 @@ static bool megasas_use_queue64(MegasasState *s) | ||||
|     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) | ||||
| { | ||||
|     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)) { | ||||
|                 trace_megasas_msix_raise(0); | ||||
|                 msix_notify(pci_dev, 0); | ||||
|             } else if (msi_enabled(pci_dev)) { | ||||
|                 trace_megasas_msi_raise(0); | ||||
|                 msi_notify(pci_dev, 0); | ||||
|             } else { | ||||
|                 trace_megasas_irq_raise(); | ||||
|                 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); | ||||
|     memcpy(info.image_component[0].name, "APP", 3); | ||||
|     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_time, __TIME__, 8); | ||||
|     memcpy(info.image_component[0].build_date, "Apr  1 2014", 11); | ||||
|     memcpy(info.image_component[0].build_time, "12:34:56", 8); | ||||
|     info.image_component_count = 1; | ||||
|     if (pci_dev->has_rom) { | ||||
|         uint8_t biosver[32]; | ||||
| @@ -1106,6 +1117,21 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) | ||||
|     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, | ||||
|                                       MegasasCmd *cmd) | ||||
| { | ||||
| @@ -1409,6 +1435,8 @@ static const struct dcmd_cmd_tbl_t { | ||||
|       megasas_dcmd_dummy }, | ||||
|     { MFI_DCMD_LD_GET_LIST, "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", | ||||
|       megasas_dcmd_ld_get_info }, | ||||
|     { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP", | ||||
| @@ -1939,12 +1967,20 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, | ||||
|         break; | ||||
|     case MFI_OMSK: | ||||
|         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(); | ||||
|             pci_irq_deassert(pci_dev); | ||||
|         } | ||||
|         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(); | ||||
|             } | ||||
|         } else { | ||||
|             trace_megasas_intr_disabled(); | ||||
|         } | ||||
| @@ -2068,6 +2104,7 @@ static const VMStateDescription vmstate_megasas = { | ||||
|     .minimum_version_id_old = 0, | ||||
|     .fields      = (VMStateField[]) { | ||||
|         VMSTATE_PCI_DEVICE(parent_obj, MegasasState), | ||||
|         VMSTATE_MSIX(parent_obj, MegasasState), | ||||
|  | ||||
|         VMSTATE_INT32(fw_state, MegasasState), | ||||
|         VMSTATE_INT32(intr_mask, MegasasState), | ||||
| @@ -2083,9 +2120,12 @@ static void megasas_scsi_uninit(PCIDevice *d) | ||||
| { | ||||
|     MegasasState *s = MEGASAS(d); | ||||
|  | ||||
| #ifdef USE_MSIX | ||||
|     msix_uninit(d, &s->mmio_io); | ||||
| #endif | ||||
|     if (megasas_use_msix(s)) { | ||||
|         msix_uninit(d, &s->mmio_io, &s->mmio_io); | ||||
|     } | ||||
|     if (megasas_use_msi(s)) { | ||||
|         msi_uninit(d); | ||||
|     } | ||||
|     memory_region_destroy(&s->mmio_io); | ||||
|     memory_region_destroy(&s->port_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, | ||||
|                           "megasas-queue", 0x40000); | ||||
|  | ||||
| #ifdef USE_MSIX | ||||
|     /* MSI-X support is currently broken */ | ||||
|     if (megasas_use_msi(s) && | ||||
|         msi_init(dev, 0x50, 1, true, false)) { | ||||
|         s->flags &= ~MEGASAS_MASK_USE_MSI; | ||||
|     } | ||||
|     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; | ||||
|     } | ||||
| #else | ||||
|     s->flags &= ~MEGASAS_MASK_USE_MSIX; | ||||
| #endif | ||||
|  | ||||
|     bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64; | ||||
|     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; | ||||
|     } | ||||
|     trace_megasas_init(s->fw_sge, s->fw_cmds, | ||||
|                        megasas_use_msix(s) ? "MSI-X" : "INTx", | ||||
|                        megasas_is_jbod(s) ? "jbod" : "raid"); | ||||
|     s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ? | ||||
|         MAX_SCSI_DEVS : MFI_MAX_LD; | ||||
| @@ -2189,6 +2228,13 @@ static int megasas_scsi_init(PCIDevice *dev) | ||||
|     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[] = { | ||||
|     DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, | ||||
|                        MEGASAS_DEFAULT_SGE), | ||||
| @@ -2196,10 +2242,10 @@ static Property megasas_properties[] = { | ||||
|                        MEGASAS_DEFAULT_FRAMES), | ||||
|     DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), | ||||
|     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, | ||||
|                     MEGASAS_FLAG_USE_MSIX, false), | ||||
| #endif | ||||
|     DEFINE_PROP_BIT("use_jbod", MegasasState, flags, | ||||
|                     MEGASAS_FLAG_USE_JBOD, false), | ||||
|     DEFINE_PROP_END_OF_LIST(), | ||||
| @@ -2222,6 +2268,7 @@ static void megasas_class_init(ObjectClass *oc, void *data) | ||||
|     dc->vmsd = &vmstate_megasas; | ||||
|     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); | ||||
|     dc->desc = "LSI MegaRAID SAS 1078"; | ||||
|     pc->config_write = megasas_write_config; | ||||
| } | ||||
|  | ||||
| static const TypeInfo megasas_info = { | ||||
|   | ||||
| @@ -164,6 +164,7 @@ typedef enum { | ||||
|     MFI_DCMD_PD_BLINK =                 0x02070100, | ||||
|     MFI_DCMD_PD_UNBLINK =               0x02070200, | ||||
|     MFI_DCMD_LD_GET_LIST =              0x03010000, | ||||
|     MFI_DCMD_LD_LIST_QUERY =            0x03010100, | ||||
|     MFI_DCMD_LD_GET_INFO =              0x03020000, | ||||
|     MFI_DCMD_LD_GET_PROP =              0x03030000, | ||||
|     MFI_DCMD_LD_SET_PROP =              0x03040000, | ||||
| @@ -411,6 +412,14 @@ typedef enum { | ||||
|     MR_PD_QUERY_TYPE_EXPOSED_TO_HOST =  5, /*query for system drives */ | ||||
| } 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 | ||||
|  */ | ||||
|   | ||||
| @@ -938,6 +938,7 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) | ||||
|         if (cmd->xfer == 0) { | ||||
|             cmd->xfer = 256; | ||||
|         } | ||||
|         /* fall through */ | ||||
|     case WRITE_10: | ||||
|     case WRITE_VERIFY_10: | ||||
|     case WRITE_12: | ||||
| @@ -952,6 +953,7 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) | ||||
|         if (cmd->xfer == 0) { | ||||
|             cmd->xfer = 256; | ||||
|         } | ||||
|         /* fall through */ | ||||
|     case READ_10: | ||||
|     case RECOVER_BUFFERED_DATA: | ||||
|     case READ_12: | ||||
|   | ||||
| @@ -2458,21 +2458,27 @@ static int scsi_block_initfn(SCSIDevice *dev) | ||||
|     int rc; | ||||
|  | ||||
|     if (!s->qdev.conf.bs) { | ||||
|         error_report("scsi-block: drive property not set"); | ||||
|         error_report("drive property not set"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* 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 || | ||||
|         sg_version < 30000) { | ||||
|         error_report("scsi-block: scsi generic interface too old"); | ||||
|     rc = bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version); | ||||
|     if (rc < 0) { | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|     /* get device type from INQUIRY data */ | ||||
|     rc = get_device_type(s); | ||||
|     if (rc < 0) { | ||||
|         error_report("scsi-block: INQUIRY failed"); | ||||
|         error_report("INQUIRY failed"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -394,6 +394,7 @@ static void scsi_destroy(SCSIDevice *s) | ||||
|  | ||||
| static int scsi_generic_initfn(SCSIDevice *s) | ||||
| { | ||||
|     int rc; | ||||
|     int sg_version; | ||||
|     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 */ | ||||
|     if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0) { | ||||
|         error_report("scsi generic interface not supported"); | ||||
|     rc = bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version); | ||||
|     if (rc < 0) { | ||||
|         error_report("cannot get SG_IO version number: %s.  " | ||||
|                      "Is this a SCSI device?", | ||||
|                      strerror(-rc)); | ||||
|         return -1; | ||||
|     } | ||||
|     if (sg_version < 30000) { | ||||
|   | ||||
| @@ -498,7 +498,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, | ||||
|                                    uint32_t event, uint32_t reason) | ||||
| { | ||||
|     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); | ||||
|     VirtIOSCSIReq *req = virtio_scsi_pop_req(s, vs->event_vq); | ||||
|     VirtIOSCSIReq *req; | ||||
|     VirtIOSCSIEvent *evt; | ||||
|     VirtIODevice *vdev = VIRTIO_DEVICE(s); | ||||
|     int in_size; | ||||
| @@ -507,6 +507,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     req = virtio_scsi_pop_req(s, vs->event_vq); | ||||
|     if (!req) { | ||||
|         s->events_dropped = true; | ||||
|         return; | ||||
|   | ||||
| @@ -793,19 +793,46 @@ static const MemoryRegionOps cmos_ops = { | ||||
| static void rtc_get_date(Object *obj, Visitor *v, void *opaque, | ||||
|                          const char *name, Error **errp) | ||||
| { | ||||
|     Error *err = NULL; | ||||
|     RTCState *s = MC146818_RTC(obj); | ||||
|     struct tm current_tm; | ||||
|  | ||||
|     rtc_update_time(s); | ||||
|     rtc_get_time(s, ¤t_tm); | ||||
|     visit_start_struct(v, NULL, "struct tm", name, 0, errp); | ||||
|     visit_type_int32(v, ¤t_tm.tm_year, "tm_year", errp); | ||||
|     visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", errp); | ||||
|     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_min, "tm_min", errp); | ||||
|     visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", errp); | ||||
|     visit_start_struct(v, NULL, "struct tm", name, 0, &err); | ||||
|     if (err) { | ||||
|         goto out; | ||||
|     } | ||||
|     visit_type_int32(v, ¤t_tm.tm_year, "tm_year", &err); | ||||
|     if (err) { | ||||
|         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); | ||||
| out: | ||||
|     error_propagate(errp, err); | ||||
| } | ||||
|  | ||||
| static void rtc_realizefn(DeviceState *dev, Error **errp) | ||||
|   | ||||
| @@ -46,6 +46,7 @@ enum mtp_code { | ||||
|  | ||||
|     /* response codes */ | ||||
|     RES_OK                         = 0x2001, | ||||
|     RES_GENERAL_ERROR              = 0x2002, | ||||
|     RES_SESSION_NOT_OPEN           = 0x2003, | ||||
|     RES_INVALID_TRANSACTION_ID     = 0x2004, | ||||
|     RES_OPERATION_NOT_SUPPORTED    = 0x2005, | ||||
| @@ -109,7 +110,8 @@ struct MTPObject { | ||||
|     struct stat  stat; | ||||
|     MTPObject    *parent; | ||||
|     MTPObject    **children; | ||||
|     int32_t      nchildren; | ||||
|     uint32_t     nchildren; | ||||
|     bool         have_children; | ||||
|     QTAILQ_ENTRY(MTPObject) next; | ||||
| }; | ||||
|  | ||||
| @@ -273,7 +275,6 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, | ||||
|     o->handle = handle; | ||||
|     o->parent = parent; | ||||
|     o->name = g_strdup(name); | ||||
|     o->nchildren = -1; | ||||
|     if (parent == NULL) { | ||||
|         o->path = g_strdup(name); | ||||
|     } else { | ||||
| @@ -340,7 +341,11 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) | ||||
|     struct dirent *entry; | ||||
|     DIR *dir; | ||||
|  | ||||
|     o->nchildren = 0; | ||||
|     if (o->have_children) { | ||||
|         return; | ||||
|     } | ||||
|     o->have_children = true; | ||||
|  | ||||
|     dir = opendir(o->path); | ||||
|     if (!dir) { | ||||
|         return; | ||||
| @@ -698,7 +703,10 @@ static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, | ||||
|     if (offset > o->stat.st_size) { | ||||
|         offset = o->stat.st_size; | ||||
|     } | ||||
|     lseek(d->fd, offset, SEEK_SET); | ||||
|     if (lseek(d->fd, offset, SEEK_SET) < 0) { | ||||
|         usb_mtp_data_free(d); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     d->length = c->argv[2]; | ||||
|     if (d->length > o->stat.st_size - offset) { | ||||
| @@ -789,9 +797,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) | ||||
|                                  c->trans, 0, 0, 0); | ||||
|             return; | ||||
|         } | ||||
|         if (o->nchildren == -1) { | ||||
|         usb_mtp_object_readdir(s, o); | ||||
|         } | ||||
|         if (c->code == CMD_GET_NUM_OBJECTS) { | ||||
|             trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path); | ||||
|             nres = 1; | ||||
| @@ -823,7 +829,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) | ||||
|         } | ||||
|         data_in = usb_mtp_get_object(s, c, o); | ||||
|         if (NULL == data_in) { | ||||
|             fprintf(stderr, "%s: TODO: handle error\n", __func__); | ||||
|             usb_mtp_queue_result(s, RES_GENERAL_ERROR, | ||||
|                                  c->trans, 0, 0, 0); | ||||
|             return; | ||||
|         } | ||||
|         break; | ||||
|     case CMD_GET_PARTIAL_OBJECT: | ||||
| @@ -840,7 +848,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) | ||||
|         } | ||||
|         data_in = usb_mtp_get_partial_object(s, c, o); | ||||
|         if (NULL == data_in) { | ||||
|             fprintf(stderr, "%s: TODO: handle error\n", __func__); | ||||
|             usb_mtp_queue_result(s, RES_GENERAL_ERROR, | ||||
|                                  c->trans, 0, 0, 0); | ||||
|             return; | ||||
|         } | ||||
|         nres = 1; | ||||
|         res0 = data_in->length; | ||||
|   | ||||
| @@ -621,6 +621,11 @@ static const char *ep_state_name(uint32_t state) | ||||
|                        ARRAY_SIZE(ep_state_names)); | ||||
| } | ||||
|  | ||||
| static bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit) | ||||
| { | ||||
|     return xhci->flags & (1 << bit); | ||||
| } | ||||
|  | ||||
| static uint64_t xhci_mfindex_get(XHCIState *xhci) | ||||
| { | ||||
|     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | ||||
| @@ -3435,7 +3440,7 @@ static void xhci_child_detach(USBPort *uport, USBDevice *child) | ||||
|     USBBus *bus = usb_bus_from_device(child); | ||||
|     XHCIState *xhci = container_of(bus, XHCIState, bus); | ||||
|  | ||||
|     xhci_detach_slot(xhci, uport); | ||||
|     xhci_detach_slot(xhci, child->port); | ||||
| } | ||||
|  | ||||
| static USBPortOps xhci_uport_ops = { | ||||
| @@ -3594,13 +3599,15 @@ static int usb_xhci_initfn(struct PCIDevice *dev) | ||||
|                      PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64, | ||||
|                      &xhci->mem); | ||||
|  | ||||
|     if (pci_bus_is_express(dev->bus)) { | ||||
|         ret = pcie_endpoint_cap_init(dev, 0xa0); | ||||
|         assert(ret >= 0); | ||||
|     } | ||||
|  | ||||
|     if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) { | ||||
|     if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) { | ||||
|         msi_init(dev, 0x70, xhci->numintrs, true, false); | ||||
|     } | ||||
|     if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) { | ||||
|     if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) { | ||||
|         msix_init(dev, xhci->numintrs, | ||||
|                   &xhci->mem, 0, OFF_MSIX_TABLE, | ||||
|                   &xhci->mem, 0, OFF_MSIX_PBA, | ||||
|   | ||||
| @@ -720,6 +720,9 @@ static void usb_host_ep_update(USBHostDevice *s) | ||||
|     struct libusb_config_descriptor *conf; | ||||
|     const struct libusb_interface_descriptor *intf; | ||||
|     const struct libusb_endpoint_descriptor *endp; | ||||
| #if LIBUSBX_API_VERSION >= 0x01000103 | ||||
|     struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp; | ||||
| #endif | ||||
|     uint8_t devep, type; | ||||
|     int pid, ep; | ||||
|     int rc, i, e; | ||||
| @@ -765,6 +768,15 @@ static void usb_host_ep_update(USBHostDevice *s) | ||||
|             usb_ep_set_type(udev, pid, ep, type); | ||||
|             usb_ep_set_ifnum(udev, pid, ep, i); | ||||
|             usb_ep_set_halted(udev, pid, ep, 0); | ||||
| #if LIBUSBX_API_VERSION >= 0x01000103 | ||||
|             if (type == LIBUSB_TRANSFER_TYPE_BULK && | ||||
|                     libusb_get_ss_endpoint_companion_descriptor(ctx, endp, | ||||
|                         &endp_ss_comp) == LIBUSB_SUCCESS) { | ||||
|                 usb_ep_set_max_streams(udev, pid, ep, | ||||
|                                        endp_ss_comp->bmAttributes); | ||||
|                 libusb_free_ss_endpoint_companion_descriptor(endp_ss_comp); | ||||
|             } | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -1202,10 +1214,23 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p) | ||||
|             usb_packet_copy(p, r->buffer, size); | ||||
|         } | ||||
|         ep = p->ep->nr | (r->in ? USB_DIR_IN : 0); | ||||
|         if (p->stream) { | ||||
| #if LIBUSBX_API_VERSION >= 0x01000103 | ||||
|             libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream, | ||||
|                                              r->buffer, size, | ||||
|                                              usb_host_req_complete_data, r, | ||||
|                                              BULK_TIMEOUT); | ||||
| #else | ||||
|             usb_host_req_free(r); | ||||
|             p->status = USB_RET_STALL; | ||||
|             return; | ||||
| #endif | ||||
|         } else { | ||||
|             libusb_fill_bulk_transfer(r->xfer, s->dh, ep, | ||||
|                                       r->buffer, size, | ||||
|                                       usb_host_req_complete_data, r, | ||||
|                                       BULK_TIMEOUT); | ||||
|         } | ||||
|         break; | ||||
|     case USB_ENDPOINT_XFER_INT: | ||||
|         r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size); | ||||
| @@ -1268,6 +1293,54 @@ static void usb_host_handle_reset(USBDevice *udev) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps, | ||||
|                                   int nr_eps, int streams) | ||||
| { | ||||
| #if LIBUSBX_API_VERSION >= 0x01000103 | ||||
|     USBHostDevice *s = USB_HOST_DEVICE(udev); | ||||
|     unsigned char endpoints[30]; | ||||
|     int i, rc; | ||||
|  | ||||
|     for (i = 0; i < nr_eps; i++) { | ||||
|         endpoints[i] = eps[i]->nr; | ||||
|         if (eps[i]->pid == USB_TOKEN_IN) { | ||||
|             endpoints[i] |= 0x80; | ||||
|         } | ||||
|     } | ||||
|     rc = libusb_alloc_streams(s->dh, streams, endpoints, nr_eps); | ||||
|     if (rc < 0) { | ||||
|         usb_host_libusb_error("libusb_alloc_streams", rc); | ||||
|     } else if (rc != streams) { | ||||
|         fprintf(stderr, | ||||
|             "libusb_alloc_streams: got less streams then requested %d < %d\n", | ||||
|             rc, streams); | ||||
|     } | ||||
|  | ||||
|     return (rc == streams) ? 0 : -1; | ||||
| #else | ||||
|     fprintf(stderr, "libusb_alloc_streams: error not implemented\n"); | ||||
|     return -1; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps, | ||||
|                                   int nr_eps) | ||||
| { | ||||
| #if LIBUSBX_API_VERSION >= 0x01000103 | ||||
|     USBHostDevice *s = USB_HOST_DEVICE(udev); | ||||
|     unsigned char endpoints[30]; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < nr_eps; i++) { | ||||
|         endpoints[i] = eps[i]->nr; | ||||
|         if (eps[i]->pid == USB_TOKEN_IN) { | ||||
|             endpoints[i] |= 0x80; | ||||
|         } | ||||
|     } | ||||
|     libusb_free_streams(s->dh, endpoints, nr_eps); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * This is *NOT* about restoring state.  We have absolutely no idea | ||||
|  * what state the host device is in at the moment and whenever it is | ||||
| @@ -1349,6 +1422,8 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data) | ||||
|     uc->handle_reset   = usb_host_handle_reset; | ||||
|     uc->handle_destroy = usb_host_handle_destroy; | ||||
|     uc->flush_ep_queue = usb_host_flush_ep_queue; | ||||
|     uc->alloc_streams  = usb_host_alloc_streams; | ||||
|     uc->free_streams   = usb_host_free_streams; | ||||
|     dc->vmsd = &vmstate_usb_host; | ||||
|     dc->props = usb_host_dev_properties; | ||||
|     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); | ||||
|   | ||||
| @@ -50,6 +50,10 @@ | ||||
|                        ((i) & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, \ | ||||
|                        (i) & 0x0f)) | ||||
|  | ||||
| #ifndef USBREDIR_VERSION /* This is not defined in older usbredir versions */ | ||||
| #define USBREDIR_VERSION 0 | ||||
| #endif | ||||
|  | ||||
| typedef struct USBRedirDevice USBRedirDevice; | ||||
|  | ||||
| /* Struct to hold buffered packets */ | ||||
| @@ -68,6 +72,7 @@ struct endp_data { | ||||
|     uint8_t interval; | ||||
|     uint8_t interface; /* bInterfaceNumber this ep belongs to */ | ||||
|     uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */ | ||||
|     uint32_t max_streams; | ||||
|     uint8_t iso_started; | ||||
|     uint8_t iso_error; /* For reporting iso errors to the HC */ | ||||
|     uint8_t interrupt_started; | ||||
| @@ -106,8 +111,9 @@ struct USBRedirDevice { | ||||
|     int read_buf_size; | ||||
|     /* Active chardev-watch-tag */ | ||||
|     guint watch; | ||||
|     /* For async handling of close */ | ||||
|     /* For async handling of close / reject */ | ||||
|     QEMUBH *chardev_close_bh; | ||||
|     QEMUBH *device_reject_bh; | ||||
|     /* To delay the usb attach in case of quick chardev close + open */ | ||||
|     QEMUTimer *attach_timer; | ||||
|     int64_t next_attach_time; | ||||
| @@ -780,11 +786,12 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, | ||||
|         dev->endpoint[EP2I(ep)].bulk_receiving_enabled = 0; | ||||
|     } | ||||
|  | ||||
|     DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id); | ||||
|     DPRINTF("bulk-out ep %02X stream %u len %zd id %"PRIu64"\n", | ||||
|             ep, p->stream, size, p->id); | ||||
|  | ||||
|     bulk_packet.endpoint  = ep; | ||||
|     bulk_packet.length    = size; | ||||
|     bulk_packet.stream_id = 0; | ||||
|     bulk_packet.stream_id = p->stream; | ||||
|     bulk_packet.length_high = size >> 16; | ||||
|     assert(bulk_packet.length_high == 0 || | ||||
|            usbredirparser_peer_has_cap(dev->parser, | ||||
| @@ -1091,6 +1098,66 @@ static void usbredir_handle_control(USBDevice *udev, USBPacket *p, | ||||
|     p->status = USB_RET_ASYNC; | ||||
| } | ||||
|  | ||||
| static int usbredir_alloc_streams(USBDevice *udev, USBEndpoint **eps, | ||||
|                                   int nr_eps, int streams) | ||||
| { | ||||
|     USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); | ||||
| #if USBREDIR_VERSION >= 0x000700 | ||||
|     struct usb_redir_alloc_bulk_streams_header alloc_streams; | ||||
|     int i; | ||||
|  | ||||
|     if (!usbredirparser_peer_has_cap(dev->parser, | ||||
|                                      usb_redir_cap_bulk_streams)) { | ||||
|         ERROR("peer does not support streams\n"); | ||||
|         goto reject; | ||||
|     } | ||||
|  | ||||
|     if (streams == 0) { | ||||
|         ERROR("request to allocate 0 streams\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     alloc_streams.no_streams = streams; | ||||
|     alloc_streams.endpoints = 0; | ||||
|     for (i = 0; i < nr_eps; i++) { | ||||
|         alloc_streams.endpoints |= 1 << USBEP2I(eps[i]); | ||||
|     } | ||||
|     usbredirparser_send_alloc_bulk_streams(dev->parser, 0, &alloc_streams); | ||||
|     usbredirparser_do_write(dev->parser); | ||||
|  | ||||
|     return 0; | ||||
| #else | ||||
|     ERROR("usbredir_alloc_streams not implemented\n"); | ||||
|     goto reject; | ||||
| #endif | ||||
| reject: | ||||
|     ERROR("streams are not available, disconnecting\n"); | ||||
|     qemu_bh_schedule(dev->device_reject_bh); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void usbredir_free_streams(USBDevice *udev, USBEndpoint **eps, | ||||
|                                   int nr_eps) | ||||
| { | ||||
| #if USBREDIR_VERSION >= 0x000700 | ||||
|     USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); | ||||
|     struct usb_redir_free_bulk_streams_header free_streams; | ||||
|     int i; | ||||
|  | ||||
|     if (!usbredirparser_peer_has_cap(dev->parser, | ||||
|                                      usb_redir_cap_bulk_streams)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     free_streams.endpoints = 0; | ||||
|     for (i = 0; i < nr_eps; i++) { | ||||
|         free_streams.endpoints |= 1 << USBEP2I(eps[i]); | ||||
|     } | ||||
|     usbredirparser_send_free_bulk_streams(dev->parser, 0, &free_streams); | ||||
|     usbredirparser_do_write(dev->parser); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Close events can be triggered by usbredirparser_do_write which gets called | ||||
|  * from within the USBDevice data / control packet callbacks and doing a | ||||
| @@ -1102,6 +1169,7 @@ static void usbredir_chardev_close_bh(void *opaque) | ||||
| { | ||||
|     USBRedirDevice *dev = opaque; | ||||
|  | ||||
|     qemu_bh_cancel(dev->device_reject_bh); | ||||
|     usbredir_device_disconnect(dev); | ||||
|  | ||||
|     if (dev->parser) { | ||||
| @@ -1153,6 +1221,9 @@ static void usbredir_create_parser(USBRedirDevice *dev) | ||||
|     usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); | ||||
|     usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length); | ||||
|     usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving); | ||||
| #if USBREDIR_VERSION >= 0x000700 | ||||
|     usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams); | ||||
| #endif | ||||
|  | ||||
|     if (runstate_check(RUN_STATE_INMIGRATE)) { | ||||
|         flags |= usbredirparser_fl_no_hello; | ||||
| @@ -1171,6 +1242,17 @@ static void usbredir_reject_device(USBRedirDevice *dev) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * We may need to reject the device when the hcd calls alloc_streams, doing | ||||
|  * an usb_detach from within a hcd call is not a good idea, hence this bh. | ||||
|  */ | ||||
| static void usbredir_device_reject_bh(void *opaque) | ||||
| { | ||||
|     USBRedirDevice *dev = opaque; | ||||
|  | ||||
|     usbredir_reject_device(dev); | ||||
| } | ||||
|  | ||||
| static void usbredir_do_attach(void *opaque) | ||||
| { | ||||
|     USBRedirDevice *dev = opaque; | ||||
| @@ -1297,6 +1379,7 @@ static int usbredir_initfn(USBDevice *udev) | ||||
|     } | ||||
|  | ||||
|     dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); | ||||
|     dev->device_reject_bh = qemu_bh_new(usbredir_device_reject_bh, dev); | ||||
|     dev->attach_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usbredir_do_attach, dev); | ||||
|  | ||||
|     packet_id_queue_init(&dev->cancelled, dev, "cancelled"); | ||||
| @@ -1337,6 +1420,7 @@ static void usbredir_handle_destroy(USBDevice *udev) | ||||
|     dev->cs = NULL; | ||||
|     /* Note must be done after qemu_chr_close, as that causes a close event */ | ||||
|     qemu_bh_delete(dev->chardev_close_bh); | ||||
|     qemu_bh_delete(dev->device_reject_bh); | ||||
|  | ||||
|     timer_del(dev->attach_timer); | ||||
|     timer_free(dev->attach_timer); | ||||
| @@ -1628,6 +1712,7 @@ static void usbredir_setup_usb_eps(USBRedirDevice *dev) | ||||
|         usb_ep->type = dev->endpoint[i].type; | ||||
|         usb_ep->ifnum = dev->endpoint[i].interface; | ||||
|         usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; | ||||
|         usb_ep->max_streams = dev->endpoint[i].max_streams; | ||||
|         usbredir_set_pipeline(dev, usb_ep); | ||||
|     } | ||||
| } | ||||
| @@ -1646,6 +1731,12 @@ static void usbredir_ep_info(void *priv, | ||||
|                                      usb_redir_cap_ep_info_max_packet_size)) { | ||||
|             dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i]; | ||||
|         } | ||||
| #if USBREDIR_VERSION >= 0x000700 | ||||
|         if (usbredirparser_peer_has_cap(dev->parser, | ||||
|                                         usb_redir_cap_bulk_streams)) { | ||||
|             dev->endpoint[i].max_streams = ep_info->max_streams[i]; | ||||
|         } | ||||
| #endif | ||||
|         switch (dev->endpoint[i].type) { | ||||
|         case usb_redir_type_invalid: | ||||
|             break; | ||||
| @@ -1779,6 +1870,20 @@ static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, | ||||
| static void usbredir_bulk_streams_status(void *priv, uint64_t id, | ||||
|     struct usb_redir_bulk_streams_status_header *bulk_streams_status) | ||||
| { | ||||
| #if USBREDIR_VERSION >= 0x000700 | ||||
|     USBRedirDevice *dev = priv; | ||||
|  | ||||
|     if (bulk_streams_status->status == usb_redir_success) { | ||||
|         DPRINTF("bulk streams status %d eps %08x\n", | ||||
|                 bulk_streams_status->status, bulk_streams_status->endpoints); | ||||
|     } else { | ||||
|         ERROR("bulk streams %s failed status %d eps %08x\n", | ||||
|               (bulk_streams_status->no_streams == 0) ? "free" : "alloc", | ||||
|               bulk_streams_status->status, bulk_streams_status->endpoints); | ||||
|         ERROR("usb-redir-host does not provide streams, disconnecting\n"); | ||||
|         usbredir_reject_device(dev); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static void usbredir_bulk_receiving_status(void *priv, uint64_t id, | ||||
| @@ -1850,8 +1955,8 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, | ||||
|     int len = (bulk_packet->length_high << 16) | bulk_packet->length; | ||||
|     USBPacket *p; | ||||
|  | ||||
|     DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n", | ||||
|             bulk_packet->status, ep, len, id); | ||||
|     DPRINTF("bulk-in status %d ep %02X stream %u len %d id %"PRIu64"\n", | ||||
|             bulk_packet->status, ep, bulk_packet->stream_id, len, id); | ||||
|  | ||||
|     p = usbredir_find_packet_by_id(dev, ep, id); | ||||
|     if (p) { | ||||
| @@ -2165,6 +2270,23 @@ static bool usbredir_bulk_receiving_needed(void *priv) | ||||
|     return endp->bulk_receiving_started; | ||||
| } | ||||
|  | ||||
| static const VMStateDescription usbredir_stream_vmstate = { | ||||
|     .name = "usb-redir-ep/stream-state", | ||||
|     .version_id = 1, | ||||
|     .minimum_version_id = 1, | ||||
|     .fields = (VMStateField[]) { | ||||
|         VMSTATE_UINT32(max_streams, struct endp_data), | ||||
|         VMSTATE_END_OF_LIST() | ||||
|     } | ||||
| }; | ||||
|  | ||||
| static bool usbredir_stream_needed(void *priv) | ||||
| { | ||||
|     struct endp_data *endp = priv; | ||||
|  | ||||
|     return endp->max_streams; | ||||
| } | ||||
|  | ||||
| static const VMStateDescription usbredir_ep_vmstate = { | ||||
|     .name = "usb-redir-ep", | ||||
|     .version_id = 1, | ||||
| @@ -2196,6 +2318,9 @@ static const VMStateDescription usbredir_ep_vmstate = { | ||||
|         { | ||||
|             .vmsd = &usbredir_bulk_receiving_vmstate, | ||||
|             .needed = usbredir_bulk_receiving_needed, | ||||
|         }, { | ||||
|             .vmsd = &usbredir_stream_vmstate, | ||||
|             .needed = usbredir_stream_needed, | ||||
|         }, { | ||||
|             /* empty */ | ||||
|         } | ||||
| @@ -2361,6 +2486,8 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) | ||||
|     uc->handle_control = usbredir_handle_control; | ||||
|     uc->flush_ep_queue = usbredir_flush_ep_queue; | ||||
|     uc->ep_stopped     = usbredir_ep_stopped; | ||||
|     uc->alloc_streams  = usbredir_alloc_streams; | ||||
|     uc->free_streams   = usbredir_free_streams; | ||||
|     dc->vmsd           = &usbredir_vmstate; | ||||
|     dc->props          = usbredir_properties; | ||||
|     set_bit(DEVICE_CATEGORY_MISC, 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, | ||||
|                                   void *opaque, const char *name, Error **errp) | ||||
| { | ||||
|     Error *err = NULL; | ||||
|     VirtIOBalloon *s = opaque; | ||||
|     int i; | ||||
|  | ||||
| @@ -116,17 +117,33 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     visit_start_struct(v, NULL, "guest-stats", name, 0, errp); | ||||
|     visit_type_int(v, &s->stats_last_update, "last-update", errp); | ||||
|  | ||||
|     visit_start_struct(v, NULL, NULL, "stats", 0, errp); | ||||
|     for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { | ||||
|         visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], | ||||
|                          errp); | ||||
|     visit_start_struct(v, NULL, "guest-stats", name, 0, &err); | ||||
|     if (err) { | ||||
|         goto out; | ||||
|     } | ||||
|     visit_type_int(v, &s->stats_last_update, "last-update", &err); | ||||
|     if (err) { | ||||
|         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, | ||||
|   | ||||
| @@ -120,6 +120,8 @@ typedef enum { | ||||
| /* BDRV_BLOCK_DATA: data is read from bs->file or another file | ||||
|  * BDRV_BLOCK_ZERO: sectors read as zero | ||||
|  * 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 | ||||
|  *                 was answered by the raw driver and that one | ||||
|  *                 should look in bs->file directly. | ||||
| @@ -141,10 +143,11 @@ typedef enum { | ||||
|  *  f    t        f       not allocated or unknown offset, read as zero | ||||
|  *  f    f        f       not allocated or unknown offset, read from backing_hd | ||||
|  */ | ||||
| #define BDRV_BLOCK_DATA         1 | ||||
| #define BDRV_BLOCK_ZERO         2 | ||||
| #define BDRV_BLOCK_OFFSET_VALID 4 | ||||
| #define BDRV_BLOCK_RAW          8 | ||||
| #define BDRV_BLOCK_DATA         0x01 | ||||
| #define BDRV_BLOCK_ZERO         0x02 | ||||
| #define BDRV_BLOCK_OFFSET_VALID 0x04 | ||||
| #define BDRV_BLOCK_RAW          0x08 | ||||
| #define BDRV_BLOCK_ALLOCATED    0x10 | ||||
| #define BDRV_BLOCK_OFFSET_MASK  BDRV_SECTOR_MASK | ||||
|  | ||||
| typedef enum { | ||||
|   | ||||
| @@ -364,6 +364,7 @@ struct BlockDriverState { | ||||
|     BlockJob *job; | ||||
|  | ||||
|     QDict *options; | ||||
|     BlockdevDetectZeroesOptions detect_zeroes; | ||||
| }; | ||||
|  | ||||
| int get_tmp_filename(char *filename, int size); | ||||
|   | ||||
							
								
								
									
										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. | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| #ifndef __KVM_S390_FLIC_H | ||||
| #define __KVM_S390_FLIC_H | ||||
| #ifndef __HW_S390_FLIC_H | ||||
| #define __HW_S390_FLIC_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) \ | ||||
|     OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC) | ||||
|  | ||||
| typedef struct KVMS390FLICState { | ||||
|     SysBusDevice parent_obj; | ||||
| #define TYPE_QEMU_S390_FLIC "s390-flic-qemu" | ||||
| #define QEMU_S390_FLIC(obj) \ | ||||
|     OBJECT_CHECK(QEMUS390FLICState, (obj), TYPE_QEMU_S390_FLIC) | ||||
|  | ||||
|     uint32_t fd; | ||||
| } KVMS390FLICState; | ||||
| typedef struct QEMUS390FLICState { | ||||
|     S390FLICState parent_obj; | ||||
| } QEMUS390FLICState; | ||||
|  | ||||
| void s390_flic_init(void); | ||||
|  | ||||
| S390FLICState *s390_get_flic(void); | ||||
|  | ||||
| #ifdef CONFIG_KVM | ||||
| void s390_flic_init(void); | ||||
| DeviceState *s390_flic_kvm_create(void); | ||||
| #else | ||||
| static inline void s390_flic_init(void) { } | ||||
| static inline DeviceState *s390_flic_kvm_create(void) | ||||
| { | ||||
|     return NULL; | ||||
| } | ||||
| #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); | ||||
|  | ||||
| /** | ||||
|  * 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. | ||||
|  */ | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
| #include "qapi/qmp/qobject.h" | ||||
| #include "qapi/qmp/qlist.h" | ||||
| #include "qemu/queue.h" | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| #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_array_split(QDict *src, QList **dst); | ||||
|  | ||||
| void qdict_join(QDict *dest, QDict *src, bool overwrite); | ||||
|  | ||||
| #endif /* QDICT_H */ | ||||
|   | ||||
| @@ -42,13 +42,9 @@ struct Visitor | ||||
|                         Error **errp); | ||||
|  | ||||
|     /* May be NULL */ | ||||
|     void (*start_optional)(Visitor *v, bool *present, const char *name, | ||||
|     void (*optional)(Visitor *v, bool *present, const char *name, | ||||
|                      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_uint16)(Visitor *v, uint16_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); | ||||
| GenericList *visit_next_list(Visitor *v, GenericList **list, 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); | ||||
| void visit_end_optional(Visitor *v, Error **errp); | ||||
| void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, | ||||
|                          const char *name, Error **errp); | ||||
| 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, | ||||
|                            struct iovec *src_iov, unsigned int src_cnt, | ||||
|                            size_t soffset, size_t sbytes); | ||||
| bool qemu_iovec_is_zero(QEMUIOVector *qiov); | ||||
| void qemu_iovec_destroy(QEMUIOVector *qiov); | ||||
| void qemu_iovec_reset(QEMUIOVector *qiov); | ||||
| 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 PcGuestInfo PcGuestInfo; | ||||
| typedef struct Range Range; | ||||
| typedef struct AdapterInfo AdapterInfo; | ||||
|  | ||||
| #endif /* QEMU_TYPEDEFS_H */ | ||||
|   | ||||
| @@ -300,7 +300,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension); | ||||
|         };                                                           \ | ||||
|         uint64_t args_tmp[] = { __VA_ARGS__ };                       \ | ||||
|         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++) {                \ | ||||
|             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__ };                       \ | ||||
|         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++) {                \ | ||||
|             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); | ||||
| 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, | ||||
|                                    EventNotifier *rn, int virq); | ||||
| int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq); | ||||
|   | ||||
							
								
								
									
										38
									
								
								kvm-all.c
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								kvm-all.c
									
									
									
									
									
								
							| @@ -27,6 +27,7 @@ | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "hw/hw.h" | ||||
| #include "hw/pci/msi.h" | ||||
| #include "hw/s390x/adapter.h" | ||||
| #include "exec/gdbstub.h" | ||||
| #include "sysemu/kvm.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); | ||||
| } | ||||
|  | ||||
| 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 */ | ||||
|  | ||||
| void kvm_init_irq_routing(KVMState *s) | ||||
| @@ -1256,6 +1286,11 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) | ||||
|     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) | ||||
| { | ||||
|     abort(); | ||||
| @@ -1285,7 +1320,8 @@ static int kvm_irqchip_create(KVMState *s) | ||||
|     int ret; | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -136,6 +136,11 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) | ||||
|     return -ENOSYS; | ||||
| } | ||||
|  | ||||
| int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) | ||||
| { | ||||
|     return -ENOSYS; | ||||
| } | ||||
|  | ||||
| int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, | ||||
|                                    EventNotifier *rn, int virq) | ||||
| { | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include <linux/types.h> | ||||
|  | ||||
| #define __KVM_S390 | ||||
| #define __KVM_HAVE_GUEST_DEBUG | ||||
|  | ||||
| /* Device control API: s390-specific devices */ | ||||
| #define KVM_DEV_FLIC_GET_ALL_IRQS	1 | ||||
| @@ -54,6 +55,13 @@ struct kvm_s390_io_adapter_req { | ||||
| 	__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 */ | ||||
| struct kvm_regs { | ||||
| 	/* general purpose regs for s390 */ | ||||
| @@ -72,11 +80,31 @@ struct kvm_fpu { | ||||
| 	__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 { | ||||
| 	__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 */ | ||||
| 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) | ||||
|   | ||||
| @@ -416,6 +416,8 @@ struct kvm_s390_psw { | ||||
| #define KVM_S390_INT_PFAULT_INIT	0xfffe0004u | ||||
| #define KVM_S390_INT_PFAULT_DONE	0xfffe0005u | ||||
| #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_SERVICE		0xffff2401u | ||||
| #define KVM_S390_INT_EMERGENCY		0xffff1201u | ||||
| @@ -515,6 +517,7 @@ enum { | ||||
| 	kvm_ioeventfd_flag_nr_pio, | ||||
| 	kvm_ioeventfd_flag_nr_deassign, | ||||
| 	kvm_ioeventfd_flag_nr_virtio_ccw_notify, | ||||
| 	kvm_ioeventfd_flag_nr_fast_mmio, | ||||
| 	kvm_ioeventfd_flag_nr_max, | ||||
| }; | ||||
|  | ||||
| @@ -529,7 +532,7 @@ enum { | ||||
| struct kvm_ioeventfd { | ||||
| 	__u64 datamatch; | ||||
| 	__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; | ||||
| 	__u32 flags; | ||||
| 	__u8  pad[36]; | ||||
| @@ -743,6 +746,8 @@ struct kvm_ppc_smmu_info { | ||||
| #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97 | ||||
| #define KVM_CAP_ENABLE_CAP_VM 98 | ||||
| #define KVM_CAP_S390_IRQCHIP 99 | ||||
| #define KVM_CAP_IOEVENTFD_NO_LENGTH 100 | ||||
| #define KVM_CAP_VM_ATTRIBUTES 101 | ||||
|  | ||||
| #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); | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
|     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) | ||||
| { | ||||
|     size_t len; | ||||
| @@ -4376,6 +4448,77 @@ void object_del_completion(ReadLineState *rs, int nb_args, const char *str) | ||||
|     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, | ||||
|                                              const mon_cmd_t *cmd_table, | ||||
|                                              char **args, | ||||
| @@ -4444,15 +4587,7 @@ static void monitor_find_completion_by_table(Monitor *mon, | ||||
|             break; | ||||
|         case 's': | ||||
|         case 'S': | ||||
|             if (!strcmp(cmd->name, "sendkey")) { | ||||
|                 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|?")) { | ||||
|             if (!strcmp(cmd->name, "help|?")) { | ||||
|                 monitor_find_completion_by_table(mon, cmd_table, | ||||
|                                                  &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) { | ||||
|             continue; | ||||
|         } | ||||
|         if (!strcmp(nc->name, id)) { | ||||
|         if (!id || !strcmp(nc->name, id)) { | ||||
|             if (ret < max) { | ||||
|                 ncs[ret] = nc; | ||||
|             } | ||||
|   | ||||
| @@ -691,7 +691,7 @@ | ||||
| # Information about current migration process. | ||||
| # | ||||
| # @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 | ||||
| #          has been initiated | ||||
| # | ||||
| @@ -942,6 +942,8 @@ | ||||
| # @encryption_key_missing: true if the backing device is encrypted but an | ||||
| #                          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_rd: read throughput limit in bytes per second is specified | ||||
| @@ -977,6 +979,7 @@ | ||||
|   'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', | ||||
|             '*backing_file': 'str', 'backing_file_depth': 'int', | ||||
|             'encrypted': 'bool', 'encryption_key_missing': 'bool', | ||||
|             'detect_zeroes': 'BlockdevDetectZeroesOptions', | ||||
|             'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', | ||||
|             'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', | ||||
|             'image': 'ImageInfo', | ||||
| @@ -4254,6 +4257,22 @@ | ||||
| { 'enum': 'BlockdevDiscardOptions', | ||||
|   '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 | ||||
| # | ||||
| @@ -4320,6 +4339,8 @@ | ||||
| #                 (default: enospc) | ||||
| # @read-only:     #optional whether the block device should be read-only | ||||
| #                 (default: false) | ||||
| # @detect-zeroes: #optional detect and optimize zero writes (Since 2.1) | ||||
| #                 (default: off) | ||||
| # | ||||
| # Since: 1.7 | ||||
| ## | ||||
| @@ -4332,7 +4353,8 @@ | ||||
|             '*aio': 'BlockdevAioOptions', | ||||
|             '*rerror': 'BlockdevOnError', | ||||
|             '*werror': 'BlockdevOnError', | ||||
|             '*read-only': 'bool' } } | ||||
|             '*read-only': 'bool', | ||||
|             '*detect-zeroes': 'BlockdevDetectZeroesOptions' } } | ||||
|  | ||||
| ## | ||||
| # @BlockdevOptionsFile | ||||
|   | ||||
| @@ -484,8 +484,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) | ||||
|  | ||||
|  | ||||
| static void | ||||
| opts_start_optional(Visitor *v, bool *present, const char *name, | ||||
|                        Error **errp) | ||||
| opts_optional(Visitor *v, bool *present, const char *name, Error **errp) | ||||
| { | ||||
|     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 | ||||
|      * skip some mandatory methods... */ | ||||
|  | ||||
|     ov->visitor.start_optional = &opts_start_optional; | ||||
|     ov->visitor.optional = &opts_optional; | ||||
|  | ||||
|     ov->opts_root = opts; | ||||
|  | ||||
|   | ||||
| @@ -17,46 +17,27 @@ | ||||
| #include "qapi/visitor.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, | ||||
|                         const char *name, size_t size, Error **errp) | ||||
| { | ||||
|     if (!error_is_set(errp)) { | ||||
|     v->start_struct(v, obj, kind, name, size, errp); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_end_struct(Visitor *v, Error **errp) | ||||
| { | ||||
|     assert(!error_is_set(errp)); | ||||
|     v->end_struct(v, errp); | ||||
| } | ||||
|  | ||||
| void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, | ||||
|                                  Error **errp) | ||||
| { | ||||
|     if (!error_is_set(errp) && v->start_implicit_struct) { | ||||
|     if (v->start_implicit_struct) { | ||||
|         v->start_implicit_struct(v, obj, size, errp); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_end_implicit_struct(Visitor *v, Error **errp) | ||||
| { | ||||
|     assert(!error_is_set(errp)); | ||||
|     if (v->end_implicit_struct) { | ||||
|         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) | ||||
| { | ||||
|     if (!error_is_set(errp)) { | ||||
|     v->start_list(v, name, errp); | ||||
|     } | ||||
| } | ||||
|  | ||||
| GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp) | ||||
| { | ||||
|     if (!error_is_set(errp)) { | ||||
|     return v->next_list(v, list, errp); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void visit_end_list(Visitor *v, Error **errp) | ||||
| { | ||||
|     assert(!error_is_set(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) | ||||
| { | ||||
|     if (!error_is_set(errp) && v->start_optional) { | ||||
|         v->start_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); | ||||
|     if (v->optional) { | ||||
|         v->optional(v, present, name, errp); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, | ||||
|                          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); | ||||
|     } | ||||
| } | ||||
| @@ -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[], | ||||
|                      const char *kind, const char *name, Error **errp) | ||||
| { | ||||
|     if (!error_is_set(errp)) { | ||||
|     v->type_enum(v, obj, strings, kind, name, 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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     int64_t value; | ||||
|     if (!error_is_set(errp)) { | ||||
|  | ||||
|     if (v->type_uint8) { | ||||
|         v->type_uint8(v, obj, name, errp); | ||||
|     } else { | ||||
| @@ -138,13 +101,12 @@ void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp) | ||||
|         } | ||||
|         *obj = value; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     int64_t value; | ||||
|     if (!error_is_set(errp)) { | ||||
|  | ||||
|     if (v->type_uint16) { | ||||
|         v->type_uint16(v, obj, name, errp); | ||||
|     } else { | ||||
| @@ -157,13 +119,12 @@ void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp | ||||
|         } | ||||
|         *obj = value; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     int64_t value; | ||||
|     if (!error_is_set(errp)) { | ||||
|  | ||||
|     if (v->type_uint32) { | ||||
|         v->type_uint32(v, obj, name, errp); | ||||
|     } else { | ||||
| @@ -176,13 +137,12 @@ void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp | ||||
|         } | ||||
|         *obj = value; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     int64_t value; | ||||
|     if (!error_is_set(errp)) { | ||||
|  | ||||
|     if (v->type_uint64) { | ||||
|         v->type_uint64(v, obj, name, errp); | ||||
|     } else { | ||||
| @@ -190,13 +150,12 @@ void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp | ||||
|         v->type_int(v, &value, name, errp); | ||||
|         *obj = value; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     int64_t value; | ||||
|     if (!error_is_set(errp)) { | ||||
|  | ||||
|     if (v->type_int8) { | ||||
|         v->type_int8(v, obj, name, errp); | ||||
|     } else { | ||||
| @@ -209,13 +168,12 @@ void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp) | ||||
|         } | ||||
|         *obj = value; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     int64_t value; | ||||
|     if (!error_is_set(errp)) { | ||||
|  | ||||
|     if (v->type_int16) { | ||||
|         v->type_int16(v, obj, name, errp); | ||||
|     } else { | ||||
| @@ -228,13 +186,12 @@ void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp) | ||||
|         } | ||||
|         *obj = value; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     int64_t value; | ||||
|     if (!error_is_set(errp)) { | ||||
|  | ||||
|     if (v->type_int32) { | ||||
|         v->type_int32(v, obj, name, errp); | ||||
|     } else { | ||||
| @@ -247,24 +204,21 @@ void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp) | ||||
|         } | ||||
|         *obj = value; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     if (!error_is_set(errp)) { | ||||
|     if (v->type_int64) { | ||||
|         v->type_int64(v, obj, name, errp); | ||||
|     } else { | ||||
|         v->type_int(v, obj, name, errp); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) | ||||
| { | ||||
|     int64_t value; | ||||
|     if (!error_is_set(errp)) { | ||||
|  | ||||
|     if (v->type_size) { | ||||
|         v->type_size(v, obj, name, errp); | ||||
|     } else if (v->type_uint64) { | ||||
| @@ -274,28 +228,21 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) | ||||
|         v->type_int(v, &value, name, errp); | ||||
|         *obj = value; | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void output_type_enum(Visitor *v, int *obj, const char *strings[], | ||||
| @@ -321,13 +268,15 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[], | ||||
|                      const char *kind, const char *name, | ||||
|                      Error **errp) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
|     int64_t value = 0; | ||||
|     char *enum_str; | ||||
|  | ||||
|     assert(strings); | ||||
|  | ||||
|     visit_type_str(v, &enum_str, name, errp); | ||||
|     if (error_is_set(errp)) { | ||||
|     visit_type_str(v, &enum_str, name, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         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, | ||||
|                                      const char *name, Error **errp) | ||||
| static void qmp_input_optional(Visitor *v, bool *present, const char *name, | ||||
|                                Error **errp) | ||||
| { | ||||
|     QmpInputVisitor *qiv = to_qiv(v); | ||||
|     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_str = qmp_input_type_str; | ||||
|     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; | ||||
|  | ||||
|     qmp_input_push(v, obj, NULL); | ||||
|   | ||||
| @@ -120,8 +120,8 @@ static void parse_type_number(Visitor *v, double *obj, const char *name, | ||||
|     *obj = val; | ||||
| } | ||||
|  | ||||
| static void parse_start_optional(Visitor *v, bool *present, | ||||
|                                  const char *name, Error **errp) | ||||
| static void parse_optional(Visitor *v, bool *present, const char *name, | ||||
|                            Error **errp) | ||||
| { | ||||
|     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_str = parse_type_str; | ||||
|     v->visitor.type_number = parse_type_number; | ||||
|     v->visitor.start_optional = parse_start_optional; | ||||
|     v->visitor.optional = parse_optional; | ||||
|  | ||||
|     v->string = str; | ||||
|     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), | ||||
|                                     Error **errp) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
|     CharDriver *cd; | ||||
|     CharDriverState *chr; | ||||
|     GSList *i; | ||||
| @@ -3245,13 +3246,14 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, | ||||
|         chr = NULL; | ||||
|         backend->kind = cd->kind; | ||||
|         if (cd->parse) { | ||||
|             cd->parse(opts, backend, errp); | ||||
|             if (error_is_set(errp)) { | ||||
|             cd->parse(opts, backend, &local_err); | ||||
|             if (local_err) { | ||||
|                 error_propagate(errp, local_err); | ||||
|                 goto qapi_out; | ||||
|             } | ||||
|         } | ||||
|         ret = qmp_chardev_add(bid ? bid : id, backend, errp); | ||||
|         if (error_is_set(errp)) { | ||||
|         if (!ret) { | ||||
|             goto qapi_out; | ||||
|         } | ||||
|  | ||||
| @@ -3263,7 +3265,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, | ||||
|             backend->kind = CHARDEV_BACKEND_KIND_MUX; | ||||
|             backend->mux->chardev = g_strdup(bid); | ||||
|             ret = qmp_chardev_add(id, backend, errp); | ||||
|             if (error_is_set(errp)) { | ||||
|             if (!ret) { | ||||
|                 chr = qemu_chr_find(bid); | ||||
|                 qemu_chr_delete(chr); | ||||
|                 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) | ||||
| { | ||||
|     int flags, in = -1, out = -1; | ||||
|     int flags, in = -1, out; | ||||
|  | ||||
|     flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY; | ||||
|     out = qmp_chardev_open_file_source(file->out, flags, errp); | ||||
|     if (error_is_set(errp)) { | ||||
|     if (out < 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     if (file->has_in) { | ||||
|         flags = O_RDONLY; | ||||
|         in = qmp_chardev_open_file_source(file->in, flags, errp); | ||||
|         if (error_is_set(errp)) { | ||||
|         if (in < 0) { | ||||
|             qemu_close(out); | ||||
|             return NULL; | ||||
|         } | ||||
| @@ -3647,7 +3649,7 @@ static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial, | ||||
|     int fd; | ||||
|  | ||||
|     fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); | ||||
|     if (error_is_set(errp)) { | ||||
|     if (fd < 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|     qemu_set_nonblock(fd); | ||||
| @@ -3665,7 +3667,7 @@ static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, | ||||
|     int fd; | ||||
|  | ||||
|     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp); | ||||
|     if (error_is_set(errp)) { | ||||
|     if (fd < 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return qemu_chr_open_pp_fd(fd); | ||||
| @@ -3692,7 +3694,7 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, | ||||
|     } else { | ||||
|         fd = socket_connect(addr, errp, NULL, NULL); | ||||
|     } | ||||
|     if (error_is_set(errp)) { | ||||
|     if (fd < 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|     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; | ||||
|  | ||||
|     fd = socket_dgram(udp->remote, udp->local, errp); | ||||
|     if (error_is_set(errp)) { | ||||
|     if (fd < 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return qemu_chr_open_udp_fd(fd); | ||||
| @@ -3796,7 +3798,13 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, | ||||
|         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"); | ||||
|     } | ||||
|     if (chr) { | ||||
|   | ||||
| @@ -70,11 +70,8 @@ static void add_format_to_seq(void *opaque, const char *fmt_name) | ||||
| { | ||||
|     GSequence *seq = opaque; | ||||
|  | ||||
|     if (!g_sequence_lookup(seq, (gpointer)fmt_name, | ||||
|                            compare_data, NULL)) { | ||||
|     g_sequence_insert_sorted(seq, (gpointer)fmt_name, | ||||
|                              compare_data, NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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" | ||||
|     "       [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\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" | ||||
|     "       [[,iops=i]|[[,iops_rd=r][,iops_wr=w]]]\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} | ||||
| @var{copy-on-read} is "on" or "off" and enables whether to copy read backing | ||||
| 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 | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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 | ||||
|  | ||||
| STEXI | ||||
|   | ||||
| @@ -2032,6 +2032,8 @@ Each json-object contain the following: | ||||
|          - "iops_rd_max":  read 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) | ||||
|          - "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 | ||||
|             the following: | ||||
|              - "filename": image file name (json-string) | ||||
| @@ -2108,6 +2110,7 @@ Example: | ||||
|                "iops_rd_max": 0, | ||||
|                "iops_wr_max": 0, | ||||
|                "iops_size": 0, | ||||
|                "detect_zeroes": "on", | ||||
|                "image":{ | ||||
|                   "filename":"disks/test.qcow2", | ||||
|                   "format":"qcow2", | ||||
| @@ -2937,7 +2940,7 @@ block migration status. | ||||
| The main json-object contains the following: | ||||
|  | ||||
| - "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 | ||||
|                 migration has ended, it returns the total migration | ||||
|                 time (json-int) | ||||
|   | ||||
| @@ -665,3 +665,35 @@ void qdict_array_split(QDict *src, QList **dst) | ||||
|         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 | ||||
| # | ||||
| # Copyright IBM, Corp. 2011 | ||||
| # Copyright (C) 2014 Red Hat, Inc. | ||||
| # | ||||
| # Authors: | ||||
| #  Anthony Liguori <aliguori@us.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. | ||||
| # See the COPYING file in the top-level directory. | ||||
|  | ||||
| from ordereddict import OrderedDict | ||||
| from qapi import * | ||||
| import re | ||||
| import sys | ||||
| import os | ||||
| 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() | ||||
|  | ||||
| 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): | ||||
|     ret = "" | ||||
|     arglist="" | ||||
| @@ -49,15 +61,14 @@ def gen_sync_call(name, args, ret_type, indent=0): | ||||
|         arglist += "%s, " % (c_var(argname)) | ||||
|     push_indent(indent) | ||||
|     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() | ||||
|     if ret_type: | ||||
|         ret += "\n" + gen_err_check('local_err') | ||||
|         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() | ||||
|     pop_indent(indent) | ||||
| @@ -67,18 +78,19 @@ if (!error_is_set(errp)) { | ||||
| def gen_marshal_output_call(name, ret_type): | ||||
|     if not ret_type: | ||||
|         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 = "" | ||||
|  | ||||
|     push_indent() | ||||
|     if len(args) > 0: | ||||
|         ret += mcgen(''' | ||||
| QmpInputVisitor *mi; | ||||
| QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s); | ||||
| QapiDeallocVisitor *md; | ||||
| Visitor *v; | ||||
| ''') | ||||
| ''', | ||||
|                      obj=obj) | ||||
|     pop_indent() | ||||
|  | ||||
|     return ret.rstrip() | ||||
| @@ -99,16 +111,17 @@ bool has_%(argname)s = false; | ||||
|                          argname=c_var(argname), argtype=c_type(argtype)) | ||||
|         else: | ||||
|             ret += mcgen(''' | ||||
| %(argtype)s %(argname)s; | ||||
| %(argtype)s %(argname)s = {0}; | ||||
| ''', | ||||
|                          argname=c_var(argname), argtype=c_type(argtype)) | ||||
|  | ||||
|     pop_indent() | ||||
|     return ret.rstrip() | ||||
|  | ||||
| def gen_visitor_input_block(args, obj, dealloc=False): | ||||
| def gen_visitor_input_block(args, dealloc=False): | ||||
|     ret = "" | ||||
|     errparg = 'errp' | ||||
|     errparg = '&local_err' | ||||
|     errarg = 'local_err' | ||||
|  | ||||
|     if len(args) == 0: | ||||
|         return ret | ||||
| @@ -117,44 +130,44 @@ def gen_visitor_input_block(args, obj, dealloc=False): | ||||
|  | ||||
|     if dealloc: | ||||
|         errparg = 'NULL' | ||||
|         errarg = None; | ||||
|         ret += mcgen(''' | ||||
| qmp_input_visitor_cleanup(mi); | ||||
| md = qapi_dealloc_visitor_new(); | ||||
| v = qapi_dealloc_get_visitor(md); | ||||
| ''') | ||||
|     else: | ||||
|         ret += mcgen(''' | ||||
| mi = qmp_input_visitor_new_strict(%(obj)s); | ||||
| v = qmp_input_get_visitor(mi); | ||||
| ''', | ||||
|                      obj=obj) | ||||
| ''') | ||||
|  | ||||
|     for argname, argtype, optional, structured in parse_args(args): | ||||
|         if optional: | ||||
|             ret += mcgen(''' | ||||
| visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); | ||||
| if (has_%(c_name)s) { | ||||
| visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); | ||||
| ''', | ||||
|                          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() | ||||
|         ret += mcgen(''' | ||||
| %(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); | ||||
| ''', | ||||
|                      c_name=c_var(argname), name=argname, argtype=argtype, | ||||
|                      visitor=type_visitor(argtype), errp=errparg) | ||||
|         ret += gen_err_check(errarg) | ||||
|         if optional: | ||||
|             pop_indent() | ||||
|             ret += mcgen(''' | ||||
| } | ||||
| visit_end_optional(v, %(errp)s); | ||||
| ''', errp=errparg) | ||||
| ''') | ||||
|  | ||||
|     if dealloc: | ||||
|         ret += mcgen(''' | ||||
| qapi_dealloc_visitor_cleanup(md); | ||||
| ''') | ||||
|     else: | ||||
|         ret += mcgen(''' | ||||
| qmp_input_visitor_cleanup(mi); | ||||
| ''') | ||||
|     pop_indent() | ||||
|     return ret.rstrip() | ||||
| @@ -166,16 +179,22 @@ def gen_marshal_output(name, args, ret_type, middle_mode): | ||||
|     ret = mcgen(''' | ||||
| 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(); | ||||
|     QapiDeallocVisitor *md; | ||||
|     Visitor *v; | ||||
|  | ||||
|     v = qmp_output_get_visitor(mo); | ||||
|     %(visitor)s(v, &ret_in, "unused", errp); | ||||
|     if (!error_is_set(errp)) { | ||||
|         *ret_out = qmp_output_get_qobject(mo); | ||||
|     %(visitor)s(v, &ret_in, "unused", &local_err); | ||||
|     if (local_err) { | ||||
|         goto out; | ||||
|     } | ||||
|     *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); | ||||
|     %(visitor)s(v, &ret_in, "unused", NULL); | ||||
|     qapi_dealloc_visitor_cleanup(md); | ||||
| @@ -200,13 +219,12 @@ def gen_marshal_input(name, args, ret_type, middle_mode): | ||||
|     ret = mcgen(''' | ||||
| %(header)s | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
| ''', | ||||
|                 header=hdr) | ||||
|  | ||||
|     if middle_mode: | ||||
|         ret += mcgen(''' | ||||
|     Error *local_err = NULL; | ||||
|     Error **errp = &local_err; | ||||
|     QDict *args = (QDict *)qdict; | ||||
| ''') | ||||
|  | ||||
| @@ -228,29 +246,32 @@ def gen_marshal_input(name, args, ret_type, middle_mode): | ||||
| %(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_block=gen_visitor_input_block(args, "QOBJECT(args)")) | ||||
|                      visitor_input_block=gen_visitor_input_block(args)) | ||||
|     else: | ||||
|         ret += mcgen(''' | ||||
|  | ||||
|     (void)args; | ||||
| ''') | ||||
|  | ||||
|     ret += mcgen(''' | ||||
|     if (error_is_set(errp)) { | ||||
|         goto out; | ||||
|     } | ||||
| %(sync_call)s | ||||
| ''', | ||||
|                  sync_call=gen_sync_call(name, args, ret_type, indent=4)) | ||||
|     if re.search('^ *goto out\\;', ret, re.MULTILINE): | ||||
|         ret += mcgen(''' | ||||
|  | ||||
| out: | ||||
| ''') | ||||
|     if not middle_mode: | ||||
|         ret += mcgen(''' | ||||
|     error_propagate(errp, local_err); | ||||
| ''') | ||||
|     ret += mcgen(''' | ||||
| %(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)) | ||||
|  | ||||
|     if middle_mode: | ||||
|   | ||||
| @@ -2,21 +2,47 @@ | ||||
| # QAPI visitor generator | ||||
| # | ||||
| # Copyright IBM, Corp. 2011 | ||||
| # Copyright (C) 2014 Red Hat, Inc. | ||||
| # | ||||
| # Authors: | ||||
| #  Anthony Liguori <aliguori@us.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. | ||||
| # See the COPYING file in the top-level directory. | ||||
|  | ||||
| from ordereddict import OrderedDict | ||||
| from qapi import * | ||||
| import re | ||||
| import sys | ||||
| import os | ||||
| import getopt | ||||
| 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): | ||||
|     substructs = [] | ||||
|     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) | ||||
|             ret += generate_visit_struct_fields(name, nested_field_prefix, | ||||
|                                                 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(''' | ||||
|  | ||||
| @@ -47,12 +86,9 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error * | ||||
|  | ||||
|     if base: | ||||
|         ret += mcgen(''' | ||||
| visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(type)s), &err); | ||||
| if (!err) { | ||||
|     visit_type_%(type)s_fields(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); | ||||
|     error_propagate(errp, err); | ||||
|     err = NULL; | ||||
|     visit_end_implicit_struct(m, &err); | ||||
| visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); | ||||
| if (err) { | ||||
|     goto out; | ||||
| } | ||||
| ''', | ||||
|                      c_prefix=c_var(field_prefix), | ||||
| @@ -61,15 +97,18 @@ if (!err) { | ||||
|     for argname, argentry, optional, structured in parse_args(members): | ||||
|         if optional: | ||||
|             ret += mcgen(''' | ||||
| visit_start_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); | ||||
| if ((*obj)->%(prefix)shas_%(c_name)s) { | ||||
| visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); | ||||
| if (!err && (*obj)->%(prefix)shas_%(c_name)s) { | ||||
| ''', | ||||
|                          c_prefix=c_var(field_prefix), prefix=field_prefix, | ||||
|                          c_name=c_var(argname), name=argname) | ||||
|             push_indent() | ||||
|  | ||||
|         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: | ||||
|             ret += mcgen(''' | ||||
| 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() | ||||
|             ret += mcgen(''' | ||||
| } | ||||
| visit_end_optional(m, &err); | ||||
| ''') | ||||
|         ret += mcgen(''' | ||||
| if (err) { | ||||
|     goto out; | ||||
| } | ||||
| ''') | ||||
|  | ||||
|     pop_indent() | ||||
|     if re.search('^ *goto out\\;', ret, re.MULTILINE): | ||||
|         ret += mcgen(''' | ||||
|  | ||||
| out: | ||||
| ''') | ||||
|     ret += mcgen(''' | ||||
|     error_propagate(errp, err); | ||||
| } | ||||
| ''') | ||||
| @@ -96,9 +143,9 @@ visit_end_optional(m, &err); | ||||
|  | ||||
| def generate_visit_struct_body(field_prefix, name, members): | ||||
|     ret = mcgen(''' | ||||
| if (!error_is_set(errp)) { | ||||
|     Error *err = NULL; | ||||
|  | ||||
| ''') | ||||
|     push_indent() | ||||
|  | ||||
|     if not field_prefix: | ||||
|         full_name = name | ||||
| @@ -107,36 +154,26 @@ if (!error_is_set(errp)) { | ||||
|  | ||||
|     if len(field_prefix): | ||||
|         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) | ||||
|     else: | ||||
|         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) | ||||
|  | ||||
|     ret += mcgen(''' | ||||
| if (!err) { | ||||
|     if (!err) { | ||||
|         if (*obj) { | ||||
|         visit_type_%(name)s_fields(m, obj, &err); | ||||
|         error_propagate(errp, err); | ||||
|         err = NULL; | ||||
|             visit_type_%(name)s_fields(m, obj, errp); | ||||
|         } | ||||
| ''', | ||||
|         name=full_name) | ||||
|  | ||||
|     pop_indent() | ||||
|     ret += mcgen(''' | ||||
|         /* Always call end_struct if start_struct succeeded.  */ | ||||
|         visit_end_struct(m, &err); | ||||
|     } | ||||
|     error_propagate(errp, err); | ||||
| } | ||||
| ''') | ||||
| ''', | ||||
|         name=full_name) | ||||
|  | ||||
|     return ret | ||||
|  | ||||
| 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) | ||||
|  | ||||
|     push_indent() | ||||
|     ret += generate_visit_struct_body("", name, members) | ||||
|     pop_indent() | ||||
|  | ||||
|     ret += mcgen(''' | ||||
| } | ||||
| @@ -168,24 +203,26 @@ def generate_visit_list(name, members): | ||||
|  | ||||
| void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp) | ||||
| { | ||||
|     GenericList *i, **prev = (GenericList **)obj; | ||||
|     Error *err = NULL; | ||||
|     GenericList *i, **prev; | ||||
|  | ||||
|     if (!error_is_set(errp)) { | ||||
|     visit_start_list(m, name, &err); | ||||
|         if (!err) { | ||||
|             for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) { | ||||
|     if (err) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     for (prev = (GenericList **)obj; | ||||
|          !err && (i = visit_next_list(m, prev, &err)) != NULL; | ||||
|          prev = &i) { | ||||
|         %(name)sList *native_i = (%(name)sList *)i; | ||||
|         visit_type_%(name)s(m, &native_i->value, NULL, &err); | ||||
|     } | ||||
|  | ||||
|     error_propagate(errp, err); | ||||
|     err = NULL; | ||||
|  | ||||
|             /* Always call end_list if start_list succeeded.  */ | ||||
|     visit_end_list(m, &err); | ||||
|         } | ||||
| out: | ||||
|     error_propagate(errp, err); | ||||
|     } | ||||
| } | ||||
| ''', | ||||
|                 name=name) | ||||
| @@ -207,9 +244,14 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** | ||||
| { | ||||
|     Error *err = NULL; | ||||
|  | ||||
|     if (!error_is_set(errp)) { | ||||
|     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); | ||||
|     if (err) { | ||||
|         goto out_end; | ||||
|     } | ||||
|     switch ((*obj)->kind) { | ||||
| ''', | ||||
|     name=name) | ||||
| @@ -237,10 +279,12 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** | ||||
|     default: | ||||
|         abort(); | ||||
|     } | ||||
| out_end: | ||||
|     error_propagate(errp, err); | ||||
|     err = NULL; | ||||
|     visit_end_implicit_struct(m, &err); | ||||
|     } | ||||
| out: | ||||
|     error_propagate(errp, err); | ||||
| } | ||||
| ''') | ||||
|  | ||||
| @@ -277,39 +321,42 @@ def generate_visit_union(expr): | ||||
|             del base_fields[discriminator] | ||||
|         ret += generate_visit_struct_fields(name, "", "", base_fields) | ||||
|  | ||||
|     if discriminator: | ||||
|         for key in members: | ||||
|             ret += generate_visit_implicit_struct(members[key]) | ||||
|  | ||||
|     ret += mcgen(''' | ||||
|  | ||||
| void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) | ||||
| { | ||||
|     Error *err = NULL; | ||||
|  | ||||
|     if (!error_is_set(errp)) { | ||||
|     visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); | ||||
|         if (!err) { | ||||
|     if (err) { | ||||
|         goto out; | ||||
|     } | ||||
|     if (*obj) { | ||||
| ''', | ||||
|                  name=name) | ||||
|  | ||||
|  | ||||
|     push_indent() | ||||
|     push_indent() | ||||
|     push_indent() | ||||
|  | ||||
|     if base: | ||||
|         ret += mcgen(''' | ||||
|         visit_type_%(name)s_fields(m, obj, &err); | ||||
|         if (err) { | ||||
|             goto out_obj; | ||||
|         } | ||||
| ''', | ||||
|             name=name) | ||||
|  | ||||
|     pop_indent() | ||||
|  | ||||
|     if not discriminator: | ||||
|         disc_key = "type" | ||||
|     else: | ||||
|         disc_key = discriminator | ||||
|     ret += mcgen(''' | ||||
|         visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); | ||||
|         if (!err) { | ||||
|         if (err) { | ||||
|             goto out_obj; | ||||
|         } | ||||
|         switch ((*obj)->kind) { | ||||
| ''', | ||||
|                  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: | ||||
|             fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' | ||||
|         else: | ||||
|             fmt = '''visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(c_type)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); | ||||
|                 }''' | ||||
|             fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);' | ||||
|  | ||||
|         enum_full_value = generate_enum_full_value(disc_type, key) | ||||
|         ret += mcgen(''' | ||||
| @@ -341,23 +382,14 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** | ||||
|         default: | ||||
|             abort(); | ||||
|         } | ||||
|         } | ||||
| out_obj: | ||||
|         error_propagate(errp, err); | ||||
|         err = NULL; | ||||
|     } | ||||
| ''') | ||||
|     pop_indent() | ||||
|     ret += mcgen(''' | ||||
|         /* Always call end_struct if start_struct succeeded.  */ | ||||
|     visit_end_struct(m, &err); | ||||
|     } | ||||
| out: | ||||
|     error_propagate(errp, err); | ||||
| } | ||||
| ''') | ||||
|  | ||||
|     pop_indent(); | ||||
|     ret += mcgen(''' | ||||
| } | ||||
| ''') | ||||
|  | ||||
|     return ret | ||||
| @@ -476,7 +508,7 @@ fdecl.write(mcgen(''' | ||||
| /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | ||||
|  | ||||
| /* | ||||
|  * schema-defined QAPI visitor function | ||||
|  * schema-defined QAPI visitor functions | ||||
|  * | ||||
|  * Copyright IBM, Corp. 2011 | ||||
|  * | ||||
|   | ||||
| @@ -73,13 +73,18 @@ class QAPIExprError(Exception): | ||||
|  | ||||
| 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) | ||||
|         if input_relname is None: | ||||
|             input_relname = fp.name | ||||
|         self.input_dir = os.path.dirname(input_fname) | ||||
|         self.input_file = input_relname | ||||
|         self.include_hist = include_hist + [(input_relname, input_fname)] | ||||
|         previously_included.append(input_fname) | ||||
|         self.parent_info = parent_info | ||||
|         self.src = fp.read() | ||||
|         if self.src == '' or self.src[-1] != '\n': | ||||
| @@ -106,13 +111,16 @@ class QAPISchema: | ||||
|                        for elem in self.include_hist): | ||||
|                     raise QAPIExprError(expr_info, "Inclusion loop for %s" | ||||
|                                         % include) | ||||
|                 # skip multiple include of the same file | ||||
|                 if include_path in previously_included: | ||||
|                     continue | ||||
|                 try: | ||||
|                     fobj = open(include_path, 'r') | ||||
|                 except IOError as e: | ||||
|                 except IOError, e: | ||||
|                     raise QAPIExprError(expr_info, | ||||
|                                         '%s: %s' % (e.strerror, include)) | ||||
|                 exprs_include = QAPISchema(fobj, include, | ||||
|                                            self.include_hist, expr_info) | ||||
|                 exprs_include = QAPISchema(fobj, include, self.include_hist, | ||||
|                                            previously_included, expr_info) | ||||
|                 self.exprs.extend(exprs_include.exprs) | ||||
|             else: | ||||
|                 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, | ||||
|                                   CPUState *cpu, void *opaque); | ||||
| 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_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; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
|     if (mask & PSW_MASK_WAIT) { | ||||
|   | ||||
| @@ -36,6 +36,7 @@ | ||||
| #include "sysemu/device_tree.h" | ||||
| #include "qapi/qmp/qjson.h" | ||||
| #include "monitor/monitor.h" | ||||
| #include "exec/gdbstub.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| /* #define DEBUG_KVM */ | ||||
| @@ -86,6 +87,14 @@ | ||||
| #define ICPT_CPU_STOP                   0x28 | ||||
| #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[] = { | ||||
|     KVM_CAP_LAST_INFO | ||||
| }; | ||||
| @@ -320,12 +329,16 @@ static void *legacy_s390_alloc(size_t size) | ||||
|     return mem == MAP_FAILED ? NULL : mem; | ||||
| } | ||||
|  | ||||
| /* DIAG 501 is used for sw breakpoints */ | ||||
| static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; | ||||
|  | ||||
| int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) | ||||
| { | ||||
|     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) || | ||||
|         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 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) | ||||
| { | ||||
|     uint8_t t[4]; | ||||
|     static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; | ||||
|     uint8_t t[sizeof(diag_501)]; | ||||
|  | ||||
|     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; | ||||
|     } else if (memcmp(t, diag_501, 4)) { | ||||
|     } else if (memcmp(t, diag_501, sizeof(diag_501))) { | ||||
|         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 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, | ||||
|                                   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 insert_hw_breakpoint(addr, len, type); | ||||
| } | ||||
|  | ||||
| int kvm_arch_remove_hw_breakpoint(target_ulong addr, | ||||
|                                   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) | ||||
| { | ||||
|     nb_hw_breakpoints = 0; | ||||
|     g_free(hw_breakpoints); | ||||
|     hw_breakpoints = NULL; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| @@ -579,6 +694,22 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run) | ||||
|     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 | ||||
|  | ||||
| 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); | ||||
|         break; | ||||
|     case DIAG_KVM_BREAKPOINT: | ||||
|         sleep(10); | ||||
|         r = handle_sw_breakpoint(cpu, run); | ||||
|         break; | ||||
|     default: | ||||
|         DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code); | ||||
| @@ -701,7 +832,7 @@ out: | ||||
|     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); | ||||
|     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) { | ||||
|         r = 0; | ||||
|         enter_pgmcheck(cpu, 0x0001); | ||||
|     } | ||||
|  | ||||
|     return r; | ||||
| } | ||||
|  | ||||
| static bool is_special_wait_psw(CPUState *cs) | ||||
| @@ -749,7 +883,7 @@ static int handle_intercept(S390CPU *cpu) | ||||
|             (long)cs->kvm_run->psw_addr); | ||||
|     switch (icpt_code) { | ||||
|         case ICPT_INSTRUCTION: | ||||
|             handle_instruction(cpu, run); | ||||
|             r = handle_instruction(cpu, run); | ||||
|             break; | ||||
|         case ICPT_WAITPSW: | ||||
|             /* 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) | ||||
| { | ||||
|     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) | ||||
| @@ -911,6 +1074,16 @@ void kvm_s390_enable_css_support(S390CPU *cpu) | ||||
|  | ||||
| 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, | ||||
|   | ||||
| @@ -193,7 +193,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ | ||||
|         flat-union-string-discriminator.json \ | ||||
|         include-simple.json include-relpath.json include-format-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 | ||||
|  | ||||
|   | ||||
| @@ -444,6 +444,92 @@ static void qdict_array_split_test(void) | ||||
|     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 | ||||
|  */ | ||||
| @@ -584,6 +670,7 @@ int main(int argc, char **argv) | ||||
|     g_test_add_func("/public/iterapi", qdict_iterapi_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/join", qdict_join_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); | ||||
|   | ||||
							
								
								
									
										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' % mid_img, test_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() | ||||
|  | ||||
|     def tearDown(self): | ||||
|   | ||||
| @@ -47,6 +47,11 @@ _supported_os Linux | ||||
| _default_cache_mode "writethrough" | ||||
| _supported_cache_modes "writethrough" | ||||
|  | ||||
| _no_dump_exec() | ||||
| { | ||||
|     (ulimit -c 0; exec "$@") | ||||
| } | ||||
|  | ||||
| size=128M | ||||
|  | ||||
| echo | ||||
| @@ -67,10 +72,7 @@ echo "== Creating a dirty image file ==" | ||||
| IMGOPTS="compat=1.1,lazy_refcounts=on" | ||||
| _make_test_img $size | ||||
|  | ||||
| old_ulimit=$(ulimit -c) | ||||
| 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" | ||||
| _no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io | ||||
|  | ||||
| # The dirty bit must be set | ||||
| ./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" | ||||
| _make_test_img $size | ||||
|  | ||||
| old_ulimit=$(ulimit -c) | ||||
| 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" | ||||
| _no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io | ||||
|  | ||||
| # The dirty bit must be set | ||||
| ./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" | ||||
| _make_test_img $size | ||||
|  | ||||
| old_ulimit=$(ulimit -c) | ||||
| 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" | ||||
| _no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io | ||||
|  | ||||
| # The dirty bit must not be set since lazy_refcounts=off | ||||
| ./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  | ||||
| wrote 512/512 bytes at offset 0 | ||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| ./039: Aborted                 ( ulimit -c 0; exec "$@" ) | ||||
| incompatible_features     0x1 | ||||
| ERROR cluster 5 refcount=0 reference=1 | ||||
| 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  | ||||
| wrote 512/512 bytes at offset 0 | ||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| ./039: Aborted                 ( ulimit -c 0; exec "$@" ) | ||||
| incompatible_features     0x1 | ||||
| Repairing cluster 5 refcount=0 reference=1 | ||||
| wrote 512/512 bytes at offset 0 | ||||
| @@ -52,6 +54,7 @@ incompatible_features     0x0 | ||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728  | ||||
| wrote 512/512 bytes at offset 0 | ||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||
| ./039: Aborted                 ( ulimit -c 0; exec "$@" ) | ||||
| incompatible_features     0x0 | ||||
| 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 | ||||
| QMP_VERSION | ||||
| {"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": {}} | ||||
| {"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 | ||||
| QMP_VERSION | ||||
| {"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": {}} | ||||
| @@ -44,7 +44,7 @@ Testing: | ||||
| QMP_VERSION | ||||
| {"return": {}} | ||||
| {"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": {}} | ||||
| @@ -64,14 +64,14 @@ Testing: | ||||
| QMP_VERSION | ||||
| {"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": {}} | ||||
| {"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": "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": {}} | ||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"} | ||||
| {"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 \ | ||||
|                                                       | _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 | ||||
| echo "*** done" | ||||
| rm -f $seq.full | ||||
|   | ||||
| @@ -18,4 +18,11 @@ No errors were found on the image. | ||||
| === Verify open image read-only succeeds after log replay === | ||||
| read 18874368/18874368 bytes at offset 0 | ||||
| 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 | ||||
|   | ||||
							
								
								
									
										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_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" | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -95,5 +95,7 @@ | ||||
| 086 rw auto quick | ||||
| 087 rw auto | ||||
| 088 rw auto | ||||
| 089 rw auto quick | ||||
| 090 rw auto quick | ||||
| 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, | ||||
|                                   const char *name, Error **errp) | ||||
| { | ||||
|     Error *err = NULL; | ||||
|  | ||||
|     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_bool(v, &(*obj)->boolean, "boolean", errp); | ||||
|     visit_type_str(v, &(*obj)->string, "string", 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); | ||||
|  | ||||
|     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, | ||||
|   | ||||
| @@ -199,16 +199,24 @@ static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | ||||
|  | ||||
|     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), | ||||
|                        &err); | ||||
|     if (!err) { | ||||
|     if (err) { | ||||
|         goto out; | ||||
|     } | ||||
|     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); | ||||
|  | ||||
|         /* Always call end_struct if start_struct succeeded.  */ | ||||
| out_end: | ||||
|     error_propagate(errp, err); | ||||
|     err = NULL; | ||||
|     visit_end_struct(v, &err); | ||||
|     } | ||||
| out: | ||||
|     error_propagate(errp, err); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -176,14 +176,30 @@ typedef struct TestStruct | ||||
| static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | ||||
|                                   const char *name, Error **errp) | ||||
| { | ||||
|     Error *err = NULL; | ||||
|  | ||||
|     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_bool(v, &(*obj)->boolean, "boolean", errp); | ||||
|     visit_type_str(v, &(*obj)->string, "string", 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); | ||||
|  | ||||
|     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, | ||||
|   | ||||
| @@ -195,13 +195,29 @@ typedef struct TestStruct | ||||
| static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | ||||
|                                   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_type_bool(v, &(*obj)->boolean, "boolean", errp); | ||||
|     visit_type_str(v, &(*obj)->string, "string", errp); | ||||
|     visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err); | ||||
|     if (err) { | ||||
|         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) | ||||
|   | ||||
| @@ -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_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d" | ||||
| megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x" | ||||
| megasas_dcmd_ld_list_query(int cmd, int flags) "scmd %d: DCMD LD list query flags %x" | ||||
| megasas_dcmd_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_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 "" | ||||
| megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x" | ||||
| megasas_reset(void) "Reset" | ||||
| megasas_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_msi_raise(int vector) "vector %d" | ||||
| megasas_irq_lower(void) "INTx" | ||||
| megasas_irq_raise(void) "INTx" | ||||
| megasas_intr_enabled(void) "Interrupts enabled" | ||||
| megasas_intr_disabled(void) "Interrupts disabled" | ||||
| megasas_msix_enabled(int vector) "vector %d" | ||||
| megasas_msi_enabled(int vector) "vector %d" | ||||
| megasas_mmio_readl(unsigned long addr, uint32_t val) "addr 0x%lx: 0x%x" | ||||
| megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx" | ||||
| megasas_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x" | ||||
|   | ||||
| @@ -142,11 +142,6 @@ Error *error_copy(const Error *err) | ||||
|     return err_new; | ||||
| } | ||||
|  | ||||
| bool error_is_set(Error **errp) | ||||
| { | ||||
|     return (errp && *errp); | ||||
| } | ||||
|  | ||||
| ErrorClass error_get_class(const Error *err) | ||||
| { | ||||
|     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); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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) | ||||
| { | ||||
|     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, | ||||
|                       NonBlockingConnectHandler *callback, void *opaque) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
|     struct addrinfo *res, *e; | ||||
|     int sock = -1; | ||||
|     bool in_progress; | ||||
| @@ -372,24 +373,27 @@ int inet_connect_opts(QemuOpts *opts, Error **errp, | ||||
|     } | ||||
|  | ||||
|     for (e = res; e != NULL; e = e->ai_next) { | ||||
|         if (error_is_set(errp)) { | ||||
|             error_free(*errp); | ||||
|             *errp = NULL; | ||||
|         } | ||||
|         error_free(local_err); | ||||
|         local_err = NULL; | ||||
|         if (connect_state != NULL) { | ||||
|             connect_state->current_addr = e; | ||||
|         } | ||||
|         sock = inet_connect_addr(e, &in_progress, connect_state, errp); | ||||
|         if (in_progress) { | ||||
|             return sock; | ||||
|         } else if (sock >= 0) { | ||||
|             /* non blocking socket immediate success, call callback */ | ||||
|             if (callback != NULL) { | ||||
|                 callback(sock, opaque); | ||||
|             } | ||||
|         sock = inet_connect_addr(e, &in_progress, connect_state, &local_err); | ||||
|         if (sock >= 0) { | ||||
|             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); | ||||
|     freeaddrinfo(res); | ||||
|     return sock; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user