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