* [dpdk-dev] [PATCH v2 1/4] vhost: prevent features to be changed while device is running
2017-12-05 8:34 [dpdk-dev] [PATCH v2 0/4] Vhost: fix mq=on but VIRTIO_NET_F_MQ not negotiated Maxime Coquelin
@ 2017-12-05 8:34 ` Maxime Coquelin
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 2/4] vhost: propagate VHOST_USER_SET_FEATURES handling error Maxime Coquelin
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Maxime Coquelin @ 2017-12-05 8:34 UTC (permalink / raw)
To: dev, yliu, tiwei.bie, jianfeng.tan; +Cc: lprosek, lersek, Maxime Coquelin
As section 2.2 of the Virtio spec states about features
negotiation:
"During device initialization, the driver reads this and tells
the device the subset that it accepts. The only way to
renegotiate is to reset the device."
This patch implements a check to prevent illegal features change
while the device is running.
One exception is the VHOST_F_LOG_ALL feature bit, which is enabled
when live-migration is initiated. But this feature is not negotiated
with the Virtio driver, but directly with the Vhost master.
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
lib/librte_vhost/vhost_user.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index f4c7ce462..2d86c0ca8 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -183,7 +183,22 @@ vhost_user_set_features(struct virtio_net *dev, uint64_t features)
return -1;
}
- if ((dev->flags & VIRTIO_DEV_RUNNING) && dev->features != features) {
+ if (dev->features == features)
+ return 0;
+
+ if (dev->flags & VIRTIO_DEV_RUNNING) {
+ /*
+ * Error out if master tries to change features while device is
+ * in running state. The exception being VHOST_F_LOG_ALL, which
+ * is enabled when the live-migration starts.
+ */
+ if ((dev->features ^ features) & ~(1ULL << VHOST_F_LOG_ALL)) {
+ RTE_LOG(ERR, VHOST_CONFIG,
+ "(%d) features changed while device is running.\n",
+ dev->vid);
+ return -1;
+ }
+
if (dev->notify_ops->features_changed)
dev->notify_ops->features_changed(dev->vid, features);
}
--
2.14.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [dpdk-dev] [PATCH v2 2/4] vhost: propagate VHOST_USER_SET_FEATURES handling error
2017-12-05 8:34 [dpdk-dev] [PATCH v2 0/4] Vhost: fix mq=on but VIRTIO_NET_F_MQ not negotiated Maxime Coquelin
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 1/4] vhost: prevent features to be changed while device is running Maxime Coquelin
@ 2017-12-05 8:34 ` Maxime Coquelin
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 3/4] vhost: extract virtqueue cleaning and freeing functions Maxime Coquelin
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 4/4] vhost: destroy unused virtqueues when multiqueue not negotiated Maxime Coquelin
3 siblings, 0 replies; 7+ messages in thread
From: Maxime Coquelin @ 2017-12-05 8:34 UTC (permalink / raw)
To: dev, yliu, tiwei.bie, jianfeng.tan; +Cc: lprosek, lersek, Maxime Coquelin
Not propagating VHOST_USER_SET_FEATURES request handling
error may result in unpredictable behavior, as host and
guests features may no more be synchronized.
This patch fixes this by reporting the error to the upper
layer, which would result in the device being destroyed
and the connection with the master to be closed.
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
lib/librte_vhost/vhost_user.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 2d86c0ca8..a5e1f2482 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -1263,7 +1263,9 @@ vhost_user_msg_handler(int vid, int fd)
send_vhost_reply(fd, &msg);
break;
case VHOST_USER_SET_FEATURES:
- vhost_user_set_features(dev, msg.payload.u64);
+ ret = vhost_user_set_features(dev, msg.payload.u64);
+ if (ret)
+ return -1;
break;
case VHOST_USER_GET_PROTOCOL_FEATURES:
--
2.14.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [dpdk-dev] [PATCH v2 3/4] vhost: extract virtqueue cleaning and freeing functions
2017-12-05 8:34 [dpdk-dev] [PATCH v2 0/4] Vhost: fix mq=on but VIRTIO_NET_F_MQ not negotiated Maxime Coquelin
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 1/4] vhost: prevent features to be changed while device is running Maxime Coquelin
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 2/4] vhost: propagate VHOST_USER_SET_FEATURES handling error Maxime Coquelin
@ 2017-12-05 8:34 ` Maxime Coquelin
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 4/4] vhost: destroy unused virtqueues when multiqueue not negotiated Maxime Coquelin
3 siblings, 0 replies; 7+ messages in thread
From: Maxime Coquelin @ 2017-12-05 8:34 UTC (permalink / raw)
To: dev, yliu, tiwei.bie, jianfeng.tan; +Cc: lprosek, lersek, Maxime Coquelin
This patch extracts needed code for vhost_user.c to be able
to clean and free virtqueues unitary.
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
lib/librte_vhost/vhost.c | 22 ++++++++++++----------
lib/librte_vhost/vhost.h | 3 +++
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/lib/librte_vhost/vhost.c b/lib/librte_vhost/vhost.c
index 4f8b73a09..df528a4ea 100644
--- a/lib/librte_vhost/vhost.c
+++ b/lib/librte_vhost/vhost.c
@@ -103,7 +103,7 @@ get_device(int vid)
return dev;
}
-static void
+void
cleanup_vq(struct vhost_virtqueue *vq, int destroy)
{
if ((vq->callfd >= 0) && (destroy != 0))
@@ -127,6 +127,15 @@ cleanup_device(struct virtio_net *dev, int destroy)
cleanup_vq(dev->virtqueue[i], destroy);
}
+void
+free_vq(struct vhost_virtqueue *vq)
+{
+ rte_free(vq->shadow_used_ring);
+ rte_free(vq->batch_copy_elems);
+ rte_mempool_free(vq->iotlb_pool);
+ rte_free(vq);
+}
+
/*
* Release virtqueues and device memory.
*/
@@ -134,16 +143,9 @@ static void
free_device(struct virtio_net *dev)
{
uint32_t i;
- struct vhost_virtqueue *vq;
-
- for (i = 0; i < dev->nr_vring; i++) {
- vq = dev->virtqueue[i];
- rte_free(vq->shadow_used_ring);
- rte_free(vq->batch_copy_elems);
- rte_mempool_free(vq->iotlb_pool);
- rte_free(vq);
- }
+ for (i = 0; i < dev->nr_vring; i++)
+ free_vq(dev->virtqueue[i]);
rte_free(dev);
}
diff --git a/lib/librte_vhost/vhost.h b/lib/librte_vhost/vhost.h
index 1cc81c17c..9cad1bb3c 100644
--- a/lib/librte_vhost/vhost.h
+++ b/lib/librte_vhost/vhost.h
@@ -364,6 +364,9 @@ void cleanup_device(struct virtio_net *dev, int destroy);
void reset_device(struct virtio_net *dev);
void vhost_destroy_device(int);
+void cleanup_vq(struct vhost_virtqueue *vq, int destroy);
+void free_vq(struct vhost_virtqueue *vq);
+
int alloc_vring_queue(struct virtio_net *dev, uint32_t vring_idx);
void vhost_set_ifname(int, const char *if_name, unsigned int if_len);
--
2.14.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [dpdk-dev] [PATCH v2 4/4] vhost: destroy unused virtqueues when multiqueue not negotiated
2017-12-05 8:34 [dpdk-dev] [PATCH v2 0/4] Vhost: fix mq=on but VIRTIO_NET_F_MQ not negotiated Maxime Coquelin
` (2 preceding siblings ...)
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 3/4] vhost: extract virtqueue cleaning and freeing functions Maxime Coquelin
@ 2017-12-05 8:34 ` Maxime Coquelin
2017-12-05 14:40 ` Laszlo Ersek
3 siblings, 1 reply; 7+ messages in thread
From: Maxime Coquelin @ 2017-12-05 8:34 UTC (permalink / raw)
To: dev, yliu, tiwei.bie, jianfeng.tan; +Cc: lprosek, lersek, Maxime Coquelin
QEMU sends VHOST_USER_SET_VRING_CALL requests for all queues
declared in QEMU command line before the guest is started.
It has the effect in DPDK vhost-user backend to allocate vrings
for all queues declared by QEMU.
If the first driver being used does not support multiqueue,
the device never changes to VIRTIO_DEV_RUNNING state as only
the first queue pair is initialized. One driver impacted by
this bug is virtio-net's iPXE driver which does not support
VIRTIO_NET_F_MQ feature.
It is safe to destroy unused virtqueues in SET_FEATURES request
handler, as it is ensured the device is not in running state
at this stage, so virtqueues aren't being processed.
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
lib/librte_vhost/vhost_user.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index a5e1f2482..b17080215 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -173,6 +173,7 @@ vhost_user_get_features(struct virtio_net *dev)
static int
vhost_user_set_features(struct virtio_net *dev, uint64_t features)
{
+ int i;
uint64_t vhost_features = 0;
rte_vhost_driver_get_features(dev->ifname, &vhost_features);
@@ -216,6 +217,24 @@ vhost_user_set_features(struct virtio_net *dev, uint64_t features)
(dev->features & (1 << VIRTIO_NET_F_MRG_RXBUF)) ? "on" : "off",
(dev->features & (1ULL << VIRTIO_F_VERSION_1)) ? "on" : "off");
+ if (!(dev->features & (1ULL << VIRTIO_NET_F_MQ))) {
+ /*
+ * Remove all but first queue pair if MQ hasn't been
+ * negotiated. This is safe because the device is not
+ * running at this stage.
+ */
+ for (i = dev->nr_vring; i > 1; i--) {
+ struct vhost_virtqueue *vq = dev->virtqueue[i];
+
+ if (!vq)
+ continue;
+
+ cleanup_vq(vq, 1);
+ free_vq(vq);
+ dev->nr_vring--;
+ }
+ }
+
return 0;
}
--
2.14.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [dpdk-dev] [PATCH v2 4/4] vhost: destroy unused virtqueues when multiqueue not negotiated
2017-12-05 8:34 ` [dpdk-dev] [PATCH v2 4/4] vhost: destroy unused virtqueues when multiqueue not negotiated Maxime Coquelin
@ 2017-12-05 14:40 ` Laszlo Ersek
2017-12-05 14:56 ` Maxime Coquelin
0 siblings, 1 reply; 7+ messages in thread
From: Laszlo Ersek @ 2017-12-05 14:40 UTC (permalink / raw)
To: Maxime Coquelin, dev, yliu, tiwei.bie, jianfeng.tan; +Cc: lprosek
Hi Maxime,
On 12/05/17 09:34, Maxime Coquelin wrote:
> QEMU sends VHOST_USER_SET_VRING_CALL requests for all queues
> declared in QEMU command line before the guest is started.
> It has the effect in DPDK vhost-user backend to allocate vrings
> for all queues declared by QEMU.
>
> If the first driver being used does not support multiqueue,
> the device never changes to VIRTIO_DEV_RUNNING state as only
> the first queue pair is initialized. One driver impacted by
> this bug is virtio-net's iPXE driver which does not support
> VIRTIO_NET_F_MQ feature.
>
> It is safe to destroy unused virtqueues in SET_FEATURES request
> handler, as it is ensured the device is not in running state
> at this stage, so virtqueues aren't being processed.
>
> Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
> ---
> lib/librte_vhost/vhost_user.c | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
>
> diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
> index a5e1f2482..b17080215 100644
> --- a/lib/librte_vhost/vhost_user.c
> +++ b/lib/librte_vhost/vhost_user.c
> @@ -173,6 +173,7 @@ vhost_user_get_features(struct virtio_net *dev)
> static int
> vhost_user_set_features(struct virtio_net *dev, uint64_t features)
> {
> + int i;
> uint64_t vhost_features = 0;
>
> rte_vhost_driver_get_features(dev->ifname, &vhost_features);
> @@ -216,6 +217,24 @@ vhost_user_set_features(struct virtio_net *dev, uint64_t features)
> (dev->features & (1 << VIRTIO_NET_F_MRG_RXBUF)) ? "on" : "off",
> (dev->features & (1ULL << VIRTIO_F_VERSION_1)) ? "on" : "off");
>
> + if (!(dev->features & (1ULL << VIRTIO_NET_F_MQ))) {
> + /*
> + * Remove all but first queue pair if MQ hasn't been
> + * negotiated. This is safe because the device is not
> + * running at this stage.
> + */
> + for (i = dev->nr_vring; i > 1; i--) {
> + struct vhost_virtqueue *vq = dev->virtqueue[i];
Sorry, I don't have any experience with dpdk.
But, if "dev->virtqueue" has "dev->nr_vring" elements, then this loop is
off-by one; dev->virtqueue[dev->nr_vring] should never be accessed. For
example, if you have three queues, numbered 0, 1 and 2, this loop will
access/release virtqueue[3] (bad) and virtqueue[2] (good).
Instead, I suggest:
i = dev->nr_vring;
while (i > 2) {
struct vhost_virtqueue *vq;
vq = dev->virtqueue[--i];
/* the rest here */
}
The loop body is entered with "i" standing for "how many queues are left
that should be freed".
Thanks
Laszlo
> +
> + if (!vq)
> + continue;
> +
> + cleanup_vq(vq, 1);
> + free_vq(vq);
> + dev->nr_vring--;
> + }
> + }
> +
> return 0;
> }
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [dpdk-dev] [PATCH v2 4/4] vhost: destroy unused virtqueues when multiqueue not negotiated
2017-12-05 14:40 ` Laszlo Ersek
@ 2017-12-05 14:56 ` Maxime Coquelin
0 siblings, 0 replies; 7+ messages in thread
From: Maxime Coquelin @ 2017-12-05 14:56 UTC (permalink / raw)
To: Laszlo Ersek, dev, yliu, tiwei.bie, jianfeng.tan; +Cc: lprosek
Hi Laszlo,
On 12/05/2017 03:40 PM, Laszlo Ersek wrote:
> Hi Maxime,
>
> On 12/05/17 09:34, Maxime Coquelin wrote:
>> QEMU sends VHOST_USER_SET_VRING_CALL requests for all queues
>> declared in QEMU command line before the guest is started.
>> It has the effect in DPDK vhost-user backend to allocate vrings
>> for all queues declared by QEMU.
>>
>> If the first driver being used does not support multiqueue,
>> the device never changes to VIRTIO_DEV_RUNNING state as only
>> the first queue pair is initialized. One driver impacted by
>> this bug is virtio-net's iPXE driver which does not support
>> VIRTIO_NET_F_MQ feature.
>>
>> It is safe to destroy unused virtqueues in SET_FEATURES request
>> handler, as it is ensured the device is not in running state
>> at this stage, so virtqueues aren't being processed.
>>
>> Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
>> ---
>> lib/librte_vhost/vhost_user.c | 19 +++++++++++++++++++
>> 1 file changed, 19 insertions(+)
>>
>> diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
>> index a5e1f2482..b17080215 100644
>> --- a/lib/librte_vhost/vhost_user.c
>> +++ b/lib/librte_vhost/vhost_user.c
>> @@ -173,6 +173,7 @@ vhost_user_get_features(struct virtio_net *dev)
>> static int
>> vhost_user_set_features(struct virtio_net *dev, uint64_t features)
>> {
>> + int i;
>> uint64_t vhost_features = 0;
>>
>> rte_vhost_driver_get_features(dev->ifname, &vhost_features);
>> @@ -216,6 +217,24 @@ vhost_user_set_features(struct virtio_net *dev, uint64_t features)
>> (dev->features & (1 << VIRTIO_NET_F_MRG_RXBUF)) ? "on" : "off",
>> (dev->features & (1ULL << VIRTIO_F_VERSION_1)) ? "on" : "off");
>>
>> + if (!(dev->features & (1ULL << VIRTIO_NET_F_MQ))) {
>> + /*
>> + * Remove all but first queue pair if MQ hasn't been
>> + * negotiated. This is safe because the device is not
>> + * running at this stage.
>> + */
>> + for (i = dev->nr_vring; i > 1; i--) {
>> + struct vhost_virtqueue *vq = dev->virtqueue[i];
>
> Sorry, I don't have any experience with dpdk.
>
> But, if "dev->virtqueue" has "dev->nr_vring" elements, then this loop is
> off-by one; dev->virtqueue[dev->nr_vring] should never be accessed. For
> example, if you have three queues, numbered 0, 1 and 2, this loop will
> access/release virtqueue[3] (bad) and virtqueue[2] (good).
Right, thanks for spotting this.
I didn't noticed my mistake while testing it because of the NULL check
in the loop.
> Instead, I suggest:
>
> i = dev->nr_vring;
> while (i > 2) {
> struct vhost_virtqueue *vq;
>
> vq = dev->virtqueue[--i];
> /* the rest here */
> }
>
> The loop body is entered with "i" standing for "how many queues are left
> that should be freed".
Yes, that sounds cleaner. I think dev->nr_vring can safely be
decremented directly, so "i" could be skipped.
Thanks!
Maxime
> Thanks
> Laszlo
>
>> +
>> + if (!vq)
>> + continue;
>> +
>> + cleanup_vq(vq, 1);
>> + free_vq(vq);
>> + dev->nr_vring--;
>> + }
>> + }
>> +
>> return 0;
>> }
>>
>>
>
^ permalink raw reply [flat|nested] 7+ messages in thread