QEMU is missing a good test for vhost-user feature, so I've created a sample vhost-user application, which called Vubr (mst coined the name, but better suggestions will be appreciated). Vubr may later serve the QEMU community as vhost-user QEMU internal test.
Essentially Vubr is a very basic vhost-user backend for QEMU, It runs as a separate user-level process. For packet processing Vubr uses an additional QEMU instance with a backend configured by "-net socket" as a shared VLAN. This way another QEMU virtual machine can effectively make a bus by means of UDP communication. For a more simple setup, the another QEMU instance running the SLiRP backed can be the same QEMU instance running vhost-user client. The Vubr implementation is very preliminary. It is missing many features. I has been studying vhost-user protocol internals, so I've wrote Vubr bit by bit as I progressed through the protocol. Most probably internal architecture will change significantly. To run Vubr application: Build vubr with: $ cd qemu/tests/vubr; make Ensure the machine has hugepages enabled in kernel with command line like: default_hugepagesz=2M hugepagesz=2M hugepages=2048 Run it with: $ ./vubr The above will run vhost-user server listening for connections on UNIX domain socket /tmp/vubr.sock, and will try to connect by UDP to VLAN bridge to localhost:5555, while listening on localhost:4444 Run qemu with a virtio-net backed by vhost-user: $ qemu \ -enable-kvm -m 512 -smp 2 \ -object memory-backend-file,id=mem,size=512M,mem-path=/dev/hugepages,share=on \ -numa node,memdev=mem -mem-prealloc \ -chardev socket,id=char0,path=/tmp/vubr.sock \ -netdev type=vhost-user,id=mynet1,chardev=char0,vhostforce \ -device virtio-net-pci,netdev=mynet1 \ -net none \ -net socket,vlan=0,udp=localhost:4444,localaddr=localhost:5555 \ -net user,vlan=0 \ disk.img Vubr tested very lightly: it's able to bringup a linux on client VM with virtio-net driver, and execute transmits and receives to the internet. I tested with "wget redhat.com", "dig redhat.com". PS. I've consulted DPDK's code for vhost-user during Vubr implementation. Signed-off-by: Victor Kaplansky <vict...@redhat.com> --- tests/vubr/dispatcher.h | 26 ++ tests/vubr/vhost.h | 77 +++++ tests/vubr/vhost_user.h | 70 +++++ tests/vubr/virtio_net.h | 38 +++ tests/vubr/virtio_ring.h | 103 +++++++ tests/vubr/virtqueue.h | 17 ++ tests/vubr/vubr_config.h | 7 + tests/vubr/vubr_device.h | 41 +++ tests/vubr/dispatcher.c | 77 +++++ tests/vubr/main.c | 18 ++ tests/vubr/vhost_user.c | 83 +++++ tests/vubr/vubr_device.c | 773 +++++++++++++++++++++++++++++++++++++++++++++++ tests/vubr/Makefile | 15 + 13 files changed, 1345 insertions(+) create mode 100644 tests/vubr/dispatcher.h create mode 100644 tests/vubr/vhost.h create mode 100644 tests/vubr/vhost_user.h create mode 100644 tests/vubr/virtio_net.h create mode 100644 tests/vubr/virtio_ring.h create mode 100644 tests/vubr/virtqueue.h create mode 100644 tests/vubr/vubr_config.h create mode 100644 tests/vubr/vubr_device.h create mode 100644 tests/vubr/dispatcher.c create mode 100644 tests/vubr/main.c create mode 100644 tests/vubr/vhost_user.c create mode 100644 tests/vubr/vubr_device.c create mode 100644 tests/vubr/Makefile diff --git a/tests/vubr/dispatcher.h b/tests/vubr/dispatcher.h new file mode 100644 index 0000000..cd02f07 --- /dev/null +++ b/tests/vubr/dispatcher.h @@ -0,0 +1,26 @@ +#ifndef __DISPATCHER__ +#define __DISPATCHER__ + +#include <stddef.h> +#include <stdint.h> +#include <sys/select.h> + +typedef void (*callback_func)(int sock, void *ctx); + +struct event { + void *ctx; + callback_func callback; +}; + +struct dispatcher { + int max_sock; + fd_set fdset; + struct event events[FD_SETSIZE]; +}; + +int dispatcher_init(struct dispatcher *d); +int dispatcher_add(struct dispatcher *d, int sock, void *ctx, callback_func cb); +int dispatcher_remove(struct dispatcher *d, int sock); +int dispatcher_wait(struct dispatcher *d, uint32_t timeout); + +#endif /* __DISPATCHER__ */ diff --git a/tests/vubr/vhost.h b/tests/vubr/vhost.h new file mode 100644 index 0000000..3960cc2 --- /dev/null +++ b/tests/vubr/vhost.h @@ -0,0 +1,77 @@ +#ifndef __VHOST_H__ +#define __VHOST_H__ + +#include <inttypes.h> + +/* Most imported form qemu/linux-headers/linux/vhost.h + * + * Userspace interface for virtio structures. */ + +struct vhost_vring_state { + unsigned int index; + unsigned int num; +}; + +struct vhost_vring_file { + unsigned int index; + int fd; /* Pass -1 to unbind from file. */ +}; + +struct vhost_vring_addr { + unsigned int index; + /* Option flags. */ + unsigned int flags; + /* Flag values: */ + /* Whether log address is valid. If set enables logging. */ +#define VHOST_VRING_F_LOG 0 + + /* Start of array of descriptors (virtually contiguous) */ + uint64_t desc_user_addr; + /* Used structure address. Must be 32 bit aligned */ + uint64_t used_user_addr; + /* Available structure address. Must be 16 bit aligned */ + uint64_t avail_user_addr; + /* Logging support. */ + /* Log writes to used structure, at offset calculated from specified + * address. Address must be 32 bit aligned. */ + uint64_t log_guest_addr; +}; + +#define VHOST_MEMORY_MAX_NREGIONS (8) + +struct vhost_memory_region { + uint64_t guest_phys_addr; + uint64_t memory_size; /* bytes */ + uint64_t userspace_addr; + uint64_t mmap_offset; +}; + +struct vhost_memory { + uint32_t nregions; + uint32_t padding; + struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS]; +}; + +/* Feature bits */ +/* Log all write descriptors. Can be changed while device is active. */ +#define VHOST_F_LOG_ALL 26 +/* vhost-net should add virtio_net_hdr for RX, and strip for TX packets. */ +#define VHOST_NET_F_VIRTIO_NET_HDR 27 + +struct virtio_net_hdr { +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 + uint8_t flags; +#define VIRTIO_NET_HDR_GSO_NONE 0 +#define VIRTIO_NET_HDR_GSO_TCPV4 1 +#define VIRTIO_NET_HDR_GSO_UDP 3 +#define VIRTIO_NET_HDR_GSO_TCPV6 4 +#define VIRTIO_NET_HDR_GSO_ECN 0x80 + uint8_t gso_type; + uint16_t hdr_len; + uint16_t gso_size; + uint16_t csum_start; + uint16_t csum_offset; + uint16_t num_buffers; +}; + +#endif /* __VHOST_H__ */ diff --git a/tests/vubr/vhost_user.h b/tests/vubr/vhost_user.h new file mode 100644 index 0000000..44d82d0 --- /dev/null +++ b/tests/vubr/vhost_user.h @@ -0,0 +1,70 @@ +#ifndef __VHOST_USER_H__ +#define __VHOST_USER_H__ + +/* Based on qemu/hw/virtio/vhost-user.c */ + +#include <stdint.h> +#include <stddef.h> +#include "vhost.h" + +#define VHOST_USER_F_PROTOCOL_FEATURES 30 +#define VHOST_USER_PROTOCOL_FEATURE_MASK 0x1ULL + +#define VHOST_USER_PROTOCOL_F_MQ 0 + +#define VHOST_USER_REQUEST_LIST \ + INFO(NONE, 0) \ + INFO(GET_FEATURES, 1) \ + INFO(SET_FEATURES, 2) \ + INFO(SET_OWNER, 3) \ + INFO(RESET_DEVICE, 4) \ + INFO(SET_MEM_TABLE, 5) \ + INFO(SET_LOG_BASE, 6) \ + INFO(SET_LOG_FD, 7) \ + INFO(SET_VRING_NUM, 8) \ + INFO(SET_VRING_ADDR, 9) \ + INFO(SET_VRING_BASE, 10) \ + INFO(GET_VRING_BASE, 11) \ + INFO(SET_VRING_KICK, 12) \ + INFO(SET_VRING_CALL, 13) \ + INFO(SET_VRING_ERR, 14) \ + INFO(GET_PROTOCOL_FEATURES, 15) \ + INFO(SET_PROTOCOL_FEATURES, 16) \ + INFO(GET_QUEUE_NUM, 17) \ + INFO(SET_VRING_ENABLE, 18) + +enum vhost_user_request { +#define INFO(a, b) VHOST_USER_ ## a = b, + VHOST_USER_REQUEST_LIST +#undef INFO + VHOST_USER_MAX +}; + +struct vhost_user_message { + enum vhost_user_request request; + +#define VHOST_USER_VERSION_MASK (0x3) +#define VHOST_USER_REPLY_MASK (0x1<<2) + uint32_t flags; + uint32_t size; /* the following payload size */ + union { +#define VHOST_USER_VRING_IDX_MASK (0xff) +#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) + uint64_t u64; + struct vhost_vring_state state; + struct vhost_vring_addr addr; + struct vhost_memory memory; + } payload; + int fds[VHOST_MEMORY_MAX_NREGIONS]; + int fd_num; +} __attribute__((packed)); + +#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_message, payload.u64) + +/* The version of the protocol we support */ +#define VHOST_USER_VERSION (0x1) + +void vhost_user_message_read(int conn_fd, struct vhost_user_message *vmsg); +void vhost_user_message_write(int conn_fd, struct vhost_user_message *vmsg); + +#endif /* __VHOST_USER_H__ */ diff --git a/tests/vubr/virtio_net.h b/tests/vubr/virtio_net.h new file mode 100644 index 0000000..f6f87b1 --- /dev/null +++ b/tests/vubr/virtio_net.h @@ -0,0 +1,38 @@ +#ifndef __VIRTIO_NET_H__ +#define __VIRTIO_NET_H__ + +/* Form qemu/include/standard-headers/linux/virtio_net.h */ + +/* The feature bitmap for virtio net */ +#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ +#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS 2 /* Dynamic offload configuration. */ +#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ +#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ +#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ +#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */ +#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */ +#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ +#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ +#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ +#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the + * network */ +#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow + * Steering */ +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ + +#ifndef VIRTIO_NET_NO_LEGACY +#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ +#endif /* VIRTIO_NET_NO_LEGACY */ + +#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ +#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ + +#endif /* __VIRTIO_NET_H__ */ diff --git a/tests/vubr/virtio_ring.h b/tests/vubr/virtio_ring.h new file mode 100644 index 0000000..e2d0adb --- /dev/null +++ b/tests/vubr/virtio_ring.h @@ -0,0 +1,103 @@ +#ifndef VIRTQUEUE_H +#define VIRTQUEUE_H +/* + * + * Virtual I/O Device (VIRTIO) Version 1.0 + * Committee Specification 03 + * 02 August 2015 + * Copyright (c) OASIS Open 2015. All Rights Reserved. + * Source: http://docs.oasis-open.org/virtio/virtio/v1.0/cs03/listings/ + * + */ +#include <stdint.h> + +typedef uint64_t le64; +typedef uint32_t le32; +typedef uint16_t le16; + +/* This marks a buffer as continuing via the next field. */ +#define VIRTQ_DESC_F_NEXT 1 +/* This marks a buffer as write-only (otherwise read-only). */ +#define VIRTQ_DESC_F_WRITE 2 +/* This means the buffer contains a list of buffer descriptors. */ +#define VIRTQ_DESC_F_INDIRECT 4 + +/* The device uses this in used->flags to advise the driver: don't kick me + * when you add a buffer. It's unreliable, so it's simply an + * optimization. */ +#define VIRTQ_USED_F_NO_NOTIFY 1 +/* The driver uses this in avail->flags to advise the device: don't + * interrupt me when you consume a buffer. It's unreliable, so it's + * simply an optimization. */ +#define VIRTQ_AVAIL_F_NO_INTERRUPT 1 + +/* Support for indirect descriptors */ +#define VIRTIO_F_INDIRECT_DESC 28 + +/* Support for avail_event and used_event fields */ +#define VIRTIO_F_EVENT_IDX 29 + +/* Arbitrary descriptor layouts. */ +#define VIRTIO_F_ANY_LAYOUT 27 + +/* Virtqueue descriptors: 16 bytes. + * These can chain together via "next". */ +struct virtq_desc { + /* Address (guest-physical). */ + le64 addr; + /* Length. */ + le32 len; + /* The flags as indicated above. */ + le16 flags; + /* We chain unused descriptors via this, too */ + le16 next; +}; + +struct virtq_avail { + le16 flags; + le16 idx; + le16 ring[]; + /* Only if VIRTIO_F_EVENT_IDX: le16 used_event; */ +}; + +/* le32 is used here for ids for padding reasons. */ +struct virtq_used_elem { + /* Index of start of used descriptor chain. */ + le32 id; + /* Total length of the descriptor chain which was written to. */ + le32 len; +}; + +struct virtq_used { + le16 flags; + le16 idx; + struct virtq_used_elem ring[]; + /* Only if VIRTIO_F_EVENT_IDX: le16 avail_event; */ +}; + +struct virtq { + unsigned int num; + + struct virtq_desc *desc; + struct virtq_avail *avail; + struct virtq_used *used; +}; + +static inline int virtq_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old_idx) +{ + return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old_idx); +} + +/* Get location of event indices (only with VIRTIO_F_EVENT_IDX) */ +static inline le16 *virtq_used_event(struct virtq *vq) +{ + /* For backwards compat, used event index is at *end* of avail ring. */ + return &vq->avail->ring[vq->num]; +} + +static inline le16 *virtq_avail_event(struct virtq *vq) +{ + /* For backwards compat, avail event index is at *end* of used ring. */ + return (le16 *)&vq->used->ring[vq->num]; +} +#endif /* VIRTQUEUE_H */ diff --git a/tests/vubr/virtqueue.h b/tests/vubr/virtqueue.h new file mode 100644 index 0000000..b018cca --- /dev/null +++ b/tests/vubr/virtqueue.h @@ -0,0 +1,17 @@ +#ifndef __VIRTQUEUE__ +#define __VIRTQUEUE__ + +#include "virtio_ring.h" + +struct virtqueue { + int call_fd; + int kick_fd; + uint32_t size; + uint16_t last_avail_index; + uint16_t last_used_index; + struct virtq_desc* desc; + struct virtq_avail* avail; + struct virtq_used* used; +}; + +#endif /* __VIRTQUEUE__ */ diff --git a/tests/vubr/vubr_config.h b/tests/vubr/vubr_config.h new file mode 100644 index 0000000..19681d0 --- /dev/null +++ b/tests/vubr/vubr_config.h @@ -0,0 +1,7 @@ +#ifndef __VHU_CONFIG__ +#define __VHU_CONFIG__ + +#define VHOST_USER_SHOW_MGMT_TRAFFIC +#define VHOST_USER_SHOW_NET_TRAFFIC + +#endif /* __VHU_CONFIG__ */ diff --git a/tests/vubr/vubr_device.h b/tests/vubr/vubr_device.h new file mode 100644 index 0000000..04a0ecb --- /dev/null +++ b/tests/vubr/vubr_device.h @@ -0,0 +1,41 @@ +#ifndef __VHU_DEVICE__ +#define __VHU_DEVICE__ + +#include <arpa/inet.h> +#include <sys/socket.h> + +#include "vhost.h" +#include "virtqueue.h" +#include "dispatcher.h" + +#define MAX_NR_VIRTQUEUE (8) + +struct vubr_device_region { + /* Guest Phhysical address. */ + uint64_t gpa; + /* Memory region size. */ + uint64_t size; + /* QEMU virtual address (userspace). */ + uint64_t qva; + /* Starting offset in our mmaped space. */ + uint64_t mmap_offset; + /* Start addrtess of mmaped space. */ + uint64_t mmap_addr; +}; + +struct vubr_device { + int sock; + struct dispatcher dispatcher; + uint32_t nregions; + struct vubr_device_region regions[VHOST_MEMORY_MAX_NREGIONS]; + struct virtqueue virtqueue[MAX_NR_VIRTQUEUE]; + int backend_udp_sock; + struct sockaddr_in backend_udp_dest; +}; + +struct vubr_device *vubr_device_new(char *path); +void vubr_device_run(struct vubr_device * dev); +void vubr_device_backend_udp_setup(struct vubr_device *dev, char *local_host, + uint16_t local_port, char *dest_host, uint16_t dest_port); + +#endif /* __VHU_DEVICE__ */ diff --git a/tests/vubr/dispatcher.c b/tests/vubr/dispatcher.c new file mode 100644 index 0000000..62d386a --- /dev/null +++ b/tests/vubr/dispatcher.c @@ -0,0 +1,77 @@ +#include <stdio.h> +#include <sys/select.h> + +#include "vubr_config.h" +#include "dispatcher.h" + +int +dispatcher_init(struct dispatcher *d) +{ + FD_ZERO(&d->fdset); + d->max_sock = -1; + return 0; +} + +int +dispatcher_add(struct dispatcher *d, int sock, void *ctx, callback_func cb) +{ + if (sock >= FD_SETSIZE) { + fprintf(stderr, "Error: Failed to add new event. sock %d should be less than %d\n", + sock, FD_SETSIZE); + return -1; + } + + d->events[sock].ctx = ctx; + d->events[sock].callback = cb; + + FD_SET(sock, &d->fdset); + if (sock > d->max_sock) + d->max_sock = sock; + printf("DEBUG: Added sock %d for watching. max_sock: %d\n", sock, d->max_sock); + return 0; +} + +int +dispatcher_remove(struct dispatcher *d, int sock) +{ + if (sock >= FD_SETSIZE) { + fprintf(stderr, "Error: Failed to remove event. sock %d should be less than %d\n", + sock, FD_SETSIZE); + return -1; + } + + FD_CLR(sock, &d->fdset); + return 0; +} + +/* timeout in us */ +int +dispatcher_wait(struct dispatcher *d, uint32_t timeout) +{ + struct timeval tv; + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + + fd_set fdset = d->fdset; + + /* wait until some of sockets become readable. */ + int rc = select(d->max_sock + 1, &fdset, 0, 0, &tv); + + if (rc == -1) + perror("select"); + + /* Timeout */ + if (rc == 0) + return 0; + + /* Now call callback for every ready socket. */ + + int sock; + for (sock = 0; sock < d->max_sock + 1; sock++) + if (FD_ISSET(sock, &fdset)) { + struct event *e = &d->events[sock]; + e->callback(sock, e->ctx); + } + + return 0; +} diff --git a/tests/vubr/main.c b/tests/vubr/main.c new file mode 100644 index 0000000..a7a3e9d --- /dev/null +++ b/tests/vubr/main.c @@ -0,0 +1,18 @@ +#include "vubr_config.h" +#include "vubr_device.h" + +int main(int argc, char* argv[]) +{ + struct vubr_device *dev; + + if((dev = vubr_device_new("/tmp/vubr.sock"))) { + vubr_device_backend_udp_setup(dev, + "127.0.0.1", 4444, + "127.0.0.1", 5555); + + vubr_device_run(dev); + return 0; + } + else + return 1; +} diff --git a/tests/vubr/vhost_user.c b/tests/vubr/vhost_user.c new file mode 100644 index 0000000..91ab09e --- /dev/null +++ b/tests/vubr/vhost_user.c @@ -0,0 +1,83 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <unistd.h> +#include <errno.h> + +#include "vubr_config.h" +#include "vhost_user.h" + +void +vhost_user_message_read(int conn_fd, struct vhost_user_message *vmsg) +{ + int rc; + struct msghdr msg = {}; + struct iovec iov; + size_t fd_size = VHOST_MEMORY_MAX_NREGIONS * sizeof(int); + char control[CMSG_SPACE(fd_size)]; + memset(control, 0, sizeof(control)); + + iov.iov_base = (char *)vmsg; + iov.iov_len = VHOST_USER_HDR_SIZE; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + rc = recvmsg(conn_fd, &msg, 0); + + if (rc <= 0) { + perror("recvmsg"); + exit(1); + } + + vmsg->fd_num = 0; + struct cmsghdr *cmsg; + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if ((cmsg->cmsg_level == SOL_SOCKET) && + (cmsg->cmsg_type == SCM_RIGHTS)) + { + fd_size = cmsg->cmsg_len - CMSG_LEN(0); + vmsg->fd_num = fd_size / sizeof(int); + memcpy(vmsg->fds, CMSG_DATA(cmsg), fd_size); + break; + } + } + + if (vmsg->size > sizeof(vmsg->payload)) { + fprintf(stderr, "Error: too big message request: %d, size: vmsg->size: %u, while sizeof(vmsg->payload) = %lu\n", + vmsg->request, vmsg->size, sizeof(vmsg->payload)); + exit(1); + } + + if (vmsg->size) { + rc = read(conn_fd, &vmsg->payload, vmsg->size); + if (rc <= 0) { + perror("recvmsg"); + exit(1); + } + + assert(rc == vmsg->size); + } +} + +void +vhost_user_message_write(int conn_fd, struct vhost_user_message *vmsg) +{ + int rc; + do { + rc = write(conn_fd, vmsg, VHOST_USER_HDR_SIZE + vmsg->size); + } while (rc < 0 && errno == EINTR); + + if (rc < 0) { + perror("write"); + exit(1); + } +} diff --git a/tests/vubr/vubr_device.c b/tests/vubr/vubr_device.c new file mode 100644 index 0000000..a296aef --- /dev/null +++ b/tests/vubr/vubr_device.c @@ -0,0 +1,773 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/unistd.h> +#include <sys/mman.h> +#include <sys/eventfd.h> + +#include "vubr_config.h" +#include "vhost_user.h" +#include "virtio_net.h" +#include "vubr_device.h" +#include "virtqueue.h" + +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC +static char *vhost_user_request_str[] = { +#define INFO(name,num) \ + [num] = #name, +VHOST_USER_REQUEST_LIST +#undef INFO +}; +#endif /* VHOST_USER_SHOW_MGMT_TRAFFIC */ + +void +die(char *s) +{ + perror(s); + exit(1); +} + +static void +print_buffer(uint8_t* buf, size_t len) +{ + int i; + printf("raw buffer:\n"); + for(i = 0; i < len; i++) { + if ((i % 16) == 0) + printf("\n"); + if ((i % 4) == 0) + printf(" "); + printf("%02x ", buf[i]); + } + printf("\n............................................................\n"); +} + +/* Translate guest physical address to our virtual address. */ +static uint64_t __attribute__((unused)) +gpa_to_va(struct vubr_device *dev, uint64_t guest_addr) +{ + int i; + /* Find matching memory region. */ + + for (i = 0; i < dev->nregions; i++) { + struct vubr_device_region *r = &dev->regions[i]; + + if ((guest_addr >= r->gpa) && (guest_addr < (r->gpa + r->size))) + return (guest_addr - r->gpa + r->mmap_addr + r->mmap_offset); + } + + assert(!"address not found in regions"); + return 0; +} + +/* Translate qemu virtual address to our virtual address. */ +static uint64_t +qva_to_va(struct vubr_device *dev, uint64_t qemu_addr) +{ + int i; + /* Find matching memory region. */ + + for (i = 0; i < dev->nregions; i++) { + struct vubr_device_region *r = &dev->regions[i]; + + if ((qemu_addr >= r->qva) && (qemu_addr < (r->qva + r->size))) + return (qemu_addr - r->qva + r->mmap_addr + r->mmap_offset); + } + + assert(!"address not found in regions"); + return 0; +} + +static void vubr_device_backend_udp_sendbuf(struct vubr_device *dev, uint8_t *buf, size_t len); + +static void +_consume_raw_packet(struct vubr_device *dev, uint8_t *buf, uint32_t len) +{ + int hdrlen = sizeof(struct virtio_net_hdr); + +#ifdef VHOST_USER_SHOW_NET_TRAFFIC + print_buffer(buf, len); +#endif + vubr_device_backend_udp_sendbuf(dev, buf + hdrlen, len - hdrlen); +} + +/* Kick the guest if necessary. */ +static void +virtqueue_kick(struct virtqueue *vq) +{ + if (!(vq->avail->flags & VIRTQ_AVAIL_F_NO_INTERRUPT)) { + printf("Kicking the guest...\n"); + eventfd_write(vq->call_fd, 1); + } +} + +static void +_post_buffer(struct vubr_device *dev, struct virtqueue *vq, uint8_t *buf, int32_t len) +{ + struct virtq_desc* desc = vq->desc; + struct virtq_avail* avail = vq->avail; + struct virtq_used* used = vq->used; + + unsigned int size = vq->size; + + uint16_t a_index = vq->last_avail_index % size; + uint16_t u_index = vq->last_used_index % size; + uint16_t d_index = avail->ring[a_index]; + + int i = d_index; + +#ifdef VHOST_USER_SHOW_NET_TRAFFIC + printf("Posting the packet to guest on vq:\n"); + printf(" size = %d\n", vq->size); + printf(" last_avail_index = %d\n", vq->last_avail_index); + printf(" last_used_index = %d\n", vq->last_used_index); + printf(" a_index = %d\n", a_index); + printf(" u_index = %d\n", u_index); + printf(" d_index = %d\n", d_index); + printf(" desc[%d].addr = 0x%016"PRIx64"\n", i, desc[i].addr); + printf(" desc[%d].len = %d\n", i, desc[i].len); + printf(" desc[%d].flags = %d\n", i, desc[i].flags); + printf(" avail->idx = %d\n", avail->idx); + printf(" used->idx = %d\n", used->idx); +#endif + + if (!(desc[i].flags & VIRTQ_DESC_F_WRITE)) { + // FIXME: we should find writable descriptor + fprintf(stderr, "descriptor is not writable. exiting.\n"); + exit(1); + } + + void *chunk_start = (void *)gpa_to_va(dev, desc[i].addr); + uint32_t chunk_len = desc[i].len; + + if (len <= chunk_len) { + memcpy(chunk_start, buf, len); + } else { + fprintf(stderr, "received too long packet from the backend. dropping...\n"); + return; + } + + /* Add descriptor to the used ring. */ + used->ring[u_index].id = d_index; + used->ring[u_index].len = len; + + vq->last_avail_index++; + vq->last_used_index++; + + used->idx = vq->last_used_index; + + /* Kick the guest if necessary. */ + virtqueue_kick(vq); +} + +static int +_process_desc(struct vubr_device *dev, struct virtqueue *vq) +{ + struct virtq_desc* desc = vq->desc; + struct virtq_avail* avail = vq->avail; + struct virtq_used* used = vq->used; + + unsigned int size = vq->size; + + uint16_t a_index = vq->last_avail_index % size; + uint16_t u_index = vq->last_used_index % size; + uint16_t d_index = avail->ring[a_index]; + + uint32_t i, len = 0; + size_t buf_size = 4096; + uint8_t buf[4096]; + +#ifdef VHOST_USER_SHOW_NET_TRAFFIC + printf("chunks: "); +#endif + + i = d_index; + do { + void *chunk_start = (void *)gpa_to_va(dev, desc[i].addr); + uint32_t chunk_len = desc[i].len; + + if (len + chunk_len < buf_size) { + memcpy(buf + len, chunk_start, chunk_len); +#ifdef VHOST_USER_SHOW_NET_TRAFFIC + printf("%d ", chunk_len); +#endif + } else { + fprintf(stderr, "too long packet. dropping...\n"); + break; + } + + len += chunk_len; + + if (!(desc[i].flags & VIRTQ_DESC_F_NEXT)) + break; + + i = desc[i].next; + } while(1); + + if (!len) + return -1; + + /* Add descriptor to the used ring. */ + used->ring[u_index].id = d_index; + used->ring[u_index].len = len; + +#ifdef VHOST_USER_SHOW_NET_TRAFFIC + printf("\n"); +#endif + + _consume_raw_packet(dev, buf, len); + + return 0; +} + +static void +_process_avail(struct vubr_device *dev, struct virtqueue *vq) +{ + struct virtq_avail *avail = vq->avail; + struct virtq_used* used = vq->used; + + while(vq->last_avail_index != avail->idx) { + _process_desc(dev, vq); + vq->last_avail_index++; + vq->last_used_index++; + } + + used->idx = vq->last_used_index; +} + +static int vubr_device_backend_udp_recvbuf(struct vubr_device *dev, uint8_t *buf, size_t buflen); + +static void +_backend_recv_cb(int sock, void *ctx) +{ + printf("\n\n *** IN UDP RECEIVE CALLBACK ***\n\n"); + struct vubr_device *dev = (struct vubr_device *) ctx; + struct virtqueue *rx_vq = &dev->virtqueue[0]; +#define BUFLEN 4096 + uint8_t buf[BUFLEN]; + int len; + struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)buf; + int hdrlen = sizeof(struct virtio_net_hdr); + + *hdr = (struct virtio_net_hdr) {}; + hdr->num_buffers = 1; + + len = vubr_device_backend_udp_recvbuf(dev, buf + hdrlen, BUFLEN - hdrlen); + _post_buffer(dev, rx_vq, buf, len + hdrlen); +#undef BUFLEN +} + +static void +_kick_cb(int sock, void *ctx) +{ + struct vubr_device *dev = (struct vubr_device *) ctx; + eventfd_t kick_data; + ssize_t rc; + + rc = eventfd_read(sock, &kick_data); + + if (rc == -1) { + perror("read kick"); + exit(1); + } else { +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("Got kick_data: %016"PRIx64"\n", kick_data); +#endif + _process_avail(dev, &dev->virtqueue[1]); + } +} + +static int +_execute_NONE(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("function %s() not implemented yet.\n", __FUNCTION__); + return 0; +} + +static int +_execute_GET_FEATURES(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + vmsg->payload.u64 = + ((1ULL << VIRTIO_NET_F_MRG_RXBUF) | + (1ULL << VIRTIO_NET_F_CTRL_VQ) | + (1ULL << VIRTIO_NET_F_CTRL_RX) | + (1ULL << VHOST_F_LOG_ALL)); + vmsg->size = sizeof(vmsg->payload.u64); + +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("returing u64: 0x%016"PRIx64"\n", vmsg->payload.u64); +#endif + + /* reply */ + return 1; +} + +static int +_execute_SET_FEATURES(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); +#endif + return 0; +} + +static int +_execute_SET_OWNER(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("function %s() not implemented yet.\n", __FUNCTION__); + return 0; +} + +static int +_execute_RESET_DEVICE(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("function %s() not implemented yet.\n", __FUNCTION__); + return 0; +} + +static int +_execute_SET_MEM_TABLE(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("Nregions: %d\n", vmsg->payload.memory.nregions); + + struct vhost_memory *memory = &vmsg->payload.memory; + dev->nregions = memory->nregions; + int i; + for (i = 0; i < dev->nregions; i++) { + struct vhost_memory_region *msg_region = &memory->regions[i]; + struct vubr_device_region *dev_region = &dev->regions[i]; + +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("Region %d\n", i); + printf(" guest_phys_addr: 0x%016"PRIx64"\n", msg_region->guest_phys_addr); + printf(" memory_size: 0x%016"PRIx64"\n", msg_region->memory_size); + printf(" userspace_addr 0x%016"PRIx64"\n", msg_region->userspace_addr); + printf(" mmap_offset 0x%016"PRIx64"\n", msg_region->mmap_offset); +#endif + + dev_region->gpa = msg_region->guest_phys_addr; + dev_region->size = msg_region->memory_size; + dev_region->qva = msg_region->userspace_addr; + dev_region->mmap_offset = msg_region->mmap_offset; + + void *mmap_addr; + + /* We don't use offset argument of mmap() since the + * mapped address has to be page aligned, and we use huge + * pages. */ + mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, + PROT_READ | PROT_WRITE, MAP_SHARED, + vmsg->fds[i], 0); + + if (mmap_addr == MAP_FAILED) { + perror("mmap"); + exit (1); + } + + dev_region->mmap_addr = (uint64_t) mmap_addr; + +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf(" mmap_addr: 0x%016"PRIx64"\n", dev_region->mmap_addr); +#endif + } + + return 0; +} + +static int +_execute_SET_LOG_BASE(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("function %s() not implemented yet.\n", __FUNCTION__); + return 0; +} + +static int +_execute_SET_LOG_FD(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("function %s() not implemented yet.\n", __FUNCTION__); + return 0; +} + +static int +_execute_SET_VRING_NUM(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + unsigned int index = vmsg->payload.state.index; + unsigned int num = vmsg->payload.state.num; + +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("state.index: %d\n", index); + printf("state.num: %d\n", num); +#endif + dev->virtqueue[index].size = num; + return 0; +} + +static int +_execute_SET_VRING_ADDR(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + struct vhost_vring_addr *vra = &vmsg->payload.addr; +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("vhost_vring_addr:\n"); + printf(" index: %d\n", vra->index); + printf(" flags: %d\n", vra->flags); + printf(" desc_user_addr: 0x%016"PRIx64"\n", vra->desc_user_addr); + printf(" used_user_addr: 0x%016"PRIx64"\n", vra->used_user_addr); + printf(" avail_user_addr: 0x%016"PRIx64"\n", vra->avail_user_addr); + printf(" log_guest_addr: 0x%016"PRIx64"\n", vra->log_guest_addr); +#endif + + unsigned int index = vra->index; + struct virtqueue *vq = &dev->virtqueue[index]; + + vq->desc = (struct virtq_desc *)qva_to_va(dev, vra->desc_user_addr); + vq->used = (struct virtq_used *)qva_to_va(dev, vra->used_user_addr); + vq->avail = (struct virtq_avail *)qva_to_va(dev, vra->avail_user_addr); + + printf("Setting virtq addresses:\n"); + printf(" virtq_desc at %p\n", vq->desc); + printf(" virtq_used at %p\n", vq->used); + printf(" virtq_avail at %p\n", vq->avail); + + vq->last_used_index = vq->used->idx; + return 0; +} + +static int +_execute_SET_VRING_BASE(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + unsigned int index = vmsg->payload.state.index; + unsigned int num = vmsg->payload.state.num; + +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("state.index: %d\n", index); + printf("state.num: %d\n", num); +#endif + dev->virtqueue[index].last_avail_index = num; + + return 0; +} + +static int +_execute_GET_VRING_BASE(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("function %s() not implemented yet.\n", __FUNCTION__); + return 0; +} + +static int +_execute_SET_VRING_KICK(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); +#endif + + uint64_t u64_arg = vmsg->payload.u64; + int index = u64_arg & VHOST_USER_VRING_IDX_MASK; + + assert((u64_arg & VHOST_USER_VRING_NOFD_MASK) == 0); + assert(vmsg->fd_num == 1); + + dev->virtqueue[index].kick_fd = vmsg->fds[0]; + printf("Got kick_fd: %d for vq: %d\n", vmsg->fds[0], index); + + if ((index % 2 == 1)) { + /* TX queue. */ + dispatcher_add(&dev->dispatcher, dev->virtqueue[index].kick_fd, dev, _kick_cb); + +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("Waiting for kicks on fd: %d for vq: %d\n", + dev->virtqueue[index].kick_fd, index); +#endif + } + return 0; +} + +static int +_execute_SET_VRING_CALL(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); +#endif + + uint64_t u64_arg = vmsg->payload.u64; + int index = u64_arg & VHOST_USER_VRING_IDX_MASK; + + assert((u64_arg & VHOST_USER_VRING_NOFD_MASK) == 0); + assert(vmsg->fd_num == 1); + + dev->virtqueue[index].call_fd = vmsg->fds[0]; + printf("Got call_fd: %d for vq: %d\n", vmsg->fds[0], index); + + return 0; +} + +static int +_execute_SET_VRING_ERR(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); +#endif + return 0; +} + +static int +_execute_GET_PROTOCOL_FEATURES(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + /* FIXME: unimplented */ +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); +#endif + return 0; +} + +static int +_execute_SET_PROTOCOL_FEATURES(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + /* FIXME: unimplented */ +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + printf("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); +#endif + return 0; +} + +static int +_execute_GET_QUEUE_NUM(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("function %s() not implemented yet.\n", __FUNCTION__); + return 0; +} + +static int +_execute_SET_VRING_ENABLE(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ + printf("function %s() not implemented yet.\n", __FUNCTION__); + return 0; +} + +static int +vubr_device_execute_request(struct vubr_device *dev, struct vhost_user_message *vmsg) +{ +#ifdef VHOST_USER_SHOW_MGMT_TRAFFIC + /* Print out generic part of the request. */ + printf("======================= Vhost user message from QEMU =======================\n"); + printf("Request: %s (%d)\n", vhost_user_request_str[vmsg->request], vmsg->request); + printf("Flags: 0x%x\n", vmsg->flags); + printf("Size: %d\n", vmsg->size); + + if (vmsg->fd_num) { + int i; + printf("Fds:"); + for (i = 0; i < vmsg->fd_num; i++) + printf(" %d", vmsg->fds[i]); + printf("\n"); + } +#endif /* VHOST_USER_SHOW_MGMT_TRAFFIC */ + + switch (vmsg->request) { + case VHOST_USER_NONE: + return _execute_NONE(dev, vmsg); + case VHOST_USER_GET_FEATURES: + return _execute_GET_FEATURES(dev, vmsg); + case VHOST_USER_SET_FEATURES: + return _execute_SET_FEATURES(dev, vmsg); + case VHOST_USER_SET_OWNER: + return _execute_SET_OWNER(dev, vmsg); + case VHOST_USER_RESET_DEVICE: + return _execute_RESET_DEVICE(dev, vmsg); + case VHOST_USER_SET_MEM_TABLE: + return _execute_SET_MEM_TABLE(dev, vmsg); + case VHOST_USER_SET_LOG_BASE: + return _execute_SET_LOG_BASE(dev, vmsg); + case VHOST_USER_SET_LOG_FD: + return _execute_SET_LOG_FD(dev, vmsg); + case VHOST_USER_SET_VRING_NUM: + return _execute_SET_VRING_NUM(dev, vmsg); + case VHOST_USER_SET_VRING_ADDR: + return _execute_SET_VRING_ADDR(dev, vmsg); + case VHOST_USER_SET_VRING_BASE: + return _execute_SET_VRING_BASE(dev, vmsg); + case VHOST_USER_GET_VRING_BASE: + return _execute_GET_VRING_BASE(dev, vmsg); + case VHOST_USER_SET_VRING_KICK: + return _execute_SET_VRING_KICK(dev, vmsg); + case VHOST_USER_SET_VRING_CALL: + return _execute_SET_VRING_CALL(dev, vmsg); + case VHOST_USER_SET_VRING_ERR: + return _execute_SET_VRING_ERR(dev, vmsg); + case VHOST_USER_GET_PROTOCOL_FEATURES: + return _execute_GET_PROTOCOL_FEATURES(dev, vmsg); + case VHOST_USER_SET_PROTOCOL_FEATURES: + return _execute_SET_PROTOCOL_FEATURES(dev, vmsg); + case VHOST_USER_GET_QUEUE_NUM: + return _execute_GET_QUEUE_NUM(dev, vmsg); + case VHOST_USER_SET_VRING_ENABLE: + return _execute_SET_VRING_ENABLE(dev, vmsg); + case VHOST_USER_MAX: + assert(vmsg->request != VHOST_USER_MAX); + } + return 0; +} + +static void +vubr_device_receive_cb(int sock, void *ctx) +{ + struct vubr_device *dev = (struct vubr_device *) ctx; + struct vhost_user_message vmsg; + + vhost_user_message_read(sock, &vmsg); + + int reply_requested = vubr_device_execute_request(dev, &vmsg); + + if (reply_requested) { + /* Set the version in the flags when sending the reply */ + vmsg.flags &= ~VHOST_USER_VERSION_MASK; + vmsg.flags |= VHOST_USER_VERSION; + vmsg.flags |= VHOST_USER_REPLY_MASK; + vhost_user_message_write(sock, &vmsg); + } +} + +static void +vubr_device_accept_cb(int sock, void *ctx) +{ + struct vubr_device *dev = (struct vubr_device *)ctx; + int conn_fd; + struct sockaddr_un un; + socklen_t len = sizeof(un); + + if ((conn_fd = accept(sock, (struct sockaddr *) &un, &len)) == -1) { + perror("accept"); + exit(1); + } + + printf("DEBUG: Got connection from remote peer on sock %d\n", conn_fd); + dispatcher_add(&dev->dispatcher, conn_fd, ctx, vubr_device_receive_cb); +} + +struct vubr_device * +vubr_device_new(char *path) +{ + struct vubr_device *dev = + (struct vubr_device *) calloc(1, sizeof(struct vubr_device)); + + dev->nregions = 0; + + int i; + for (i = 0; i < MAX_NR_VIRTQUEUE; i++) + dev->virtqueue[i] = (struct virtqueue) { + .call_fd = -1, .kick_fd = -1, + .size = 0, + .last_avail_index = 0, .last_used_index = 0, + .desc = 0, .avail = 0, .used = 0, + }; + + /* Get a UNIX socket. */ + if ((dev->sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket"); + exit(1); + } + + struct sockaddr_un un; + un.sun_family = AF_UNIX; + strcpy(un.sun_path, path); + + size_t len = sizeof(un.sun_family) + strlen(path); + + unlink(path); + + if (bind(dev->sock, (struct sockaddr *) &un, len) == -1) { + perror("bind"); + exit(1); + } + + if (listen(dev->sock, 1) == -1) { + perror("listen"); + exit(1); + } + + dispatcher_init(&dev->dispatcher); + dispatcher_add(&dev->dispatcher, dev->sock, (void*) dev, vubr_device_accept_cb); + + printf("Waiting for connections on UNIX socket %s ...\n", path); + return dev; +} + +void +vubr_device_backend_udp_setup(struct vubr_device *dev, + char *local_host, + uint16_t local_port, + char *dest_host, + uint16_t dest_port) +{ + + struct sockaddr_in si_local; + int sock; + + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + die("socket"); + + memset((char *) &si_local, 0, sizeof(struct sockaddr_in)); + si_local.sin_family = AF_INET; + si_local.sin_port = htons(local_port); + if(inet_aton(local_host, &si_local.sin_addr) == 0) { + fprintf(stderr, "inet_aton() failed.\n"); + exit(1); + } + + if( bind(sock, (struct sockaddr*)&si_local, sizeof(si_local) ) == -1) + die("bind"); + + /* setup destination for sends */ + struct sockaddr_in *si_remote = &dev->backend_udp_dest; + memset((char *) si_remote, 0, sizeof(struct sockaddr_in)); + si_remote->sin_family = AF_INET; + si_remote->sin_port = htons(dest_port); + if(inet_aton(dest_host, &si_remote->sin_addr) == 0) { + fprintf(stderr, "inet_aton() failed.\n"); + exit(1); + } + + dev->backend_udp_sock = sock; + dispatcher_add(&dev->dispatcher, sock, dev, _backend_recv_cb); + printf("Waiting for data from udp backend on %s:%d...\n", local_host, local_port); +} + +static void +vubr_device_backend_udp_sendbuf(struct vubr_device *dev, uint8_t *buf, size_t len) +{ + int slen = sizeof(struct sockaddr_in); + + if (sendto(dev->backend_udp_sock, buf, len, 0, (struct sockaddr *) &dev->backend_udp_dest, slen) == -1) + die("sendto()"); +} + +static int +vubr_device_backend_udp_recvbuf(struct vubr_device *dev, uint8_t *buf, size_t buflen) +{ + int slen = sizeof(struct sockaddr_in); + int rc; + + if ((rc = recvfrom(dev->backend_udp_sock, buf, buflen, 0, + (struct sockaddr *) &dev->backend_udp_dest, + (socklen_t *)&slen)) == -1) + die("recvfrom()"); + + return rc; +} + +void +vubr_device_run(struct vubr_device * dev) +{ + while (1) { + /* timeout 200ms */ + dispatcher_wait(&dev->dispatcher, 200000); + /* Here one can try polling strategy. */ + } +} diff --git a/tests/vubr/Makefile b/tests/vubr/Makefile new file mode 100644 index 0000000..c3400fb --- /dev/null +++ b/tests/vubr/Makefile @@ -0,0 +1,15 @@ +SRCS=dispatcher.c vhost_user.c vubr_device.c main.c +INCLUDES+=vhost.h virtio_ring.h virtio_net.h +INCLUDES+=vubr_config.h vhost_user.h virtqueue.h +INCLUDES+=dispatcher.h vubr_device.h + +EXE=vubr +CFLAGS += -m64 -Wall -Werror -g + +all: $(EXE) + +$(EXE): $(SRCS) $(INCLUDES) + $(CC) $(CFLAGS) $(SRCS) -o $@ + +clean: + rm -f $(EXE) -- --Victor