@@ -9,40 +9,56 @@
int uip_tx(struct iovec *iov, u16 out, struct uip_info *info)
{
void *vnet;
+ ssize_t len;
struct uip_tx_arg arg;
- int eth_len, vnet_len;
+ size_t eth_len, vnet_len;
struct uip_eth *eth;
- u8 *buf = NULL;
+ void *vnet_buf = NULL;
+ void *eth_buf = NULL;
+ size_t iovcount = out;
+
u16 proto;
- int i;
/*
* Buffer from guest to device
*/
- vnet_len = iov[0].iov_len;
+ vnet_len = info->vnet_hdr_len;
vnet = iov[0].iov_base;
- eth_len = iov[1].iov_len;
- eth = iov[1].iov_base;
+ len = iov_size(iov, iovcount);
+ if (len <= (ssize_t)vnet_len)
+ return -EINVAL;
+
+ /* Try to avoid memcpy if possible */
+ if (iov[0].iov_len == vnet_len && out == 2) {
+ /* Legacy layout: first descriptor for vnet header */
+ eth = iov[1].iov_base;
+ eth_len = iov[1].iov_len;
+
+ } else if (out == 1) {
+ /* Single descriptor */
+ eth = (void *)vnet + vnet_len;
+ eth_len = iov[0].iov_len - vnet_len;
+
+ } else {
+ /* Any layout */
+ len = vnet_len;
+ vnet = vnet_buf = malloc(len);
+ if (!vnet)
+ return -ENOMEM;
- /*
- * In case, ethernet frame is in more than one iov entry.
- * Copy iov buffer into one linear buffer.
- */
- if (out > 2) {
- eth_len = 0;
- for (i = 1; i < out; i++)
- eth_len += iov[i].iov_len;
+ len = memcpy_fromiovec_safe(vnet_buf, &iov, len, &iovcount);
+ if (len)
+ goto out_free_buf;
- buf = malloc(eth_len);
- if (!buf)
- return -ENOMEM;
+ len = eth_len = iov_size(iov, iovcount);
+ eth = eth_buf = malloc(len);
+ if (!eth)
+ goto out_free_buf;
- eth = (struct uip_eth *)buf;
- for (i = 1; i < out; i++) {
- memcpy(buf, iov[i].iov_base, iov[i].iov_len);
- buf += iov[i].iov_len;
- }
+ len = memcpy_fromiovec_safe(eth_buf, &iov, len, &iovcount);
+ if (len)
+ goto out_free_buf;
}
memset(&arg, 0, sizeof(arg));
@@ -65,14 +81,17 @@ int uip_tx(struct iovec *iov, u16 out, struct uip_info *info)
case UIP_ETH_P_IP:
uip_tx_do_ipv4(&arg);
break;
- default:
- break;
}
- if (out > 2 && buf)
- free(eth);
+ free(vnet_buf);
+ free(eth_buf);
return vnet_len + eth_len;
+
+out_free_buf:
+ free(vnet_buf);
+ free(eth_buf);
+ return -EINVAL;
}
int uip_rx(struct iovec *iov, u16 in, struct uip_info *info)
@@ -221,8 +221,9 @@ static void *virtio_net_ctrl_thread(void *p)
struct net_dev *ndev = queue->ndev;
u16 out, in, head;
struct kvm *kvm = ndev->kvm;
- struct virtio_net_ctrl_hdr *ctrl;
- virtio_net_ctrl_ack *ack;
+ struct virtio_net_ctrl_hdr ctrl;
+ virtio_net_ctrl_ack ack;
+ size_t len;
kvm__set_thread_name("virtio-net-ctrl");
@@ -234,18 +235,19 @@ static void *virtio_net_ctrl_thread(void *p)
while (virt_queue__available(vq)) {
head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
- ctrl = iov[0].iov_base;
- ack = iov[out].iov_base;
+ len = min(iov_size(iov, in), sizeof(ctrl));
+ memcpy_fromiovec((void *)&ctrl, iov, len);
- switch (ctrl->class) {
+ switch (ctrl.class) {
case VIRTIO_NET_CTRL_MQ:
- *ack = virtio_net_handle_mq(kvm, ndev, ctrl);
+ ack = virtio_net_handle_mq(kvm, ndev, &ctrl);
break;
default:
- *ack = VIRTIO_NET_ERR;
+ ack = VIRTIO_NET_ERR;
break;
}
- virt_queue__set_used_elem(vq, head, iov[out].iov_len);
+ memcpy_toiovec(iov + in, &ack, sizeof(ack));
+ virt_queue__set_used_elem(vq, head, sizeof(ack));
}
if (virtio_queue__should_signal(vq))
@@ -495,7 +497,8 @@ static u32 get_host_features(struct kvm *kvm, void *dev)
| 1UL << VIRTIO_RING_F_INDIRECT_DESC
| 1UL << VIRTIO_NET_F_CTRL_VQ
| 1UL << VIRTIO_NET_F_MRG_RXBUF
- | 1UL << (ndev->queue_pairs > 1 ? VIRTIO_NET_F_MQ : 0);
+ | 1UL << (ndev->queue_pairs > 1 ? VIRTIO_NET_F_MQ : 0)
+ | 1UL << VIRTIO_F_ANY_LAYOUT;
/*
* The UFO feature for host and guest only can be enabled when the
Modern virtio demands that devices do not make assumptions about the buffer layouts. Currently the user network backend assumes that TX packets are neatly split between virtio-net header and ethernet frame. Modern virtio-net usually puts everything into one descriptor, but could also split the buffer arbitrarily. Handle arbitrary buffer layouts and advertise the VIRTIO_F_ANY_LAYOUT feature. Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com> --- net/uip/core.c | 71 ++++++++++++++++++++++++++++++++------------------ virtio/net.c | 21 ++++++++------- 2 files changed, 57 insertions(+), 35 deletions(-)