diff --git a/block/file-posix.c b/block/file-posix.c index b283093e5b7b4a0643dc82e3f5cea896476a2b16..5180fd1d0b95d43c84f32a0dfebd5ddc2e01a33c 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -128,6 +128,10 @@ #define FTYPE_CD 1 #define MAX_BLOCKSIZE 4096 +#define DEFAULT_BUFFER_SIZE 65536 +#define BUFFER_ALIGN_SIZE 65536 +#define MIN_BUFFER_SIZE 65536 +#define MAX_BUFFER_SIZE 16777216 /* Posix file locking bytes. Libvirt takes byte 0, we start from higher bytes, * leaving a few more bytes for its future use. */ @@ -206,6 +210,8 @@ typedef struct RawPosixAIOData { off_t aio_offset; uint64_t aio_nbytes; + size_t buffer_size; + union { struct { struct iovec *iov; @@ -804,7 +810,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } #endif - bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; + bs->supported_zero_flags = s->discard_zeroes ? (BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) : 0; if (S_ISREG(st.st_mode)) { /* When extending regular files, we get zeros from the OS */ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; @@ -2218,7 +2224,8 @@ static void raw_close(BlockDriverState *bs) */ static int coroutine_fn raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, - PreallocMode prealloc, Error **errp) + PreallocMode prealloc, size_t buffer_size, + Error **errp) { RawPosixAIOData acb; @@ -2227,6 +2234,7 @@ raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, .aio_fildes = fd, .aio_type = QEMU_AIO_TRUNCATE, .aio_offset = offset, + .buffer_size = buffer_size, .truncate = { .prealloc = prealloc, .errp = errp, @@ -2252,7 +2260,8 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, if (S_ISREG(st.st_mode)) { /* Always resizes to the exact @offset */ - return raw_regular_truncate(bs, s->fd, offset, prealloc, errp); + return raw_regular_truncate(bs, s->fd, offset, prealloc, + DEFAULT_BUFFER_SIZE, errp); } if (prealloc != PREALLOC_MODE_OFF) { @@ -2465,6 +2474,8 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) int fd; uint64_t perm, shared; int result = 0; + int flags = O_RDWR | O_BINARY; + size_t buffer_size = DEFAULT_BUFFER_SIZE; /* Validate options and set default values */ assert(options->driver == BLOCKDEV_DRIVER_FILE); @@ -2484,9 +2495,19 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) error_setg(errp, "Extent size hint is too large"); goto out; } + if (!file_opts->has_cache) { + file_opts->cache = g_strdup("writeback"); + } + if (file_opts->preallocation == PREALLOC_MODE_FULL && + !strcmp(file_opts->cache, "none")) { + flags |= O_DIRECT; + } + if (file_opts->has_buffersize) { + buffer_size = file_opts->buffersize; + } /* Create file */ - fd = qemu_create(file_opts->filename, O_RDWR | O_BINARY, 0644, errp); + fd = qemu_create(file_opts->filename, flags, 0644, errp); if (fd < 0) { result = -errno; goto out; @@ -2521,7 +2542,8 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) } /* Clear the file by truncating it to 0 */ - result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp); + result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, + buffer_size, errp); if (result < 0) { goto out_unlock; } @@ -2565,7 +2587,8 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) /* Resize and potentially preallocate the file to the desired * final size */ result = raw_regular_truncate(NULL, fd, file_opts->size, - file_opts->preallocation, errp); + file_opts->preallocation, + buffer_size, errp); if (result < 0) { goto out_unlock; } @@ -2586,6 +2609,7 @@ out_close: error_setg_errno(errp, -result, "Could not close the new file"); } out: + g_free(file_opts->cache); return result; } @@ -2602,6 +2626,8 @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv, PreallocMode prealloc; char *buf = NULL; Error *local_err = NULL; + size_t buffersize = DEFAULT_BUFFER_SIZE; + char *cache = NULL; /* Skip file: protocol prefix */ strstart(filename, "file:", &filename); @@ -2624,6 +2650,21 @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv, return -EINVAL; } + buffersize = qemu_opt_get_size_del(opts, BLOCK_OPT_BUFFER_SIZE, + DEFAULT_BUFFER_SIZE); + if (buffersize < MIN_BUFFER_SIZE || buffersize > MAX_BUFFER_SIZE) { + error_setg_errno(errp, EINVAL, "Buffer size must be between %d " + "and %d", MIN_BUFFER_SIZE, MAX_BUFFER_SIZE); + return -EINVAL; + } + + cache = qemu_opt_get_del(opts, BLOCK_OPT_CACHE); + if (!cache) { + cache = g_strdup("writeback"); + } + + buffersize = ROUND_UP(buffersize, BUFFER_ALIGN_SIZE); + options = (BlockdevCreateOptions) { .driver = BLOCKDEV_DRIVER_FILE, .u.file = { @@ -2635,6 +2676,10 @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv, .nocow = nocow, .has_extent_size_hint = has_extent_size_hint, .extent_size_hint = extent_size_hint, + .has_buffersize = true, + .buffersize = buffersize, + .has_cache = true, + .cache = cache, }, }; return raw_co_create(&options, errp); @@ -3133,6 +3178,16 @@ static QemuOptsList raw_create_opts = { .type = QEMU_OPT_SIZE, .help = "Extent size hint for the image file, 0 to disable" }, + { + .name = BLOCK_OPT_CACHE, + .type = QEMU_OPT_STRING, + .help = "Cache mode (allowed values: writeback, none)" + }, + { + .name = BLOCK_OPT_BUFFER_SIZE, + .type = QEMU_OPT_SIZE, + .help = "write buffer size" + }, { /* end of list */ } } }; diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 836cfa0bc21be39ea1bcc6ab4a48eafbd2ef0bdf..b1e9f43ec64106c77bff971da68bf389d9bf96ef 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -393,6 +393,22 @@ static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond) return qio_channel_create_watch(s->ioc, cond); } +static void tcp_chr_set_reconnect_time(Chardev *chr, + int64_t reconnect_time) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + s->reconnect_time = reconnect_time; +} + +void qemu_chr_set_reconnect_time(Chardev *chr, int64_t reconnect_time) +{ + ChardevClass *cc = CHARDEV_GET_CLASS(chr); + + if (cc->chr_set_reconnect_time) { + cc->chr_set_reconnect_time(chr, reconnect_time); + } +} + static void remove_hup_source(SocketChardev *s) { if (s->hup_source != NULL) { @@ -591,7 +607,7 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) if (s->state != TCP_CHARDEV_STATE_DISCONNECTED) { qio_channel_set_blocking(s->ioc, false, NULL); } - if (size == 0) { + if (size == 0 && chr->chr_for_flag != CHR_FOR_VHOST_USER) { /* connection closed */ tcp_chr_disconnect(chr); } @@ -1585,6 +1601,7 @@ static void char_socket_class_init(ObjectClass *oc, void *data) cc->set_msgfds = tcp_set_msgfds; cc->chr_add_client = tcp_chr_add_client; cc->chr_add_watch = tcp_chr_add_watch; + cc->chr_set_reconnect_time = tcp_chr_set_reconnect_time; cc->chr_update_read_handler = tcp_chr_update_read_handler; object_class_property_add(oc, "addr", "SocketAddress", diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 30379d2ca4105fcf30803dcf242a823604c9b354..e8a79db94d2fb8258ae2f1bcaa3d1df55bf86e87 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -152,9 +152,26 @@ static int vhost_net_get_fd(NetClientState *backend) } } +static uint64_t vhost_get_mask_features(const int *feature_bits, uint64_t features) +{ + const int *bit = feature_bits; + uint64_t out_features = 0; + + while (*bit != VHOST_INVALID_FEATURE_BIT) { + uint64_t bit_mask = (1ULL << *bit); + if (features & bit_mask) { + out_features |= bit_mask; + } + bit++; + } + return out_features; +} + struct vhost_net *vhost_net_init(VhostNetOptions *options) { int r; + VirtIONet *n; + VirtIODevice *vdev; bool backend_kernel = options->backend_type == VHOST_BACKEND_TYPE_KERNEL; struct vhost_net *net = g_new0(struct vhost_net, 1); uint64_t features = 0; @@ -180,7 +197,46 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->backend = r; net->dev.protocol_features = 0; } else { - net->dev.backend_features = 0; + /* for ovs restart when vm start. + * Normal situation: + * 1.vm start. + * 2.vhost_net_init init ok, then dev.acked_features is 0x40000000. + * 3.guest virtio-net mod load. qemu will call virtio_net_set_features set + * dev.acked_features to 0x40408000. + * 4.feature set to ovs's vhostuser(0x40408000). + * 5.ovs restart. + * 6.vhost_user_stop will save net->dev.acked_features(0x40408000) to + * VhostUserState's acked_features(0x40408000). + * 7.restart ok. + * 8.vhost_net_init fun call vhost_user_get_acked_features get the save + * features, and set to net->dev.acked_features. + * Abnormal situation: + * 1.vm start. + * 2.vhost_net_init init ok, then dev.acked_features is 0x40000000. + * 3.ovs restart. + * 4.vhost_user_stop will save net->dev.acked_features(0x40000000) to + * VhostUserState's acked_features(0x40000000). + * 5.guest virtio-net mod load. qemu will call virtio_net_set_features set + * dev.acked_features to 0x40408000. + * 6.restart ok. + * 7.vhost_net_init fun call vhost_user_get_acked_features get the save + * features(0x40000000), and set to net->dev.acked_features(0x40000000). + * 8.feature set to ovs's vhostuser(0x40000000). + * + * in abnormal situation, qemu set the wrong features to ovs's vhostuser, + * then the vm's network will be down. + * in abnormal situation, we found it just lost the guest feartures in + * acked_features, so hear we set the acked_features to vm's featrue + * just the same as guest virtio-net mod load. + */ + if (options->net_backend->peer) { + n = qemu_get_nic_opaque(options->net_backend->peer); + vdev = VIRTIO_DEVICE(n); + net->dev.backend_features = vhost_get_mask_features(vhost_net_get_feature_bits(net), + vdev->guest_features); + } else { + net->dev.backend_features = 0; + } net->dev.protocol_features = 0; net->backend = -1; @@ -376,7 +432,9 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, goto err_start; } - if (peer->vring_enable) { + /* ovs needs to restore all states of vring */ + if (peer->vring_enable || + ncs[i].peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { /* restore vring enable state */ r = vhost_set_vring_enable(peer, peer->vring_enable); diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index f2014d5ea0b30ceed3b422aeecca3ed8b043fd46..e887589a30cda7790d1c9cd52b0f9156464437be 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -51,12 +51,11 @@ #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ /* previously fixed value */ -#define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 -#define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256 +#define VIRTIO_NET_VHOST_USER_DEFAULT_SIZE 2048 /* for now, only allow larger queue_pairs; with virtio-1, guest can downsize */ -#define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE -#define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE +#define VIRTIO_NET_RX_QUEUE_MIN_SIZE 256 +#define VIRTIO_NET_TX_QUEUE_MIN_SIZE 256 #define VIRTIO_NET_IP4_ADDR_SIZE 8 /* ipv4 saddr + daddr */ @@ -622,6 +621,28 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, } } +static void virtio_net_set_default_queue_size(VirtIONet *n) +{ + NetClientState *peer = n->nic_conf.peers.ncs[0]; + + /* Default value is 0 if not set */ + if (n->net_conf.rx_queue_size == 0) { + if (peer && peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + n->net_conf.rx_queue_size = VIRTIO_NET_VHOST_USER_DEFAULT_SIZE; + } else { + n->net_conf.rx_queue_size = VIRTIO_NET_VQ_MAX_SIZE; + } + } + + if (n->net_conf.tx_queue_size == 0) { + if (peer && peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + n->net_conf.tx_queue_size = VIRTIO_NET_VHOST_USER_DEFAULT_SIZE; + } else { + n->net_conf.tx_queue_size = VIRTIO_NET_VQ_MAX_SIZE; + } + } +} + static int virtio_net_max_tx_queue_size(VirtIONet *n) { NetClientState *peer = n->nic_conf.peers.ncs[0]; @@ -630,14 +651,14 @@ static int virtio_net_max_tx_queue_size(VirtIONet *n) * Backends other than vhost-user don't support max queue size. */ if (!peer) { - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + return VIRTIO_NET_VQ_MAX_SIZE; } if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) { - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + return VIRTIO_NET_VQ_MAX_SIZE; } - return VIRTQUEUE_MAX_SIZE; + return VIRTIO_NET_VQ_MAX_SIZE; } static int peer_attach(VirtIONet *n, int index) @@ -2644,7 +2665,10 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) return; } virtio_queue_set_notification(vq, 0); - qemu_bh_schedule(q->tx_bh); + + if (q->tx_bh) { + qemu_bh_schedule(q->tx_bh); + } } static void virtio_net_tx_timer(void *opaque) @@ -3385,29 +3409,31 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) virtio_net_set_config_size(n, n->host_features); virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); + virtio_net_set_default_queue_size(n); + /* * We set a lower limit on RX queue size to what it always was. * Guests that want a smaller ring can always resize it without * help from us (using virtio 1 and up). */ if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE || - n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE || + n->net_conf.rx_queue_size > VIRTIO_NET_VQ_MAX_SIZE || !is_power_of_2(n->net_conf.rx_queue_size)) { error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), " "must be a power of 2 between %d and %d.", n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE, - VIRTQUEUE_MAX_SIZE); + VIRTIO_NET_VQ_MAX_SIZE); virtio_cleanup(vdev); return; } if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE || - n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE || + n->net_conf.tx_queue_size > VIRTIO_NET_VQ_MAX_SIZE || !is_power_of_2(n->net_conf.tx_queue_size)) { error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), " "must be a power of 2 between %d and %d", n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE, - VIRTQUEUE_MAX_SIZE); + VIRTIO_NET_VQ_MAX_SIZE); virtio_cleanup(vdev); return; } @@ -3676,10 +3702,8 @@ static Property virtio_net_properties[] = { TX_TIMER_INTERVAL), DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST), DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), - DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, - VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE), - DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size, - VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE), + DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, 0), + DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size, 0), DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0), DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend, true), @@ -3689,6 +3713,46 @@ static Property virtio_net_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void virtio_net_print_features(uint64_t features) +{ + Property *props = virtio_net_properties; + int feature_cnt = 0; + + if (!features) { + return; + } + printf("virtio_net_feature: "); + + for (; features && props->name; props++) { + /* The bitnr of property may be default(0) besides 'csum' property. */ + if (props->bitnr == 0 && strcmp(props->name, "csum")) { + continue; + } + + /* Features only support 64bit. */ + if (props->bitnr > 63) { + continue; + } + + if (virtio_has_feature(features, props->bitnr)) { + virtio_clear_feature(&features, props->bitnr); + if (feature_cnt != 0) { + printf(", "); + } + printf("%s", props->name); + feature_cnt++; + } + } + + if (features) { + if (feature_cnt != 0) { + printf(", "); + } + printf("unkown bits 0x%." PRIx64, features); + } + printf("\n"); +} + static void virtio_net_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -3703,6 +3767,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->set_config = virtio_net_set_config; vdc->get_features = virtio_net_get_features; vdc->set_features = virtio_net_set_features; + vdc->print_features = virtio_net_print_features; vdc->bad_features = virtio_net_bad_features; vdc->reset = virtio_net_reset; vdc->set_status = virtio_net_set_status; diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 1b2f7eed98895b8851bf13661881209c10e0676a..052740a76ec9fad97bfe2976db62152cc60a8a97 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -29,6 +29,9 @@ #include "hw/virtio/virtio-access.h" #include "chardev/char-fe.h" #include "sysemu/sysemu.h" +#include "qemu/log.h" + +#define VHOST_USER_SCSI_RECONNECT_TIME 3 /* Features supported by the host application */ static const int user_feature_bits[] = { @@ -59,7 +62,7 @@ static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) ret = vhost_scsi_common_start(vsc); if (ret < 0) { error_report("unable to start vhost-user-scsi: %s", strerror(-ret)); - exit(1); + return; } } else { vhost_scsi_common_stop(vsc); @@ -89,11 +92,43 @@ static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) { } +static void vhost_user_scsi_event(void *opaque, QEMUChrEvent event) +{ + int ret; + VHostUserSCSI *s = (VHostUserSCSI *)opaque; + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + qemu_log("event:%d, vdev status:%d\n", event, vdev->status); + + /* if CHR_EVENT_CLOSED, do nothing */ + if (event != CHR_EVENT_OPENED) { + return; + }; + + /* if status of vdev is not DRIVER_OK, just waiting. + * vsc should start when status change to DRIVER_OK */ + if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { + return; + } + + /* vsc may not fully start because of vhost app stopping */ + if (vsc->dev.started) { + vhost_scsi_common_stop(vsc); + } + + ret = vhost_scsi_common_start(vsc); + if (ret < 0) { + qemu_log("unable to start vhost-user-scsi: %s\n", strerror(-ret)); + } +} + static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); VHostUserSCSI *s = VHOST_USER_SCSI(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + Chardev *chr; struct vhost_virtqueue *vqs = NULL; Error *err = NULL; int ret; @@ -132,6 +167,11 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) vsc->lun = 0; vsc->target = vs->conf.boot_tpgt; + chr = qemu_chr_fe_get_driver(&vs->conf.chardev); + qemu_chr_set_reconnect_time(chr, VHOST_USER_SCSI_RECONNECT_TIME); + qemu_chr_fe_set_handlers(&vs->conf.chardev, NULL, NULL, + vhost_user_scsi_event, NULL, s, NULL, true); + return; free_vhost: diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 51fd09522ac687cbb8a32bf1c6ecf4d9c2ba25ab..781a37fe89e0a51de889a9aa1c46e33053858351 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -638,7 +638,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) req->req.cmd.tag, req->req.cmd.cdb[0]); d = virtio_scsi_device_get(s, req->req.cmd.lun); - if (!d) { + if (!d || !d->qdev.realized) { req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET; virtio_scsi_complete_cmd_req(req); return -ENOENT; diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index b65f8f7e97bfc3e78128115b01836a1620342fa3..d8e17107582805ad242ef3b7a94be50b70774fdf 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -20,6 +20,8 @@ #include #include +static unsigned int vhost_kernel_used_memslots; + static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request, void *arg) { @@ -293,6 +295,16 @@ static void vhost_kernel_set_iotlb_callback(struct vhost_dev *dev, qemu_set_fd_handler((uintptr_t)dev->opaque, NULL, NULL, NULL); } +static void vhost_kernel_set_used_memslots(struct vhost_dev *dev) +{ + vhost_kernel_used_memslots = dev->mem->nregions; +} + +unsigned int vhost_kernel_get_used_memslots(void) +{ + return vhost_kernel_used_memslots; +} + const VhostOps kernel_ops = { .backend_type = VHOST_BACKEND_TYPE_KERNEL, .vhost_backend_init = vhost_kernel_init, @@ -325,6 +337,8 @@ const VhostOps kernel_ops = { #endif /* CONFIG_VHOST_VSOCK */ .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback, .vhost_send_device_iotlb_msg = vhost_kernel_send_device_iotlb_msg, + .vhost_set_used_memslots = vhost_kernel_set_used_memslots, + .vhost_get_used_memslots = vhost_kernel_get_used_memslots, }; #endif diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index bf6e50223cb49d27fa428e442f3aa9c4948e79dd..8f69a3b85047151b529a60b52542d7082a3b45f6 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -24,6 +24,7 @@ #include "sysemu/cryptodev.h" #include "migration/migration.h" #include "migration/postcopy-ram.h" +#include "migration/register.h" #include "trace.h" #include @@ -233,6 +234,7 @@ static VhostUserMsg m __attribute__ ((unused)); /* The version of the protocol we support */ #define VHOST_USER_VERSION (0x1) +static unsigned int vhost_user_used_memslots; struct vhost_user { struct vhost_dev *dev; @@ -1919,15 +1921,43 @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier, return 0; } +static int vhost_user_load_setup(QEMUFile *f, void *opaque) +{ + struct vhost_dev *hdev = opaque; + int r; + + if (hdev->vhost_ops && hdev->vhost_ops->vhost_set_mem_table) { + r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem); + if (r < 0) { + qemu_log("error: vhost_set_mem_table failed: %s(%d)\n", + strerror(errno), errno); + return r; + } else { + qemu_log("info: vhost_set_mem_table OK\n"); + } + } + return 0; +} + +SaveVMHandlers savevm_vhost_user_handlers = { + .load_setup = vhost_user_load_setup, +}; + static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, Error **errp) { uint64_t features, protocol_features, ram_slots; struct vhost_user *u; int err; + Chardev *chr; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + chr = qemu_chr_fe_get_driver(((VhostUserState *)opaque)->chr); + if (chr) { + chr->chr_for_flag = CHR_FOR_VHOST_USER; + } + u = g_new0(struct vhost_user, 1); u->user = opaque; u->dev = dev; @@ -2037,6 +2067,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, u->postcopy_notifier.notify = vhost_user_postcopy_notifier; postcopy_add_notifier(&u->postcopy_notifier); + register_savevm_live("vhost-user", -1, 1, &savevm_vhost_user_handlers, dev); return 0; } @@ -2068,6 +2099,7 @@ static int vhost_user_backend_cleanup(struct vhost_dev *dev) u->region_rb_len = 0; g_free(u); dev->opaque = 0; + unregister_savevm(NULL, "vhost-user", dev); return 0; } @@ -2493,6 +2525,30 @@ void vhost_user_cleanup(VhostUserState *user) user->chr = NULL; } +static void vhost_user_set_used_memslots(struct vhost_dev *dev) +{ + unsigned int counter = 0; + int i; + + for (i = 0; i < dev->mem->nregions; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + ram_addr_t offset; + MemoryRegion *mr; + + mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr, + &offset); + if (mr && memory_region_get_fd(mr) > 0) { + counter++; + } + } + vhost_user_used_memslots = counter; +} + +unsigned int vhost_user_get_used_memslots(void) +{ + return vhost_user_used_memslots; +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_backend_init, @@ -2526,4 +2582,6 @@ const VhostOps user_ops = { .vhost_backend_mem_section_filter = vhost_user_mem_section_filter, .vhost_get_inflight_fd = vhost_user_get_inflight_fd, .vhost_set_inflight_fd = vhost_user_set_inflight_fd, + .vhost_set_used_memslots = vhost_user_set_used_memslots, + .vhost_get_used_memslots = vhost_user_get_used_memslots, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index dafb23c481df226ed627c08a8132ef9a556a79c7..4c4072951cd3885ae65ee250b8b3db23bef8ec00 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -45,20 +45,22 @@ static struct vhost_log *vhost_log; static struct vhost_log *vhost_log_shm; -static unsigned int used_memslots; static QLIST_HEAD(, vhost_dev) vhost_devices = QLIST_HEAD_INITIALIZER(vhost_devices); +bool used_memslots_exceeded; + bool vhost_has_free_slot(void) { - unsigned int slots_limit = ~0U; struct vhost_dev *hdev; QLIST_FOREACH(hdev, &vhost_devices, entry) { - unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev); - slots_limit = MIN(slots_limit, r); + if (hdev->vhost_ops->vhost_get_used_memslots() >= + hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { + return false; + } } - return slots_limit > used_memslots; + return true; } static void vhost_dev_sync_region(struct vhost_dev *dev, @@ -521,7 +523,6 @@ static void vhost_commit(MemoryListener *listener) dev->n_mem_sections * sizeof dev->mem->regions[0]; dev->mem = g_realloc(dev->mem, regions_size); dev->mem->nregions = dev->n_mem_sections; - used_memslots = dev->mem->nregions; for (i = 0; i < dev->n_mem_sections; i++) { struct vhost_memory_region *cur_vmr = dev->mem->regions + i; struct MemoryRegionSection *mrs = dev->mem_sections + i; @@ -697,6 +698,7 @@ static void vhost_region_add_section(struct vhost_dev *dev, dev->tmp_sections[dev->n_tmp_sections - 1].fv = NULL; memory_region_ref(section->mr); } + dev->vhost_ops->vhost_set_used_memslots(dev); } /* Used for both add and nop callbacks */ @@ -712,6 +714,17 @@ static void vhost_region_addnop(MemoryListener *listener, vhost_region_add_section(dev, section); } +static void vhost_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + struct vhost_dev *dev = container_of(listener, struct vhost_dev, + memory_listener); + if (!vhost_section(dev, section)) { + return; + } + dev->vhost_ops->vhost_set_used_memslots(dev); +} + static void vhost_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) { struct vhost_iommu *iommu = container_of(n, struct vhost_iommu, n); @@ -1319,6 +1332,20 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) event_notifier_cleanup(&vq->masked_notifier); } +static bool vhost_dev_used_memslots_is_exceeded(struct vhost_dev *hdev) +{ + if (hdev->vhost_ops->vhost_get_used_memslots() > + hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { + error_report("vhost backend memory slots limit is less" + " than current number of present memory slots"); + used_memslots_exceeded = true; + return true; + } + + used_memslots_exceeded = false; + return false; +} + int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, uint32_t busyloop_timeout, Error **errp) @@ -1374,6 +1401,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, .name = "vhost", .begin = vhost_begin, .commit = vhost_commit, + .region_del = vhost_region_del, .region_add = vhost_region_addnop, .region_nop = vhost_region_addnop, .log_start = vhost_log_start, @@ -1420,9 +1448,13 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, memory_listener_register(&hdev->memory_listener, &address_space_memory); QLIST_INSERT_HEAD(&vhost_devices, hdev, entry); - if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { - error_setg(errp, "vhost backend memory slots limit is less" - " than current number of present memory slots"); + /* + * If we started a VM without any vhost device, + * vhost_dev_used_memslots_is_exceeded will always return false for the + * first time vhost device hot-plug(vhost_get_used_memslots is always 0), + * so it needs to double check here + */ + if (vhost_dev_used_memslots_is_exceeded(hdev)) { r = -EINVAL; goto fail_busyloop; } @@ -1867,3 +1899,8 @@ int vhost_net_set_backend(struct vhost_dev *hdev, return -1; } + +bool used_memslots_is_exceeded(void) +{ + return used_memslots_exceeded; +} diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 9b4ac58a16fe7141c4d781bb3e7f9ca404a417da..120672672ea79046809d5722f9b7645fb0b5c2a0 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2241,12 +2241,17 @@ void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, void virtio_queue_set_num(VirtIODevice *vdev, int n, int num) { + int vq_max_size = VIRTQUEUE_MAX_SIZE; + + if (!strcmp(vdev->name, "virtio-net")) { + vq_max_size = VIRTIO_NET_VQ_MAX_SIZE; + } + /* Don't allow guest to flip queue between existent and * nonexistent states, or to set it to an invalid size. */ if (!!num != !!vdev->vq[n].vring.num || - num > VIRTQUEUE_MAX_SIZE || - num < 0) { + num > vq_max_size || num < 0) { return; } vdev->vq[n].vring.num = num; @@ -2396,7 +2401,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, break; } - if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) { + if (i == VIRTIO_QUEUE_MAX) { qemu_log("unacceptable queue_size (%d) or num (%d)\n", queue_size, i); abort(); @@ -2417,6 +2422,7 @@ void virtio_delete_queue(VirtQueue *vq) { vq->vring.num = 0; vq->vring.num_default = 0; + vq->vring.align = 0; vq->handle_output = NULL; vq->handle_aio_output = NULL; g_free(vq->used_elems); @@ -2854,6 +2860,35 @@ static const VMStateDescription vmstate_virtio = { } }; +static void check_vring_avail_num(VirtIODevice *vdev, int index) +{ + uint16_t nheads; + VRingMemoryRegionCaches *caches; + + rcu_read_lock(); + caches = qatomic_rcu_read(&vdev->vq[index].vring.caches); + if (caches == NULL) { + /* + * caches may be NULL if virtio_reset is called at the same time, + * such as when the virtual machine starts. + */ + rcu_read_unlock(); + return; + } + + /* Check it isn't doing strange things with descriptor numbers. */ + nheads = vring_avail_idx(&vdev->vq[index]) - vdev->vq[index].last_avail_idx; + if (nheads > vdev->vq[index].vring.num) { + qemu_log("VQ %d size 0x%x Guest index 0x%x " + "inconsistent with Host index 0x%x: " + "delta 0x%x\n", + index, vdev->vq[index].vring.num, + vring_avail_idx(&vdev->vq[index]), + vdev->vq[index].last_avail_idx, nheads); + } + rcu_read_unlock(); +} + int virtio_save(VirtIODevice *vdev, QEMUFile *f) { BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -2884,6 +2919,8 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f) if (vdev->vq[i].vring.num == 0) break; + check_vring_avail_num(vdev, i); + qemu_put_be32(f, vdev->vq[i].vring.num); if (k->has_variable_vring_alignment) { qemu_put_be32(f, vdev->vq[i].vring.align); @@ -2941,6 +2978,13 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); bool bad = (val & ~(vdev->host_features)) != 0; + uint64_t feat = val & ~(vdev->host_features); + + if (bad && k->print_features) { + qemu_log("error: Please check host config, "\ + "because host does not support required feature bits 0x%" PRIx64 "\n", feat); + k->print_features(feat); + } val &= vdev->host_features; if (k->set_features) { diff --git a/include/block/block_int.h b/include/block/block_int.h index f4c75e8ba956b4486e9155bb9be1efbd2aeaca49..701f031102a7437d5c93dd9d25f431f53d089271 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -61,6 +61,8 @@ #define BLOCK_OPT_DATA_FILE_RAW "data_file_raw" #define BLOCK_OPT_COMPRESSION_TYPE "compression_type" #define BLOCK_OPT_EXTL2 "extended_l2" +#define BLOCK_OPT_CACHE "cache" +#define BLOCK_OPT_BUFFER_SIZE "buffer_size" #define BLOCK_PROBE_BUF_SIZE 512 diff --git a/include/chardev/char.h b/include/chardev/char.h index a319b5fdff7f583ba9493618eab1ab49dde08896..f388d4b109b4641d235f64979ba8bef8b795bc6a 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -14,6 +14,8 @@ #define IAC_SB 250 #define IAC 255 +#define CHR_FOR_VHOST_USER 0x32a1 + /* character device */ typedef struct CharBackend CharBackend; @@ -70,6 +72,7 @@ struct Chardev { GSource *gsource; GMainContext *gcontext; DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST); + int chr_for_flag; }; /** @@ -227,6 +230,16 @@ int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all); #define qemu_chr_write_all(s, buf, len) qemu_chr_write(s, buf, len, true) int qemu_chr_wait_connected(Chardev *chr, Error **errp); +/** + * @qemu_chr_set_reconnect_time: + * + * Set reconnect time for char disconnect. + * Currently, only vhost user will call it. + * + * @reconnect_time the reconnect_time to be set + */ +void qemu_chr_set_reconnect_time(Chardev *chr, int64_t reconnect_time); + #define TYPE_CHARDEV "chardev" OBJECT_DECLARE_TYPE(Chardev, ChardevClass, CHARDEV) @@ -306,6 +319,9 @@ struct ChardevClass { /* handle various events */ void (*chr_be_event)(Chardev *s, QEMUChrEvent event); + + /* set reconnect time */ + void (*chr_set_reconnect_time)(Chardev *chr, int64_t reconnect_time); }; Chardev *qemu_chardev_new(const char *id, const char *typename, diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 81bf3109f837e15001e04b165b029a2f5a87adfd..7bbc658161562babb42987e3a5605be0acbaed12 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -125,6 +125,8 @@ typedef int (*vhost_vq_get_addr_op)(struct vhost_dev *dev, typedef int (*vhost_get_device_id_op)(struct vhost_dev *dev, uint32_t *dev_id); typedef bool (*vhost_force_iommu_op)(struct vhost_dev *dev); +typedef void (*vhost_set_used_memslots_op)(struct vhost_dev *dev); +typedef unsigned int (*vhost_get_used_memslots_op)(void); typedef struct VhostOps { VhostBackendType backend_type; @@ -171,6 +173,8 @@ typedef struct VhostOps { vhost_vq_get_addr_op vhost_vq_get_addr; vhost_get_device_id_op vhost_get_device_id; vhost_force_iommu_op vhost_force_iommu; + vhost_set_used_memslots_op vhost_set_used_memslots; + vhost_get_used_memslots_op vhost_get_used_memslots; } VhostOps; int vhost_backend_update_device_iotlb(struct vhost_dev *dev, @@ -186,4 +190,6 @@ int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd); +unsigned int vhost_kernel_get_used_memslots(void); +unsigned int vhost_user_get_used_memslots(void); #endif /* VHOST_BACKEND_H */ diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 58a73e7b7a19b2d42ad400ce35625116a3e31dbb..86f36f010651bcfd8b1dee4e5b819deecd1dbcd8 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -154,4 +154,5 @@ int vhost_dev_set_inflight(struct vhost_dev *dev, struct vhost_inflight *inflight); int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, struct vhost_inflight *inflight); +bool used_memslots_is_exceeded(void); #endif diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 8bab9cfb7507dd9749866ff99d9a7b3ea6660dcb..747214582195585dad6742da62cb9a5f2f4cb36b 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -49,6 +49,7 @@ size_t virtio_feature_get_config_size(const VirtIOFeature *features, typedef struct VirtQueue VirtQueue; #define VIRTQUEUE_MAX_SIZE 1024 +#define VIRTIO_NET_VQ_MAX_SIZE (4096) typedef struct VirtQueueElement { @@ -126,6 +127,7 @@ struct VirtioDeviceClass { int (*validate_features)(VirtIODevice *vdev); void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); + void (*print_features)(uint64_t features); void (*reset)(VirtIODevice *vdev); void (*set_status)(VirtIODevice *vdev, uint8_t val); /* For transitional devices, this is a bitmap of features diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 98868cee03fbc3de9696ab3692e05730fb925168..f3e80ec8a7b467877c4b407746676b0a238fcad7 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -36,6 +36,7 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-ui.h" +#include "qapi/qapi-commands-net.h" #include "qapi/type-helpers.h" #include "qapi/qmp/qerror.h" #include "exec/ramlist.h" @@ -43,6 +44,7 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/intc/intc.h" #include "hw/rdma/rdma.h" +#include "hw/virtio/vhost-backend.h" NameInfo *qmp_query_name(Error **errp) { @@ -471,3 +473,13 @@ int64_t qmp_query_rtc_date_diff(Error **errp) { return get_rtc_date_diff(); } + +uint32_t qmp_query_vhost_kernel_used_memslots(Error **errp) +{ + return vhost_kernel_get_used_memslots(); +} + +uint32_t qmp_query_vhost_user_used_memslots(Error **errp) +{ + return vhost_user_get_used_memslots(); +} diff --git a/net/net.c b/net/net.c index f0d14dbfc1f0f929eb93da9e789dcb4560366054..ed4b1c1740367944ccaae936f152a423b1f5bc82 100644 --- a/net/net.c +++ b/net/net.c @@ -1202,6 +1202,12 @@ void qmp_netdev_del(const char *id, Error **errp) return; } + if (nc->info->type == NET_CLIENT_DRIVER_VHOST_USER && nc->peer) { + error_setg(errp, "Device '%s' is a netdev for vhostuser," + "please delete the peer front-end device (virtio-net) first.", id); + return; + } + qemu_del_net_client(nc); /* diff --git a/net/tap.c b/net/tap.c index c5cbeaa7a2bef4b98e386eca889249feb79f8f5d..3f79cd06c2622e3c387ab4bf307e654069ed96dd 100644 --- a/net/tap.c +++ b/net/tap.c @@ -684,7 +684,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, tap_set_sndbuf(s->fd, tap, &err); if (err) { error_propagate(errp, err); - return; + goto fail; } if (tap->has_fd || tap->has_fds) { @@ -726,13 +726,13 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } else { warn_report_err(err); } - return; + goto fail; } ret = qemu_try_set_nonblock(vhostfd); if (ret < 0) { error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", name, fd); - return; + goto fail; } } else { vhostfd = open("/dev/vhost-net", O_RDWR); @@ -744,7 +744,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, warn_report("tap: open vhost char device failed: %s", strerror(errno)); } - return; + goto fail; } qemu_set_nonblock(vhostfd); } @@ -758,11 +758,17 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } else { warn_report(VHOST_NET_INIT_FAILED); } - return; + goto fail; } } else if (vhostfdname) { error_setg(errp, "vhostfd(s)= is not valid without vhost"); + goto fail; } + + return; + +fail: + qemu_del_net_client(&s->nc); } static int get_fds(char *str, char *fds[], int max) diff --git a/net/vhost-user.c b/net/vhost-user.c index b1a0247b59818ca5ee29a1c2ed5663c4469cf865..f910a286e4a731592c616483dffc333149d867be 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -20,6 +20,9 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "trace.h" +#include "include/hw/virtio/vhost.h" + +#define VHOST_USER_RECONNECT_TIME (3) typedef struct NetVhostUserState { NetClientState nc; @@ -287,6 +290,7 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) trace_vhost_user_event(chr->label, event); switch (event) { case CHR_EVENT_OPENED: + qemu_chr_set_reconnect_time(chr, VHOST_USER_RECONNECT_TIME); if (vhost_user_start(queues, ncs, s->vhost_user) < 0) { qemu_chr_fe_disconnect(&s->chr); return; @@ -366,6 +370,11 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, NULL, nc0->name, NULL, true); + if (used_memslots_is_exceeded()) { + error_report("used memslots exceeded the backend limit, quit " + "loop"); + goto err; + } } while (!s->started); assert(s->vhost_net); diff --git a/qapi/block-core.json b/qapi/block-core.json index 804beabfb033efa3a298e3f16a35c02232e003d4..e65fabe36dacf4ae248099c6187fc827a40c065c 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4437,6 +4437,8 @@ # @nocow: Turn off copy-on-write (valid only on btrfs; default: off) # @extent-size-hint: Extent size hint to add to the image file; 0 for not # adding an extent size hint (default: 1 MB, since 5.1) +# @cache: Cache mode used to write the output disk image +# @buffersize: Buffer size for creating image # # Since: 2.12 ## @@ -4445,7 +4447,9 @@ 'size': 'size', '*preallocation': 'PreallocMode', '*nocow': 'bool', - '*extent-size-hint': 'size'} } + '*extent-size-hint': 'size', + '*cache': 'str', + '*buffersize': 'size'} } ## # @BlockdevCreateOptionsGluster: diff --git a/qapi/net.json b/qapi/net.json index 7fab2e7cd8aa5f5c504d3bad0c1e3d67037f117e..c9ff849eedc4693edd031aac507fc8d5e9631926 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -696,3 +696,21 @@ ## { 'event': 'FAILOVER_NEGOTIATED', 'data': {'device-id': 'str'} } + +## +# @query-vhost-kernel-used-memslots: +# +# Get vhost-kernel nic used memslots +# +# Since: 4.1 +## +{ 'command': 'query-vhost-kernel-used-memslots', 'returns': 'uint32' } + +## +# @query-vhost-user-used-memslots: +# +# Get vhost-user nic used memslots +# +# Since: 4.1 +## +{ 'command': 'query-vhost-user-used-memslots', 'returns': 'uint32' } diff --git a/qapi/pragma.json b/qapi/pragma.json index b37f6de4452f39ccb570b2b1a09a3b4fb9111a06..d35c897acb4fdc2b18070486b3a5cdc379e5e849 100644 --- a/qapi/pragma.json +++ b/qapi/pragma.json @@ -27,7 +27,9 @@ 'query-tpm-models', 'query-tpm-types', 'ringbuf-read', - 'query-rtc-date-diff' ], + 'query-rtc-date-diff', + 'query-vhost-user-used-memslots', + 'query-vhost-kernel-used-memslots' ], # Externally visible types whose member names may use uppercase 'member-name-exceptions': [ # visible in: 'ACPISlotType', # query-acpi-ospm-status diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 72bcdcfbfad9e32eb4fae431d52d3ef0137058fe..ec6aa2886a7f50b69ae59b91f2d7b004474470a0 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -52,9 +52,9 @@ SRST ERST DEF("create", img_create, - "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]") + "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-t cache] [-o options] filename [size]") SRST -.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-o OPTIONS] FILENAME [SIZE] +.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-t CACHE] [-o OPTIONS] FILENAME [SIZE] ERST DEF("dd", img_dd, diff --git a/qemu-img.c b/qemu-img.c index f036a1d428db21205ded31bd3035d7295cf3582b..9409558772e0df61479ce13b8dc2b1d5ef711e2f 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -504,6 +504,7 @@ static int img_create(int argc, char **argv) const char *base_fmt = NULL; const char *filename; const char *base_filename = NULL; + const char *cache = BDRV_DEFAULT_CACHE; char *options = NULL; Error *local_err = NULL; bool quiet = false; @@ -515,7 +516,7 @@ static int img_create(int argc, char **argv) {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":F:b:f:ho:qu", + c = getopt_long(argc, argv, ":F:b:f:t:ho:qu", long_options, NULL); if (c == -1) { break; @@ -539,6 +540,9 @@ static int img_create(int argc, char **argv) case 'f': fmt = optarg; break; + case 't': + cache = optarg; + break; case 'o': if (accumulate_options(&options, optarg) < 0) { goto fail; @@ -582,6 +586,14 @@ static int img_create(int argc, char **argv) error_exit("Unexpected argument: %s", argv[optind]); } + if (!options) { + options = g_strdup_printf(BLOCK_OPT_CACHE"=%s", cache); + } else { + char *old_options = options; + options = g_strdup_printf("%s,"BLOCK_OPT_CACHE"=%s", options, cache); + g_free(old_options); + } + bdrv_img_create(filename, fmt, base_filename, base_fmt, options, img_size, flags, quiet, &local_err); if (local_err) { diff --git a/softmmu/runstate.c b/softmmu/runstate.c index 5736d908db00e07e97bd283323f8c7ed719625bb..680994cdf86417db7a792cd014e935969ee73ff4 100644 --- a/softmmu/runstate.c +++ b/softmmu/runstate.c @@ -115,6 +115,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_PRELAUNCH, RUN_STATE_RUNNING }, { RUN_STATE_PRELAUNCH, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_PRELAUNCH, RUN_STATE_INMIGRATE }, + { RUN_STATE_PRELAUNCH, RUN_STATE_POSTMIGRATE }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PAUSED },