[PATCH 3/6] 9p virtio transport for QEMU (v2)

Anthony Liguori
2007-11-11T03:20:37Z

This patch implements a very simple virtio transport for 9p in QEMU. The
kernel side of this is going to change dramatically in the very near future so
this is just a demonstration for the moment. It requires an external 9p file
server and communicates with it over a TCP port. In future versions of this
patch, we'll link directly against a 9p file server library and expose the
ability to expose filesystems directly from the QEMU command line.

Signed-off-by: Anthony Liguori <aliguori-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>

diff --git a/qemu/Makefile.target b/qemu/Makefile.target
index 49c0fc7..fedf1e0 100644
--- a/qemu/Makefile.target
+++ b/qemu/Makefile.target
@@ -449,7 +449,7 @@ VL_OBJS += rtl8139.o
VL_OBJS+= hypercall.o

# virtio devices
-VL_OBJS += virtio.o virtio-blk.o
+VL_OBJS += virtio.o virtio-blk.o virtio-9p.o

ifeq ($(TARGET_BASE_ARCH), i386)
# Hardware support
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index c7483e8..fa0346b 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -951,6 +951,9 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size, int boot_device,
virtio_blk_init(pci_bus, 0x5002, 0x2258, bs);
}

+ if (1)
+ virtio_9p_init(pci_bus, 0x5002, 0x2258);
+
if (pci_enabled) {
pci_piix3_ide_init(pci_bus, bs_table, piix3_devfn + 1, i8259);
} else {
diff --git a/qemu/hw/virtio-9p.c b/qemu/hw/virtio-9p.c
new file mode 100644
index 0000000..c77b594
--- /dev/null
+++ b/qemu/hw/virtio-9p.c
@@ -0,0 +1,121 @@
+/*
+ * Virtio 9p backend
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "vl.h"
+#include "qemu_socket.h"
+#include "virtio.h"
+#include <sys/uio.h>
+#include <arpa/inet.h>
+
+/* from Linux's linux/virtio_9p.h */
+
+/* The ID for virtio console */
+#define VIRTIO_ID_9P 9
+/* Maximum number of virtio channels per partition (1 for now) */
+#define MAX_9P_CHAN 1
+
+typedef struct V9fsState
+{
+ VirtIODevice vdev;
+
+ int fd;
+ int listening;
+ VirtQueue *in_vq;
+} V9fsState;
+
+static V9fsState *to_v9fs_state(VirtIODevice *vdev)
+{
+ return (V9fsState *)vdev;
+}
+
+static void virtio_9p_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ V9fsState *s = to_v9fs_state(vdev);
+ VirtQueueElement elem;
+ ssize_t len;
+
+ while ((len = virtqueue_pop(vq, &elem)) != 0) {
+ len = writev(s->fd, elem.out_sg, elem.out_num);
+ if (len == -1 && (errno == EINTR || errno == EAGAIN))
+ continue;
+
+ virtqueue_push(vq, &elem, len);
+ virtio_notify(vdev, vq);
+ }
+}
+
+static void virtio_9p_kick_input(VirtIODevice *vdev, VirtQueue *vq)
+{
+ V9fsState *s = to_v9fs_state(vdev);
+ s->listening = 1;
+}
+
+static int virtio_9p_can_read(void *opaque)
+{
+ V9fsState *s = opaque;
+ return s->listening;
+}
+
+static void virtio_9p_handle_input(void *opaque)
+{
+ V9fsState *s = opaque;
+ VirtQueueElement elem;
+ ssize_t len;
+
+ len = virtqueue_pop(s->in_vq, &elem);
+ if (!len) {
+ /* stop listening until more queue space frees up */
+ s->listening = 0;
+ return;
+ }
+
+again:
+ len = readv(s->fd, elem.in_sg, elem.in_num);
+ if (len == -1 && (errno == EINTR || errno == EAGAIN))
+ goto again;
+
+ virtqueue_push(s->in_vq, &elem, len);
+ virtio_notify(&s->vdev, s->in_vq);
+}
+
+VirtIODevice *virtio_9p_init(PCIBus *bus, uint16_t vendor, uint16_t device)
+{
+ V9fsState *s;
+ struct sockaddr_in addr;
+ struct in_addr in;
+
+ s = (V9fsState *)virtio_init_pci(bus, "virtio-9p", vendor, device,
+ vendor, VIRTIO_ID_9P,
+ 0, sizeof(V9fsState));
+
+ s->fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (s->fd == -1)
+ exit(1);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(1025);
+ inet_aton("127.0.0.1", &in);
+ memcpy(&addr.sin_addr, &in, sizeof(in));
+
+ if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+ exit(2);
+
+ s->listening = 0; /* don't start listening until the queue is inited */
+
+ s->in_vq = virtio_add_queue(&s->vdev, virtio_9p_kick_input);
+ virtio_add_queue(&s->vdev, virtio_9p_handle_output);
+
+ qemu_set_fd_handler2(s->fd, virtio_9p_can_read, virtio_9p_handle_input,
+ NULL, s);
+
+ return &s->vdev;
+}
diff --git a/qemu/vl.h b/qemu/vl.h
index 249ede2..7b5cc8d 100644
--- a/qemu/vl.h
+++ b/qemu/vl.h
@@ -1399,6 +1399,8 @@ typedef struct VirtIODevice VirtIODevice;
VirtIODevice *virtio_blk_init(PCIBus *bus, uint16_t vendor, uint16_t device,
BlockDriverState *bs);

+VirtIODevice *virtio_9p_init(PCIBus *bus, uint16_t vendor, uint16_t device);
+
/* buf = NULL means polling */
typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
const uint8_t *buf, int len);