350 lines
12 KiB
Diff
350 lines
12 KiB
Diff
|
|
From a5d0727f516b27e39b1f223e8dcf57b4c1bf95ea Mon Sep 17 00:00:00 2001
|
||
|
|
From: fangyi <eric.fangyi@huawei.com>
|
||
|
|
Date: Thu, 16 Nov 2023 09:54:56 +0800
|
||
|
|
Subject: [PATCH] vhost: stick to -errno error return convention
|
||
|
|
|
||
|
|
The generic vhost code expects that many of the VhostOps methods in the
|
||
|
|
respective backends set errno on errors. However, none of the existing
|
||
|
|
backends actually bothers to do so. In a number of those methods errno
|
||
|
|
from the failed call is clobbered by successful later calls to some
|
||
|
|
library functions; on a few code paths the generic vhost code then
|
||
|
|
negates and returns that errno, thus making failures look as successes
|
||
|
|
to the caller.
|
||
|
|
|
||
|
|
As a result, in certain scenarios (e.g. live migration) the device
|
||
|
|
doesn't notice the first failure and goes on through its state
|
||
|
|
transitions as if everything is ok, instead of taking recovery actions
|
||
|
|
(break and reestablish the vhost-user connection, cancel migration, etc)
|
||
|
|
before it's too late.
|
||
|
|
|
||
|
|
To fix this, consolidate on the convention to return negated errno on
|
||
|
|
failures throughout generic vhost, and use it for error propagation.
|
||
|
|
|
||
|
|
Signed-off-by: Roman Kagan <rvkagan@yandex-team.ru>
|
||
|
|
Message-Id: <20211111153354.18807-10-rvkagan@yandex-team.ru>
|
||
|
|
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
|
||
|
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||
|
|
Signed-off-by: fangyi <eric.fangyi@huawei.com>
|
||
|
|
---
|
||
|
|
hw/virtio/vhost.c | 100 +++++++++++++++++++++-------------------------
|
||
|
|
1 file changed, 46 insertions(+), 54 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
|
||
|
|
index c3f375f276..caa53443ab 100644
|
||
|
|
--- a/hw/virtio/vhost.c
|
||
|
|
+++ b/hw/virtio/vhost.c
|
||
|
|
@@ -34,11 +34,13 @@
|
||
|
|
#define _VHOST_DEBUG 1
|
||
|
|
|
||
|
|
#ifdef _VHOST_DEBUG
|
||
|
|
-#define VHOST_OPS_DEBUG(fmt, ...) \
|
||
|
|
- do { error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
|
||
|
|
- strerror(errno), errno); } while (0)
|
||
|
|
+#define VHOST_OPS_DEBUG(retval, fmt, ...) \
|
||
|
|
+ do { \
|
||
|
|
+ error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
|
||
|
|
+ strerror(-retval), -retval); \
|
||
|
|
+ } while (0)
|
||
|
|
#else
|
||
|
|
-#define VHOST_OPS_DEBUG(fmt, ...) \
|
||
|
|
+#define VHOST_OPS_DEBUG(retval, fmt, ...) \
|
||
|
|
do { } while (0)
|
||
|
|
#endif
|
||
|
|
|
||
|
|
@@ -300,7 +302,7 @@ static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size)
|
||
|
|
releasing the current log, to ensure no logging is lost */
|
||
|
|
r = dev->vhost_ops->vhost_set_log_base(dev, log_base, log);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_log_base failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_log_base failed");
|
||
|
|
}
|
||
|
|
|
||
|
|
vhost_log_put(dev, true);
|
||
|
|
@@ -552,7 +554,7 @@ static void vhost_commit(MemoryListener *listener)
|
||
|
|
if (!dev->log_enabled) {
|
||
|
|
r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_mem_table failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_mem_table failed");
|
||
|
|
}
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
@@ -566,7 +568,7 @@ static void vhost_commit(MemoryListener *listener)
|
||
|
|
}
|
||
|
|
r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_mem_table failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_mem_table failed");
|
||
|
|
}
|
||
|
|
/* To log less, can only decrease log size after table update. */
|
||
|
|
if (dev->log_size > log_size + VHOST_LOG_BUFFER) {
|
||
|
|
@@ -817,8 +819,8 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
|
||
|
|
if (dev->vhost_ops->vhost_vq_get_addr) {
|
||
|
|
r = dev->vhost_ops->vhost_vq_get_addr(dev, &addr, vq);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_vq_get_addr failed");
|
||
|
|
- return -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_vq_get_addr failed");
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
addr.desc_user_addr = (uint64_t)(unsigned long)vq->desc;
|
||
|
|
@@ -830,10 +832,9 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
|
||
|
|
addr.flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0;
|
||
|
|
r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_vring_addr failed");
|
||
|
|
- return -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_vring_addr failed");
|
||
|
|
}
|
||
|
|
- return 0;
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int vhost_dev_set_features(struct vhost_dev *dev,
|
||
|
|
@@ -854,19 +855,19 @@ static int vhost_dev_set_features(struct vhost_dev *dev,
|
||
|
|
}
|
||
|
|
r = dev->vhost_ops->vhost_set_features(dev, features);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_features failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_features failed");
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
if (dev->vhost_ops->vhost_set_backend_cap) {
|
||
|
|
r = dev->vhost_ops->vhost_set_backend_cap(dev);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_backend_cap failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_backend_cap failed");
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
- return r < 0 ? -errno : 0;
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log)
|
||
|
|
@@ -1021,22 +1022,17 @@ static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev,
|
||
|
|
bool is_big_endian,
|
||
|
|
int vhost_vq_index)
|
||
|
|
{
|
||
|
|
+ int r;
|
||
|
|
struct vhost_vring_state s = {
|
||
|
|
.index = vhost_vq_index,
|
||
|
|
.num = is_big_endian
|
||
|
|
};
|
||
|
|
|
||
|
|
- if (!dev->vhost_ops->vhost_set_vring_endian(dev, &s)) {
|
||
|
|
- return 0;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_vring_endian failed");
|
||
|
|
- if (errno == ENOTTY) {
|
||
|
|
- error_report("vhost does not support cross-endian");
|
||
|
|
- return -ENOSYS;
|
||
|
|
+ r = dev->vhost_ops->vhost_set_vring_endian(dev, &s);
|
||
|
|
+ if (r < 0) {
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_vring_endian failed");
|
||
|
|
}
|
||
|
|
-
|
||
|
|
- return -errno;
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int vhost_memory_region_lookup(struct vhost_dev *hdev,
|
||
|
|
@@ -1128,15 +1124,15 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
|
||
|
|
vq->num = state.num = virtio_queue_get_num(vdev, idx);
|
||
|
|
r = dev->vhost_ops->vhost_set_vring_num(dev, &state);
|
||
|
|
if (r) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_vring_num failed");
|
||
|
|
- return -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_vring_num failed");
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
state.num = virtio_queue_get_last_avail_idx(vdev, idx);
|
||
|
|
r = dev->vhost_ops->vhost_set_vring_base(dev, &state);
|
||
|
|
if (r) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_vring_base failed");
|
||
|
|
- return -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_vring_base failed");
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (vhost_needs_vring_endian(vdev)) {
|
||
|
|
@@ -1144,7 +1140,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
|
||
|
|
virtio_is_big_endian(vdev),
|
||
|
|
vhost_vq_index);
|
||
|
|
if (r) {
|
||
|
|
- return -errno;
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1172,15 +1168,13 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
|
||
|
|
|
||
|
|
r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled);
|
||
|
|
if (r < 0) {
|
||
|
|
- r = -errno;
|
||
|
|
goto fail_alloc;
|
||
|
|
}
|
||
|
|
|
||
|
|
file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
|
||
|
|
r = dev->vhost_ops->vhost_set_vring_kick(dev, &file);
|
||
|
|
if (r) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_vring_kick failed");
|
||
|
|
- r = -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_vring_kick failed");
|
||
|
|
goto fail_kick;
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1240,7 +1234,7 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
|
||
|
|
|
||
|
|
r = dev->vhost_ops->vhost_get_vring_base(dev, &state);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost VQ %u ring restore failed: %d", idx, r);
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost VQ %u ring restore failed: %d", idx, r);
|
||
|
|
/* Connection to the backend is broken, so let's sync internal
|
||
|
|
* last avail idx to the device used idx.
|
||
|
|
*/
|
||
|
|
@@ -1284,7 +1278,7 @@ static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev,
|
||
|
|
|
||
|
|
r = dev->vhost_ops->vhost_set_vring_busyloop_timeout(dev, &state);
|
||
|
|
if (r) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_vring_busyloop_timeout failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_vring_busyloop_timeout failed");
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1306,8 +1300,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
|
||
|
|
file.fd = event_notifier_get_fd(&vq->masked_notifier);
|
||
|
|
r = dev->vhost_ops->vhost_set_vring_call(dev, &file);
|
||
|
|
if (r) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_vring_call failed");
|
||
|
|
- r = -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_vring_call failed");
|
||
|
|
goto fail_call;
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1584,7 +1577,7 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
|
||
|
|
file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n);
|
||
|
|
r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_vring_call failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_vring_call failed");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1622,7 +1615,7 @@ void vhost_config_mask(struct vhost_dev *hdev, VirtIODevice *vdev, bool mask)
|
||
|
|
}
|
||
|
|
r = hdev->vhost_ops->vhost_set_config_call(hdev, fd);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_config_call failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_config_call failed");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1687,7 +1680,7 @@ int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config,
|
||
|
|
}
|
||
|
|
|
||
|
|
error_setg(errp, "vhost_get_config not implemented");
|
||
|
|
- return -ENOTSUP;
|
||
|
|
+ return -ENOSYS;
|
||
|
|
}
|
||
|
|
|
||
|
|
int vhost_dev_set_config(struct vhost_dev *hdev, const uint8_t *data,
|
||
|
|
@@ -1700,7 +1693,7 @@ int vhost_dev_set_config(struct vhost_dev *hdev, const uint8_t *data,
|
||
|
|
size, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
- return -1;
|
||
|
|
+ return -ENOSYS;
|
||
|
|
}
|
||
|
|
|
||
|
|
void vhost_dev_set_config_notifier(struct vhost_dev *hdev,
|
||
|
|
@@ -1729,7 +1722,7 @@ static int vhost_dev_resize_inflight(struct vhost_inflight *inflight,
|
||
|
|
|
||
|
|
if (err) {
|
||
|
|
error_report_err(err);
|
||
|
|
- return -1;
|
||
|
|
+ return -ENOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
vhost_dev_free_inflight(inflight);
|
||
|
|
@@ -1762,8 +1755,9 @@ int vhost_dev_load_inflight(struct vhost_inflight *inflight, QEMUFile *f)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (inflight->size != size) {
|
||
|
|
- if (vhost_dev_resize_inflight(inflight, size)) {
|
||
|
|
- return -1;
|
||
|
|
+ int ret = vhost_dev_resize_inflight(inflight, size);
|
||
|
|
+ if (ret < 0) {
|
||
|
|
+ return ret;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
inflight->queue_size = qemu_get_be16(f);
|
||
|
|
@@ -1786,7 +1780,7 @@ int vhost_dev_prepare_inflight(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||
|
|
|
||
|
|
r = vhost_dev_set_features(hdev, hdev->log_enabled);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_dev_prepare_inflight failed");
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_dev_prepare_inflight failed");
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1801,8 +1795,8 @@ int vhost_dev_set_inflight(struct vhost_dev *dev,
|
||
|
|
if (dev->vhost_ops->vhost_set_inflight_fd && inflight->addr) {
|
||
|
|
r = dev->vhost_ops->vhost_set_inflight_fd(dev, inflight);
|
||
|
|
if (r) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_inflight_fd failed");
|
||
|
|
- return -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_inflight_fd failed");
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1817,8 +1811,8 @@ int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size,
|
||
|
|
if (dev->vhost_ops->vhost_get_inflight_fd) {
|
||
|
|
r = dev->vhost_ops->vhost_get_inflight_fd(dev, queue_size, inflight);
|
||
|
|
if (r) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_get_inflight_fd failed");
|
||
|
|
- return -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_get_inflight_fd failed");
|
||
|
|
+ return r;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -1847,8 +1841,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||
|
|
|
||
|
|
r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_mem_table failed");
|
||
|
|
- r = -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_mem_table failed");
|
||
|
|
goto fail_mem;
|
||
|
|
}
|
||
|
|
for (i = 0; i < hdev->nvqs; ++i) {
|
||
|
|
@@ -1882,8 +1875,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||
|
|
hdev->log_size ? log_base : 0,
|
||
|
|
hdev->log);
|
||
|
|
if (r < 0) {
|
||
|
|
- VHOST_OPS_DEBUG("vhost_set_log_base failed");
|
||
|
|
- r = -errno;
|
||
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_log_base failed");
|
||
|
|
goto fail_log;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
@@ -1963,7 +1955,7 @@ int vhost_net_set_backend(struct vhost_dev *hdev,
|
||
|
|
return hdev->vhost_ops->vhost_net_set_backend(hdev, file);
|
||
|
|
}
|
||
|
|
|
||
|
|
- return -1;
|
||
|
|
+ return -ENOSYS;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool used_memslots_is_exceeded(void)
|
||
|
|
--
|
||
|
|
2.27.0
|
||
|
|
|