[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] Add vhost-user test application (Vubr)
From: |
Victor Kaplansky |
Subject: |
[Qemu-devel] [PATCH] Add vhost-user test application (Vubr) |
Date: |
Sun, 25 Oct 2015 19:42:00 +0200 |
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 <address@hidden>
---
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
- [Qemu-devel] [PATCH] Add vhost-user test application (Vubr),
Victor Kaplansky <=