| #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); |
| } |