Line data Source code
1 : /*
2 : * vr_uvhost.c - implements a user-space vhost server that peers with
3 : * the vhost client inside qemu (version 2.1 and later).
4 : *
5 : * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
6 : */
7 :
8 : #include "vr_dpdk.h"
9 : #include "vr_dpdk_usocket.h"
10 : #include "vr_uvhost.h"
11 : #include "vr_uvhost_client.h"
12 : #include "vr_uvhost_msg.h"
13 : #include "vr_uvhost_util.h"
14 :
15 : #include <pthread.h>
16 : #include <stdint.h>
17 : #include <unistd.h>
18 : #include <linux/vhost.h>
19 : #include <sys/eventfd.h>
20 : #include <sys/socket.h>
21 : #include <sys/stat.h>
22 : #include <sys/types.h>
23 : #include <sys/un.h>
24 :
25 : #include <rte_errno.h>
26 :
27 : /* Global variables */
28 : vr_uvh_exit_callback_t vr_uvhost_exit_fn;
29 : struct pollfd pollfds[MAX_UVHOST_FDS];
30 :
31 : /*
32 : * vr_uvhost_init - initializes the user space vhost server and waits
33 : * for messages from the netlink thread or qemu clients. The netlink thread
34 : * sends messages on a UNIX pipe in order to specify the names of the UNIX
35 : * domain socket on which a qemu client will connect. Once this message is
36 : * received, the vhost server creates the UNIX domain socket and includes it
37 : * in the list of fds it waits on.
38 : *
39 : * Returns 0 on success, error otherwise.
40 : */
41 : int
42 0 : vr_uvhost_init(pthread_t *th, vr_uvh_exit_callback_t exit_fn)
43 : {
44 0 : if (pthread_create(th, NULL, vr_uvhost_start, NULL)) {
45 0 : return -1;
46 : }
47 :
48 0 : vr_uvhost_exit_fn = exit_fn;
49 0 : return 0;
50 : }
51 :
52 : /*
53 : * vr_uvhost_exit - exits the user space vhost server thread. Also forces
54 : * other threads in the process to exit by calling the process exit
55 : * callback.
56 : *
57 : * Returns nothing.
58 : */
59 : static void
60 53 : vr_uvhost_exit(void)
61 : {
62 53 : vr_uvhost_exit_fn();
63 :
64 53 : return;
65 : }
66 :
67 : void
68 53 : vr_uvhost_wakeup(void)
69 : {
70 53 : if (likely(vr_dpdk.uvhost_event_fd > 0)) {
71 53 : eventfd_write(vr_dpdk.uvhost_event_fd, 1);
72 : }
73 53 : }
74 :
75 : /*
76 : * vr_uvhost_start - starts the user space vhost server
77 : *
78 : * Returns NULL if an error occurs. Otherwise, it runs forever.
79 : */
80 : void *
81 53 : vr_uvhost_start(void *arg)
82 : {
83 53 : int s = 0, ret, err;
84 : struct sockaddr_un sun;
85 : nfds_t nfds;
86 :
87 53 : vr_uvhost_client_init();
88 :
89 53 : vr_uvhost_log("Starting uvhost server...\n");
90 53 : vr_dpdk.uvhost_event_fd = eventfd(0, 0);
91 53 : if (vr_dpdk.uvhost_event_fd == -1) {
92 0 : vr_uvhost_log(" error creating event FD: %s (%d)\n",
93 0 : rte_strerror(errno), errno);
94 0 : goto error;
95 : }
96 53 : vr_uvhost_log(" server event FD is %d\n", vr_dpdk.uvhost_event_fd);
97 :
98 53 : s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
99 53 : if (s == -1) {
100 0 : vr_uvhost_log(" error creating server socket: %s (%d)\n",
101 0 : rte_strerror(errno), errno);
102 0 : goto error;
103 : }
104 53 : vr_uvhost_log(" server socket FD is %d\n", s);
105 :
106 53 : memset(&sun, 0, sizeof(sun));
107 53 : sun.sun_family = AF_UNIX;
108 53 : strncpy(sun.sun_path, vr_socket_dir, sizeof(sun.sun_path) - 1);
109 53 : strncat(sun.sun_path, "/"VR_UVH_NL_SOCK_NAME, sizeof(sun.sun_path)
110 53 : - strlen(sun.sun_path) - 1);
111 :
112 53 : mkdir(vr_socket_dir, VR_DEF_SOCKET_DIR_MODE);
113 53 : unlink(sun.sun_path);
114 53 : ret = bind(s, (struct sockaddr *) &sun, sizeof(sun));
115 53 : if (ret == -1) {
116 0 : vr_uvhost_log(" error binding server FD %d to %s: %s (%d)\n",
117 0 : s, sun.sun_path, rte_strerror(errno), errno);
118 0 : goto error;
119 : }
120 :
121 53 : if (listen(s, 1) == -1) {
122 0 : vr_uvhost_log(" error listening server socket FD %d: %s (%d)\n",
123 0 : s, rte_strerror(errno), errno);
124 0 : goto error;
125 : }
126 :
127 53 : vr_uvhost_fds_init();
128 :
129 53 : if (vr_uvhost_add_fd(vr_dpdk.uvhost_event_fd, UVH_FD_READ, NULL, NULL)) {
130 0 : vr_uvhost_log(" error adding server event FD %d\n",
131 : vr_dpdk.uvhost_event_fd);
132 0 : goto error;
133 : }
134 53 : if (vr_uvhost_add_fd(s, UVH_FD_READ, NULL, vr_uvh_nl_listen_handler)) {
135 0 : vr_uvhost_log(" error adding server socket FD %d\n", s);
136 0 : goto error;
137 : }
138 :
139 : while (1) {
140 3942 : vr_uvh_init_pollfds(pollfds, &nfds);
141 :
142 3942 : rcu_thread_offline();
143 3942 : if (poll(pollfds, nfds, -1) < 0) {
144 0 : vr_uvhost_log(" error polling FDs: %s (%d)\n",
145 0 : rte_strerror(errno), errno);
146 0 : goto error;
147 : }
148 :
149 3942 : if (vr_dpdk_is_stop_flag_set())
150 53 : break;
151 :
152 3889 : rcu_thread_online();
153 :
154 3889 : vr_uvh_call_fd_handlers(pollfds, nfds);
155 : }
156 :
157 53 : error:
158 :
159 53 : err = errno;
160 53 : if (s) {
161 53 : close(s);
162 53 : unlink(sun.sun_path);
163 : }
164 53 : if (vr_dpdk.uvhost_event_fd > 0) {
165 53 : close(vr_dpdk.uvhost_event_fd);
166 53 : vr_dpdk.uvhost_event_fd = 0;
167 : }
168 :
169 53 : vr_uvhost_exit();
170 53 : errno = err;
171 :
172 53 : return NULL;
173 : }
174 :
|