virtiofsd: Add main virtio loop
[qemu/ar7.git] / tools / virtiofsd / fuse_virtio.c
blob1928a2025c91f1c8649125680cbd7f4fcd472abe
1 /*
2 * virtio-fs glue for FUSE
3 * Copyright (C) 2018 Red Hat, Inc. and/or its affiliates
5 * Authors:
6 * Dave Gilbert <dgilbert@redhat.com>
8 * Implements the glue between libfuse and libvhost-user
10 * This program can be distributed under the terms of the GNU LGPLv2.
11 * See the file COPYING.LIB
14 #include "fuse_virtio.h"
15 #include "fuse_i.h"
16 #include "standard-headers/linux/fuse.h"
17 #include "fuse_misc.h"
18 #include "fuse_opt.h"
20 #include <assert.h>
21 #include <errno.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 #include <sys/types.h>
28 #include <sys/un.h>
29 #include <unistd.h>
31 #include "contrib/libvhost-user/libvhost-user.h"
34 * We pass the dev element into libvhost-user
35 * and then use it to get back to the outer
36 * container for other data.
38 struct fv_VuDev {
39 VuDev dev;
40 struct fuse_session *se;
43 /* From spec */
44 struct virtio_fs_config {
45 char tag[36];
46 uint32_t num_queues;
50 * Callback from libvhost-user if there's a new fd we're supposed to listen
51 * to, typically a queue kick?
53 static void fv_set_watch(VuDev *dev, int fd, int condition, vu_watch_cb cb,
54 void *data)
56 fuse_log(FUSE_LOG_WARNING, "%s: TODO! fd=%d\n", __func__, fd);
60 * Callback from libvhost-user if we're no longer supposed to listen on an fd
62 static void fv_remove_watch(VuDev *dev, int fd)
64 fuse_log(FUSE_LOG_WARNING, "%s: TODO! fd=%d\n", __func__, fd);
67 /* Callback from libvhost-user to panic */
68 static void fv_panic(VuDev *dev, const char *err)
70 fuse_log(FUSE_LOG_ERR, "%s: libvhost-user: %s\n", __func__, err);
71 /* TODO: Allow reconnects?? */
72 exit(EXIT_FAILURE);
75 static bool fv_queue_order(VuDev *dev, int qidx)
77 return false;
80 static const VuDevIface fv_iface = {
81 /* TODO: Add other callbacks */
82 .queue_is_processed_in_order = fv_queue_order,
86 * Main loop; this mostly deals with events on the vhost-user
87 * socket itself, and not actual fuse data.
89 int virtio_loop(struct fuse_session *se)
91 fuse_log(FUSE_LOG_INFO, "%s: Entry\n", __func__);
93 while (!fuse_session_exited(se)) {
94 struct pollfd pf[1];
95 pf[0].fd = se->vu_socketfd;
96 pf[0].events = POLLIN;
97 pf[0].revents = 0;
99 fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for VU event\n", __func__);
100 int poll_res = ppoll(pf, 1, NULL, NULL);
102 if (poll_res == -1) {
103 if (errno == EINTR) {
104 fuse_log(FUSE_LOG_INFO, "%s: ppoll interrupted, going around\n",
105 __func__);
106 continue;
108 fuse_log(FUSE_LOG_ERR, "virtio_loop ppoll: %m\n");
109 break;
111 assert(poll_res == 1);
112 if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
113 fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x\n", __func__,
114 pf[0].revents);
115 break;
117 assert(pf[0].revents & POLLIN);
118 fuse_log(FUSE_LOG_DEBUG, "%s: Got VU event\n", __func__);
119 if (!vu_dispatch(&se->virtio_dev->dev)) {
120 fuse_log(FUSE_LOG_ERR, "%s: vu_dispatch failed\n", __func__);
121 break;
125 fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__);
127 return 0;
130 int virtio_session_mount(struct fuse_session *se)
132 struct sockaddr_un un;
133 mode_t old_umask;
135 if (strlen(se->vu_socket_path) >= sizeof(un.sun_path)) {
136 fuse_log(FUSE_LOG_ERR, "Socket path too long\n");
137 return -1;
140 se->fd = -1;
143 * Create the Unix socket to communicate with qemu
144 * based on QEMU's vhost-user-bridge
146 unlink(se->vu_socket_path);
147 strcpy(un.sun_path, se->vu_socket_path);
148 size_t addr_len = sizeof(un);
150 int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0);
151 if (listen_sock == -1) {
152 fuse_log(FUSE_LOG_ERR, "vhost socket creation: %m\n");
153 return -1;
155 un.sun_family = AF_UNIX;
158 * Unfortunately bind doesn't let you set the mask on the socket,
159 * so set umask to 077 and restore it later.
161 old_umask = umask(0077);
162 if (bind(listen_sock, (struct sockaddr *)&un, addr_len) == -1) {
163 fuse_log(FUSE_LOG_ERR, "vhost socket bind: %m\n");
164 umask(old_umask);
165 return -1;
167 umask(old_umask);
169 if (listen(listen_sock, 1) == -1) {
170 fuse_log(FUSE_LOG_ERR, "vhost socket listen: %m\n");
171 return -1;
174 fuse_log(FUSE_LOG_INFO, "%s: Waiting for vhost-user socket connection...\n",
175 __func__);
176 int data_sock = accept(listen_sock, NULL, NULL);
177 if (data_sock == -1) {
178 fuse_log(FUSE_LOG_ERR, "vhost socket accept: %m\n");
179 close(listen_sock);
180 return -1;
182 close(listen_sock);
183 fuse_log(FUSE_LOG_INFO, "%s: Received vhost-user socket connection\n",
184 __func__);
186 /* TODO: Some cleanup/deallocation! */
187 se->virtio_dev = calloc(sizeof(struct fv_VuDev), 1);
188 if (!se->virtio_dev) {
189 fuse_log(FUSE_LOG_ERR, "%s: virtio_dev calloc failed\n", __func__);
190 close(data_sock);
191 return -1;
194 se->vu_socketfd = data_sock;
195 se->virtio_dev->se = se;
196 vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, fv_set_watch,
197 fv_remove_watch, &fv_iface);
199 return 0;