diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 7d459726d42e7618a7f7f95a041a5fb0ac3ed8b5..09b76676dd82a491c640c5ce340d63d56ef1412f 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1518,24 +1518,28 @@ static bool virtio_net_can_receive(NetClientState *nc) static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) { + int opaque; + unsigned int in_bytes; VirtIONet *n = q->n; - if (virtio_queue_empty(q->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { - virtio_queue_set_notification(q->rx_vq, 1); - - /* To avoid a race condition where the guest has made some buffers - * available after the above check but before notification was - * enabled, check for available buffers again. - */ - if (virtio_queue_empty(q->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { + + while (virtio_queue_empty(q->rx_vq) || n->mergeable_rx_bufs) { + opaque = virtqueue_get_avail_bytes(q->rx_vq, &in_bytes, NULL, + bufsize, 0); + /* Buffer is enough, disable notifiaction */ + if (bufsize <= in_bytes) { + break; + } + + if (virtio_queue_enable_notification_and_check(q->rx_vq, opaque)) { + /* Guest has added some buffers, try again */ + continue; + } else { return 0; } } virtio_queue_set_notification(q->rx_vq, 0); + return 1; } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index ea7c079fb046b0a339e86e68c66ff4f787a541fd..2971d271ff656046cac450b9fbbd0bc03c28fa81 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -660,6 +660,60 @@ int virtio_queue_empty(VirtQueue *vq) } } +static bool virtio_queue_split_poll(VirtQueue *vq, unsigned shadow_idx) +{ + if (unlikely(!vq->vring.avail)) { + return false; + } + + return (uint16_t)shadow_idx != vring_avail_idx(vq); +} + +static bool virtio_queue_packed_poll(VirtQueue *vq, unsigned shadow_idx) +{ + VRingPackedDesc desc; + VRingMemoryRegionCaches *caches; + + if (unlikely(!vq->vring.desc)) { + return false; + } + + caches = vring_get_region_caches(vq); + if (!caches) { + return false; + } + + vring_packed_desc_read(vq->vdev, &desc, &caches->desc, + shadow_idx, true); + + return is_desc_avail(desc.flags, vq->shadow_avail_wrap_counter); +} + +static bool virtio_queue_poll(VirtQueue *vq, unsigned shadow_idx) +{ + if (virtio_device_disabled(vq->vdev)) { + return false; + } + + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + return virtio_queue_packed_poll(vq, shadow_idx); + } else { + return virtio_queue_split_poll(vq, shadow_idx); + } +} + +bool virtio_queue_enable_notification_and_check(VirtQueue *vq, + int opaque) +{ + virtio_queue_set_notification(vq, 1); + + if (opaque >= 0) { + return virtio_queue_poll(vq, (unsigned)opaque); + } else { + return false; + } +} + static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len) { @@ -1226,9 +1280,9 @@ err: goto done; } -void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, - unsigned int *out_bytes, - unsigned max_in_bytes, unsigned max_out_bytes) +int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes, unsigned max_in_bytes, + unsigned max_out_bytes) { uint16_t desc_size; VRingMemoryRegionCaches *caches; @@ -1261,7 +1315,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, caches); } - return; + return (int)vq->shadow_avail_idx; err: if (in_bytes) { *in_bytes = 0; @@ -1269,6 +1323,8 @@ err: if (out_bytes) { *out_bytes = 0; } + + return -1; } int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 8bab9cfb7507dd9749866ff99d9a7b3ea6660dcb..0d4299b87b52bcb5ba68c9f82f94d8ab62dd34f6 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -203,9 +203,13 @@ void qemu_put_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, VirtQueueElement *elem); int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, unsigned int out_bytes); -void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, - unsigned int *out_bytes, - unsigned max_in_bytes, unsigned max_out_bytes); +/** + * Return <0 on error or an opaque >=0 to pass to + * virtio_queue_enable_notification_and_check on success. + */ +int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes, unsigned max_in_bytes, + unsigned max_out_bytes); void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq); void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); @@ -232,6 +236,15 @@ int virtio_queue_ready(VirtQueue *vq); int virtio_queue_empty(VirtQueue *vq); +/** + * Enable notification and check whether guest has added some + * buffers since last call to virtqueue_get_avail_bytes. + * + * @opaque: value returned from virtqueue_get_avail_bytes + */ +bool virtio_queue_enable_notification_and_check(VirtQueue *vq, + int opaque); + /* Host binding interface. */ uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr); diff --git a/target/i386/csv.c b/target/i386/csv.c index 770ea7d5bfb0ae0d05f0a5e26a6b790938bcc10f..12e2cc16be6de9327639ee5c99fb58be99377a89 100644 --- a/target/i386/csv.c +++ b/target/i386/csv.c @@ -93,6 +93,8 @@ csv_init(uint32_t policy, int fd, void *state, struct sev_ops *ops) qemu_mutex_init(&csv_guest.dma_map_regions_list_mutex); csv_guest.sev_send_start = ops->sev_send_start; csv_guest.sev_receive_start = ops->sev_receive_start; + + kvm_csv_reset_inhibit = true; } return 0; } diff --git a/target/i386/csv.h b/target/i386/csv.h index 485534d2d69a36ba6c81e93797c96095b9ef0565..cc02ba83a4e55e95b9b76b284c45e892136c31bf 100644 --- a/target/i386/csv.h +++ b/target/i386/csv.h @@ -18,6 +18,8 @@ #include "qemu/thread.h" #include "qemu/queue.h" +extern bool kvm_csv_reset_inhibit; + #ifdef CONFIG_SEV #include "cpu.h" diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 469349420ca709000432757c8f1e6d2ace1b7538..a14debcfc274259d6fb16f81c0f9eb736d4fb1fb 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5383,7 +5383,9 @@ bool kvm_has_waitpkg(void) bool kvm_arch_cpu_check_are_resettable(void) { - return !(sev_es_enabled() && !is_hygon_cpu()) && !csv_enabled(); + if (is_hygon_cpu()) + return !kvm_csv_reset_inhibit; + return !sev_es_enabled(); } #define ARCH_REQ_XCOMP_GUEST_PERM 0x1025 diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c index 6080c007a2e4bf8b1485bdb807628ec98db46303..0651502f1cac4b0c4d1c16515fae5b91102dc2b5 100644 --- a/target/i386/kvm/sev-stub.c +++ b/target/i386/kvm/sev-stub.c @@ -20,3 +20,5 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) /* If we get here, cgs must be some non-SEV thing */ return 0; } + +bool kvm_csv_reset_inhibit; diff --git a/target/i386/sev.c b/target/i386/sev.c index 94d3c1853b64479e9865ddcf01f03ff07b94f70f..3f25d2b6d77788a0359a701bc4d7f529f18c19d5 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -219,6 +219,7 @@ static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { }; bool kvm_has_msr_ghcb; +bool kvm_csv_reset_inhibit; static int sev_ioctl(int fd, int cmd, void *data, int *error) @@ -1275,6 +1276,8 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) error_setg(errp, "%s: failed to create encryption context", __func__); goto err; } + } else if (is_hygon_cpu() && sev_es_enabled()) { + kvm_csv_reset_inhibit = true; } /* CSV guest needs no notifier to reg/unreg memory */ @@ -1643,7 +1646,7 @@ sev_send_get_packet_len(int *fw_err) ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_SEND_UPDATE_DATA, &update, fw_err); if (*fw_err != SEV_RET_INVALID_LEN) { - ret = -1; + ret = 0; error_report("%s: failed to get session length ret=%d fw_error=%d '%s'", __func__, ret, *fw_err, fw_error_to_str(*fw_err)); goto err; @@ -2370,7 +2373,7 @@ sev_send_vmsa_get_packet_len(int *fw_err) ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_SEND_UPDATE_VMSA, &update, fw_err); if (*fw_err != SEV_RET_INVALID_LEN) { - ret = -1; + ret = 0; error_report("%s: failed to get session length ret=%d fw_error=%d '%s'", __func__, ret, *fw_err, fw_error_to_str(*fw_err)); goto err;