mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-06-15 18:05:49 +00:00
6d6cc14b76
Currently, if a ->tx or ->rx callback into the net_dev_operations encounters an error, it returns -1 to the virtio-net code, which in turn treats this as an unsigned (size_t) size describing the data available. The resulting memcpy operation then quickly explodes with a SEGV. This patch detects the error code from the low-level callbacks and exits the thread dealing with the erroneous queue. Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Pekka Enberg <penberg@kernel.org>
157 lines
2.9 KiB
C
157 lines
2.9 KiB
C
#include "kvm/mutex.h"
|
|
#include "kvm/uip.h"
|
|
|
|
#include <linux/virtio_net.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/list.h>
|
|
#include <kvm/iovec.h>
|
|
|
|
int uip_tx(struct iovec *iov, u16 out, struct uip_info *info)
|
|
{
|
|
void *vnet;
|
|
struct uip_tx_arg arg;
|
|
int eth_len, vnet_len;
|
|
struct uip_eth *eth;
|
|
u8 *buf = NULL;
|
|
u16 proto;
|
|
int i;
|
|
|
|
/*
|
|
* Buffer from guest to device
|
|
*/
|
|
vnet_len = iov[0].iov_len;
|
|
vnet = iov[0].iov_base;
|
|
|
|
eth_len = iov[1].iov_len;
|
|
eth = iov[1].iov_base;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
buf = malloc(eth_len);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
memset(&arg, 0, sizeof(arg));
|
|
|
|
arg.vnet_len = vnet_len;
|
|
arg.eth_len = eth_len;
|
|
arg.info = info;
|
|
arg.vnet = vnet;
|
|
arg.eth = eth;
|
|
|
|
/*
|
|
* Check package type
|
|
*/
|
|
proto = ntohs(eth->type);
|
|
|
|
switch (proto) {
|
|
case UIP_ETH_P_ARP:
|
|
uip_tx_do_arp(&arg);
|
|
break;
|
|
case UIP_ETH_P_IP:
|
|
uip_tx_do_ipv4(&arg);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (out > 2 && buf)
|
|
free(eth);
|
|
|
|
return vnet_len + eth_len;
|
|
}
|
|
|
|
int uip_rx(struct iovec *iov, u16 in, struct uip_info *info)
|
|
{
|
|
struct uip_buf *buf;
|
|
int len;
|
|
|
|
/*
|
|
* Sleep until there is a buffer for guest
|
|
*/
|
|
buf = uip_buf_get_used(info);
|
|
|
|
memcpy_toiovecend(iov, buf->vnet, 0, buf->vnet_len);
|
|
memcpy_toiovecend(iov, buf->eth, buf->vnet_len, buf->eth_len);
|
|
|
|
len = buf->vnet_len + buf->eth_len;
|
|
|
|
uip_buf_set_free(info, buf);
|
|
return len;
|
|
}
|
|
|
|
void uip_static_init(struct uip_info *info)
|
|
{
|
|
struct list_head *udp_socket_head;
|
|
struct list_head *tcp_socket_head;
|
|
struct list_head *buf_head;
|
|
|
|
udp_socket_head = &info->udp_socket_head;
|
|
tcp_socket_head = &info->tcp_socket_head;
|
|
buf_head = &info->buf_head;
|
|
|
|
INIT_LIST_HEAD(udp_socket_head);
|
|
INIT_LIST_HEAD(tcp_socket_head);
|
|
INIT_LIST_HEAD(buf_head);
|
|
|
|
mutex_init(&info->udp_socket_lock);
|
|
mutex_init(&info->tcp_socket_lock);
|
|
mutex_init(&info->buf_lock);
|
|
|
|
pthread_cond_init(&info->buf_used_cond, NULL);
|
|
pthread_cond_init(&info->buf_free_cond, NULL);
|
|
|
|
info->buf_used_nr = 0;
|
|
}
|
|
|
|
int uip_init(struct uip_info *info)
|
|
{
|
|
struct list_head *buf_head;
|
|
struct uip_buf *buf;
|
|
int buf_nr;
|
|
int i;
|
|
|
|
buf_head = &info->buf_head;
|
|
buf_nr = info->buf_nr;
|
|
|
|
for (i = 0; i < buf_nr; i++) {
|
|
buf = malloc(sizeof(*buf));
|
|
memset(buf, 0, sizeof(*buf));
|
|
|
|
buf->status = UIP_BUF_STATUS_FREE;
|
|
buf->info = info;
|
|
buf->id = i;
|
|
list_add_tail(&buf->list, buf_head);
|
|
}
|
|
|
|
list_for_each_entry(buf, buf_head, list) {
|
|
buf->vnet_len = info->vnet_hdr_len;
|
|
buf->vnet = malloc(buf->vnet_len);
|
|
buf->eth_len = 1024*64 + sizeof(struct uip_pseudo_hdr);
|
|
buf->eth = malloc(buf->eth_len);
|
|
|
|
memset(buf->vnet, 0, buf->vnet_len);
|
|
memset(buf->eth, 0, buf->eth_len);
|
|
}
|
|
|
|
info->buf_free_nr = buf_nr;
|
|
|
|
uip_dhcp_get_dns(info);
|
|
|
|
return 0;
|
|
}
|