blob: 977b9b0cb7f64fae2204b8cdaaedd48b3e435895 [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;
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;
}
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);
}