vdpa: Send cvq state load commands in parallel

This patch enables sending CVQ state load commands
in parallel at device startup by following steps:

  * Refactor vhost_vdpa_net_load_cmd() to iterate through
the control commands shadow buffers. This allows different
CVQ state load commands to use their own unique buffers.

  * Delay the polling and checking of buffers until either
the SVQ is full or control commands shadow buffers are full.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1578
Signed-off-by: Hawkins Jiawei <yin31149@gmail.com>
Acked-by: Eugenio Pérez <eperezma@redhat.com>
Message-Id: <9350f32278e39f7bce297b8f2d82dac27c6f8c9a.1697165821.git.yin31149@gmail.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
Hawkins Jiawei 2023-10-13 16:09:42 +08:00 committed by Michael S. Tsirkin
parent 99d6a32469
commit acec5f685c

View File

@ -661,6 +661,31 @@ static void vhost_vdpa_net_load_cursor_reset(VhostVDPAState *s,
in_cursor->iov_len = vhost_vdpa_net_cvq_cmd_page_len();
}
/*
* Poll SVQ for multiple pending control commands and check the device's ack.
*
* Caller should hold the BQL when invoking this function.
*
* @s: The VhostVDPAState
* @len: The length of the pending status shadow buffer
*/
static ssize_t vhost_vdpa_net_svq_flush(VhostVDPAState *s, size_t len)
{
/* device uses a one-byte length ack for each control command */
ssize_t dev_written = vhost_vdpa_net_svq_poll(s, len);
if (unlikely(dev_written != len)) {
return -EIO;
}
/* check the device's ack */
for (int i = 0; i < len; ++i) {
if (s->status[i] != VIRTIO_NET_OK) {
return -EIO;
}
}
return 0;
}
static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s,
struct iovec *out_cursor,
struct iovec *in_cursor, uint8_t class,
@ -671,11 +696,31 @@ static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s,
.class = class,
.cmd = cmd,
};
size_t data_size = iov_size(data_sg, data_num);
size_t data_size = iov_size(data_sg, data_num), cmd_size;
struct iovec out, in;
ssize_t r;
unsigned dummy_cursor_iov_cnt;
VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
cmd_size = sizeof(ctrl) + data_size;
if (vhost_svq_available_slots(svq) < 2 ||
iov_size(out_cursor, 1) < cmd_size) {
/*
* It is time to flush all pending control commands if SVQ is full
* or control commands shadow buffers are full.
*
* We can poll here since we've had BQL from the time
* we sent the descriptor.
*/
r = vhost_vdpa_net_svq_flush(s, in_cursor->iov_base -
(void *)s->status);
if (unlikely(r < 0)) {
return r;
}
vhost_vdpa_net_load_cursor_reset(s, out_cursor, in_cursor);
}
/* pack the CVQ command header */
iov_from_buf(out_cursor, 1, 0, &ctrl, sizeof(ctrl));
@ -684,7 +729,7 @@ static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s,
out_cursor->iov_base + sizeof(ctrl), data_size);
/* extract the required buffer from the cursor for output */
iov_copy(&out, 1, out_cursor, 1, 0, sizeof(ctrl) + data_size);
iov_copy(&out, 1, out_cursor, 1, 0, cmd_size);
/* extract the required buffer from the cursor for input */
iov_copy(&in, 1, in_cursor, 1, 0, sizeof(*s->status));
@ -693,11 +738,13 @@ static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s,
return r;
}
/*
* We can poll here since we've had BQL from the time
* we sent the descriptor.
*/
return vhost_vdpa_net_svq_poll(s, 1);
/* iterate the cursors */
dummy_cursor_iov_cnt = 1;
iov_discard_front(&out_cursor, &dummy_cursor_iov_cnt, cmd_size);
dummy_cursor_iov_cnt = 1;
iov_discard_front(&in_cursor, &dummy_cursor_iov_cnt, sizeof(*s->status));
return 0;
}
static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
@ -709,15 +756,12 @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
.iov_base = (void *)n->mac,
.iov_len = sizeof(n->mac),
};
ssize_t dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MAC,
VIRTIO_NET_CTRL_MAC_ADDR_SET,
&data, 1);
if (unlikely(dev_written < 0)) {
return dev_written;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MAC,
VIRTIO_NET_CTRL_MAC_ADDR_SET,
&data, 1);
if (unlikely(r < 0)) {
return r;
}
}
@ -762,15 +806,12 @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
.iov_len = mul_macs_size,
},
};
ssize_t dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MAC,
VIRTIO_NET_CTRL_MAC_TABLE_SET,
data, ARRAY_SIZE(data));
if (unlikely(dev_written < 0)) {
return dev_written;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MAC,
VIRTIO_NET_CTRL_MAC_TABLE_SET,
data, ARRAY_SIZE(data));
if (unlikely(r < 0)) {
return r;
}
return 0;
@ -782,7 +823,7 @@ static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
struct iovec *in_cursor)
{
struct virtio_net_ctrl_mq mq;
ssize_t dev_written;
ssize_t r;
if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_MQ)) {
return 0;
@ -793,15 +834,12 @@ static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
.iov_base = &mq,
.iov_len = sizeof(mq),
};
dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MQ,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
&data, 1);
if (unlikely(dev_written < 0)) {
return dev_written;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MQ,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
&data, 1);
if (unlikely(r < 0)) {
return r;
}
return 0;
@ -813,7 +851,7 @@ static int vhost_vdpa_net_load_offloads(VhostVDPAState *s,
struct iovec *in_cursor)
{
uint64_t offloads;
ssize_t dev_written;
ssize_t r;
if (!virtio_vdev_has_feature(&n->parent_obj,
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
@ -841,15 +879,12 @@ static int vhost_vdpa_net_load_offloads(VhostVDPAState *s,
.iov_base = &offloads,
.iov_len = sizeof(offloads),
};
dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_GUEST_OFFLOADS,
VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET,
&data, 1);
if (unlikely(dev_written < 0)) {
return dev_written;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_GUEST_OFFLOADS,
VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET,
&data, 1);
if (unlikely(r < 0)) {
return r;
}
return 0;
@ -865,16 +900,12 @@ static int vhost_vdpa_net_load_rx_mode(VhostVDPAState *s,
.iov_base = &on,
.iov_len = sizeof(on),
};
ssize_t dev_written;
ssize_t r;
dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_RX,
cmd, &data, 1);
if (unlikely(dev_written < 0)) {
return dev_written;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_RX, cmd, &data, 1);
if (unlikely(r < 0)) {
return r;
}
return 0;
@ -1031,15 +1062,12 @@ static int vhost_vdpa_net_load_single_vlan(VhostVDPAState *s,
.iov_base = &vid,
.iov_len = sizeof(vid),
};
ssize_t dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_VLAN,
VIRTIO_NET_CTRL_VLAN_ADD,
&data, 1);
if (unlikely(dev_written < 0)) {
return dev_written;
}
if (unlikely(*s->status != VIRTIO_NET_OK)) {
return -EIO;
ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_VLAN,
VIRTIO_NET_CTRL_VLAN_ADD,
&data, 1);
if (unlikely(r < 0)) {
return r;
}
return 0;
@ -1106,6 +1134,17 @@ static int vhost_vdpa_net_cvq_load(NetClientState *nc)
if (unlikely(r)) {
return r;
}
/*
* We need to poll and check all pending device's used buffers.
*
* We can poll here since we've had BQL from the time
* we sent the descriptor.
*/
r = vhost_vdpa_net_svq_flush(s, in_cursor.iov_base - (void *)s->status);
if (unlikely(r)) {
return r;
}
}
for (int i = 0; i < v->dev->vq_index; ++i) {