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
1 changed files with 102 additions and 63 deletions

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(); 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, static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s,
struct iovec *out_cursor, struct iovec *out_cursor,
struct iovec *in_cursor, uint8_t class, struct iovec *in_cursor, uint8_t class,
@ -671,11 +696,31 @@ static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s,
.class = class, .class = class,
.cmd = cmd, .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; struct iovec out, in;
ssize_t r; 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)); 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 */ /* pack the CVQ command header */
iov_from_buf(out_cursor, 1, 0, &ctrl, sizeof(ctrl)); 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); out_cursor->iov_base + sizeof(ctrl), data_size);
/* extract the required buffer from the cursor for output */ /* 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 */ /* extract the required buffer from the cursor for input */
iov_copy(&in, 1, in_cursor, 1, 0, sizeof(*s->status)); 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; return r;
} }
/* /* iterate the cursors */
* We can poll here since we've had BQL from the time dummy_cursor_iov_cnt = 1;
* we sent the descriptor. iov_discard_front(&out_cursor, &dummy_cursor_iov_cnt, cmd_size);
*/ dummy_cursor_iov_cnt = 1;
return vhost_vdpa_net_svq_poll(s, 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, 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_base = (void *)n->mac,
.iov_len = sizeof(n->mac), .iov_len = sizeof(n->mac),
}; };
ssize_t dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC,
VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_ADDR_SET,
&data, 1); &data, 1);
if (unlikely(dev_written < 0)) { if (unlikely(r < 0)) {
return dev_written; return r;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
} }
} }
@ -762,15 +806,12 @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
.iov_len = mul_macs_size, .iov_len = mul_macs_size,
}, },
}; };
ssize_t dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC,
VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET,
data, ARRAY_SIZE(data)); data, ARRAY_SIZE(data));
if (unlikely(dev_written < 0)) { if (unlikely(r < 0)) {
return dev_written; return r;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
} }
return 0; return 0;
@ -782,7 +823,7 @@ static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
struct iovec *in_cursor) struct iovec *in_cursor)
{ {
struct virtio_net_ctrl_mq mq; 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)) { if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_MQ)) {
return 0; return 0;
@ -793,15 +834,12 @@ static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
.iov_base = &mq, .iov_base = &mq,
.iov_len = sizeof(mq), .iov_len = sizeof(mq),
}; };
dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
&data, 1); &data, 1);
if (unlikely(dev_written < 0)) { if (unlikely(r < 0)) {
return dev_written; return r;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
} }
return 0; return 0;
@ -813,7 +851,7 @@ static int vhost_vdpa_net_load_offloads(VhostVDPAState *s,
struct iovec *in_cursor) struct iovec *in_cursor)
{ {
uint64_t offloads; uint64_t offloads;
ssize_t dev_written; ssize_t r;
if (!virtio_vdev_has_feature(&n->parent_obj, if (!virtio_vdev_has_feature(&n->parent_obj,
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
@ -841,15 +879,12 @@ static int vhost_vdpa_net_load_offloads(VhostVDPAState *s,
.iov_base = &offloads, .iov_base = &offloads,
.iov_len = sizeof(offloads), .iov_len = sizeof(offloads),
}; };
dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS,
VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET,
&data, 1); &data, 1);
if (unlikely(dev_written < 0)) { if (unlikely(r < 0)) {
return dev_written; return r;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
} }
return 0; return 0;
@ -865,16 +900,12 @@ static int vhost_vdpa_net_load_rx_mode(VhostVDPAState *s,
.iov_base = &on, .iov_base = &on,
.iov_len = sizeof(on), .iov_len = sizeof(on),
}; };
ssize_t dev_written; ssize_t r;
dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX, cmd, &data, 1);
cmd, &data, 1); if (unlikely(r < 0)) {
if (unlikely(dev_written < 0)) { return r;
return dev_written;
}
if (*s->status != VIRTIO_NET_OK) {
return -EIO;
} }
return 0; return 0;
@ -1031,15 +1062,12 @@ static int vhost_vdpa_net_load_single_vlan(VhostVDPAState *s,
.iov_base = &vid, .iov_base = &vid,
.iov_len = sizeof(vid), .iov_len = sizeof(vid),
}; };
ssize_t dev_written = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN,
VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_ADD,
&data, 1); &data, 1);
if (unlikely(dev_written < 0)) { if (unlikely(r < 0)) {
return dev_written; return r;
}
if (unlikely(*s->status != VIRTIO_NET_OK)) {
return -EIO;
} }
return 0; return 0;
@ -1106,6 +1134,17 @@ static int vhost_vdpa_net_cvq_load(NetClientState *nc)
if (unlikely(r)) { if (unlikely(r)) {
return 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) { for (int i = 0; i < v->dev->vq_index; ++i) {