vfio: expose functions
[dpdk.git] / lib / librte_eal / linuxapp / eal / eal_vfio_mp_sync.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <string.h>
35 #include <fcntl.h>
36 #include <sys/socket.h>
37 #include <pthread.h>
38
39 /* sys/un.h with __USE_MISC uses strlen, which is unsafe */
40 #ifdef __USE_MISC
41 #define REMOVED_USE_MISC
42 #undef __USE_MISC
43 #endif
44 #include <sys/un.h>
45 /* make sure we redefine __USE_MISC only if it was previously undefined */
46 #ifdef REMOVED_USE_MISC
47 #define __USE_MISC
48 #undef REMOVED_USE_MISC
49 #endif
50
51 #include <rte_log.h>
52 #include <rte_pci.h>
53 #include <rte_eal_memconfig.h>
54 #include <rte_malloc.h>
55 #include <rte_vfio.h>
56
57 #include "eal_filesystem.h"
58 #include "eal_pci_init.h"
59 #include "eal_vfio.h"
60 #include "eal_thread.h"
61
62 /**
63  * @file
64  * VFIO socket for communication between primary and secondary processes.
65  *
66  * This file is only compiled if CONFIG_RTE_EAL_VFIO is set to "y".
67  */
68
69 #ifdef VFIO_PRESENT
70
71 #define SOCKET_PATH_FMT "%s/.%s_mp_socket"
72 #define CMSGLEN (CMSG_LEN(sizeof(int)))
73 #define FD_TO_CMSGHDR(fd, chdr) \
74                 do {\
75                         (chdr).cmsg_len = CMSGLEN;\
76                         (chdr).cmsg_level = SOL_SOCKET;\
77                         (chdr).cmsg_type = SCM_RIGHTS;\
78                         memcpy((chdr).__cmsg_data, &(fd), sizeof(fd));\
79                 } while (0)
80 #define CMSGHDR_TO_FD(chdr, fd) \
81                         memcpy(&(fd), (chdr).__cmsg_data, sizeof(fd))
82
83 static pthread_t socket_thread;
84 static int mp_socket_fd;
85
86
87 /* get socket path (/var/run if root, $HOME otherwise) */
88 static void
89 get_socket_path(char *buffer, int bufsz)
90 {
91         const char *dir = "/var/run";
92         const char *home_dir = getenv("HOME");
93
94         if (getuid() != 0 && home_dir != NULL)
95                 dir = home_dir;
96
97         /* use current prefix as file path */
98         snprintf(buffer, bufsz, SOCKET_PATH_FMT, dir,
99                         internal_config.hugefile_prefix);
100 }
101
102
103
104 /*
105  * data flow for socket comm protocol:
106  * 1. client sends SOCKET_REQ_CONTAINER or SOCKET_REQ_GROUP
107  * 1a. in case of SOCKET_REQ_GROUP, client also then sends group number
108  * 2. server receives message
109  * 2a. in case of invalid group, SOCKET_ERR is sent back to client
110  * 2b. in case of unbound group, SOCKET_NO_FD is sent back to client
111  * 2c. in case of valid group, SOCKET_OK is sent and immediately followed by fd
112  *
113  * in case of any error, socket is closed.
114  */
115
116 /* send a request, return -1 on error */
117 int
118 vfio_mp_sync_send_request(int socket, int req)
119 {
120         struct msghdr hdr;
121         struct iovec iov;
122         int buf;
123         int ret;
124
125         memset(&hdr, 0, sizeof(hdr));
126
127         buf = req;
128
129         hdr.msg_iov = &iov;
130         hdr.msg_iovlen = 1;
131         iov.iov_base = (char *) &buf;
132         iov.iov_len = sizeof(buf);
133
134         ret = sendmsg(socket, &hdr, 0);
135         if (ret < 0)
136                 return -1;
137         return 0;
138 }
139
140 /* receive a request and return it */
141 int
142 vfio_mp_sync_receive_request(int socket)
143 {
144         int buf;
145         struct msghdr hdr;
146         struct iovec iov;
147         int ret, req;
148
149         memset(&hdr, 0, sizeof(hdr));
150
151         buf = SOCKET_ERR;
152
153         hdr.msg_iov = &iov;
154         hdr.msg_iovlen = 1;
155         iov.iov_base = (char *) &buf;
156         iov.iov_len = sizeof(buf);
157
158         ret = recvmsg(socket, &hdr, 0);
159         if (ret < 0)
160                 return -1;
161
162         req = buf;
163
164         return req;
165 }
166
167 /* send OK in message, fd in control message */
168 int
169 vfio_mp_sync_send_fd(int socket, int fd)
170 {
171         int buf;
172         struct msghdr hdr;
173         struct cmsghdr *chdr;
174         char chdr_buf[CMSGLEN];
175         struct iovec iov;
176         int ret;
177
178         chdr = (struct cmsghdr *) chdr_buf;
179         memset(chdr, 0, sizeof(chdr_buf));
180         memset(&hdr, 0, sizeof(hdr));
181
182         hdr.msg_iov = &iov;
183         hdr.msg_iovlen = 1;
184         iov.iov_base = (char *) &buf;
185         iov.iov_len = sizeof(buf);
186         hdr.msg_control = chdr;
187         hdr.msg_controllen = CMSGLEN;
188
189         buf = SOCKET_OK;
190         FD_TO_CMSGHDR(fd, *chdr);
191
192         ret = sendmsg(socket, &hdr, 0);
193         if (ret < 0)
194                 return -1;
195         return 0;
196 }
197
198 /* receive OK in message, fd in control message */
199 int
200 vfio_mp_sync_receive_fd(int socket)
201 {
202         int buf;
203         struct msghdr hdr;
204         struct cmsghdr *chdr;
205         char chdr_buf[CMSGLEN];
206         struct iovec iov;
207         int ret, req, fd;
208
209         buf = SOCKET_ERR;
210
211         chdr = (struct cmsghdr *) chdr_buf;
212         memset(chdr, 0, sizeof(chdr_buf));
213         memset(&hdr, 0, sizeof(hdr));
214
215         hdr.msg_iov = &iov;
216         hdr.msg_iovlen = 1;
217         iov.iov_base = (char *) &buf;
218         iov.iov_len = sizeof(buf);
219         hdr.msg_control = chdr;
220         hdr.msg_controllen = CMSGLEN;
221
222         ret = recvmsg(socket, &hdr, 0);
223         if (ret < 0)
224                 return -1;
225
226         req = buf;
227
228         if (req != SOCKET_OK)
229                 return -1;
230
231         CMSGHDR_TO_FD(*chdr, fd);
232
233         return fd;
234 }
235
236 /* connect socket_fd in secondary process to the primary process's socket */
237 int
238 vfio_mp_sync_connect_to_primary(void)
239 {
240         struct sockaddr_un addr;
241         socklen_t sockaddr_len;
242         int socket_fd;
243
244         /* set up a socket */
245         socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
246         if (socket_fd < 0) {
247                 RTE_LOG(ERR, EAL, "Failed to create socket!\n");
248                 return -1;
249         }
250
251         get_socket_path(addr.sun_path, sizeof(addr.sun_path));
252         addr.sun_family = AF_UNIX;
253
254         sockaddr_len = sizeof(struct sockaddr_un);
255
256         if (connect(socket_fd, (struct sockaddr *) &addr, sockaddr_len) == 0)
257                 return socket_fd;
258
259         /* if connect failed */
260         close(socket_fd);
261         return -1;
262 }
263
264
265
266 /*
267  * socket listening thread for primary process
268  */
269 static __attribute__((noreturn)) void *
270 vfio_mp_sync_thread(void __rte_unused * arg)
271 {
272         int ret, fd, vfio_data;
273
274         /* wait for requests on the socket */
275         for (;;) {
276                 int conn_sock;
277                 struct sockaddr_un addr;
278                 socklen_t sockaddr_len = sizeof(addr);
279
280                 /* this is a blocking call */
281                 conn_sock = accept(mp_socket_fd, (struct sockaddr *) &addr,
282                                 &sockaddr_len);
283
284                 /* just restart on error */
285                 if (conn_sock == -1)
286                         continue;
287
288                 /* set socket to linger after close */
289                 struct linger l;
290                 l.l_onoff = 1;
291                 l.l_linger = 60;
292
293                 if (setsockopt(conn_sock, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
294                         RTE_LOG(WARNING, EAL, "Cannot set SO_LINGER option "
295                                         "on listen socket (%s)\n", strerror(errno));
296
297                 ret = vfio_mp_sync_receive_request(conn_sock);
298
299                 switch (ret) {
300                 case SOCKET_REQ_CONTAINER:
301                         fd = vfio_get_container_fd();
302                         if (fd < 0)
303                                 vfio_mp_sync_send_request(conn_sock, SOCKET_ERR);
304                         else
305                                 vfio_mp_sync_send_fd(conn_sock, fd);
306                         if (fd >= 0)
307                                 close(fd);
308                         break;
309                 case SOCKET_REQ_GROUP:
310                         /* wait for group number */
311                         vfio_data = vfio_mp_sync_receive_request(conn_sock);
312                         if (vfio_data < 0) {
313                                 close(conn_sock);
314                                 continue;
315                         }
316
317                         fd = vfio_get_group_fd(vfio_data);
318
319                         if (fd < 0)
320                                 vfio_mp_sync_send_request(conn_sock, SOCKET_ERR);
321                         /* if VFIO group exists but isn't bound to VFIO driver */
322                         else if (fd == 0)
323                                 vfio_mp_sync_send_request(conn_sock, SOCKET_NO_FD);
324                         /* if group exists and is bound to VFIO driver */
325                         else {
326                                 vfio_mp_sync_send_request(conn_sock, SOCKET_OK);
327                                 vfio_mp_sync_send_fd(conn_sock, fd);
328                         }
329                         break;
330                 case SOCKET_CLR_GROUP:
331                         /* wait for group fd */
332                         vfio_data = vfio_mp_sync_receive_request(conn_sock);
333                         if (vfio_data < 0) {
334                                 close(conn_sock);
335                                 continue;
336                         }
337
338                         ret = clear_group(vfio_data);
339
340                         if (ret < 0)
341                                 vfio_mp_sync_send_request(conn_sock, SOCKET_NO_FD);
342                         else
343                                 vfio_mp_sync_send_request(conn_sock, SOCKET_OK);
344                         break;
345                 default:
346                         vfio_mp_sync_send_request(conn_sock, SOCKET_ERR);
347                         break;
348                 }
349                 close(conn_sock);
350         }
351 }
352
353 static int
354 vfio_mp_sync_socket_setup(void)
355 {
356         int ret, socket_fd;
357         struct sockaddr_un addr;
358         socklen_t sockaddr_len;
359
360         /* set up a socket */
361         socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
362         if (socket_fd < 0) {
363                 RTE_LOG(ERR, EAL, "Failed to create socket!\n");
364                 return -1;
365         }
366
367         get_socket_path(addr.sun_path, sizeof(addr.sun_path));
368         addr.sun_family = AF_UNIX;
369
370         sockaddr_len = sizeof(struct sockaddr_un);
371
372         unlink(addr.sun_path);
373
374         ret = bind(socket_fd, (struct sockaddr *) &addr, sockaddr_len);
375         if (ret) {
376                 RTE_LOG(ERR, EAL, "Failed to bind socket: %s!\n", strerror(errno));
377                 close(socket_fd);
378                 return -1;
379         }
380
381         ret = listen(socket_fd, 50);
382         if (ret) {
383                 RTE_LOG(ERR, EAL, "Failed to listen: %s!\n", strerror(errno));
384                 close(socket_fd);
385                 return -1;
386         }
387
388         /* save the socket in local configuration */
389         mp_socket_fd = socket_fd;
390
391         return 0;
392 }
393
394 /*
395  * set up a local socket and tell it to listen for incoming connections
396  */
397 int
398 vfio_mp_sync_setup(void)
399 {
400         int ret;
401         char thread_name[RTE_MAX_THREAD_NAME_LEN];
402
403         if (vfio_mp_sync_socket_setup() < 0) {
404                 RTE_LOG(ERR, EAL, "Failed to set up local socket!\n");
405                 return -1;
406         }
407
408         ret = pthread_create(&socket_thread, NULL,
409                         vfio_mp_sync_thread, NULL);
410         if (ret) {
411                 RTE_LOG(ERR, EAL,
412                         "Failed to create thread for communication with secondary processes!\n");
413                 close(mp_socket_fd);
414                 return -1;
415         }
416
417         /* Set thread_name for aid in debugging. */
418         snprintf(thread_name, RTE_MAX_THREAD_NAME_LEN, "vfio-sync");
419         ret = rte_thread_setname(socket_thread, thread_name);
420         if (ret)
421                 RTE_LOG(DEBUG, EAL,
422                         "Failed to set thread name for secondary processes!\n");
423
424         return 0;
425 }
426
427 #endif