blob: e409512ef74566b16d3ae204c3bcb3809af9b61f [file] [log] [blame]
#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;
ssize_t len;
struct uip_tx_arg arg;
size_t eth_len, vnet_len;
struct uip_eth *eth;
void *vnet_buf = NULL;
void *eth_buf = NULL;
size_t iovcount = out;
u16 proto;
/*
* Buffer from guest to device
*/
vnet_len = info->vnet_hdr_len;
vnet = iov[0].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;
len = memcpy_fromiovec_safe(vnet_buf, &iov, len, &iovcount);
if (len)
goto out_free_buf;
len = eth_len = iov_size(iov, iovcount);
eth = eth_buf = malloc(len);
if (!eth)
goto out_free_buf;
len = memcpy_fromiovec_safe(eth_buf, &iov, len, &iovcount);
if (len)
goto out_free_buf;
}
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;
}
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)
{
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;
}
void uip_exit(struct uip_info *info)
{
struct uip_buf *buf, *next;
uip_udp_exit(info);
uip_tcp_exit(info);
uip_dhcp_exit(info);
list_for_each_entry_safe(buf, next, &info->buf_head, list) {
free(buf->vnet);
free(buf->eth);
list_del(&buf->list);
free(buf);
}
uip_static_init(info);
}