tailq: remove unneeded inclusions
[dpdk.git] / lib / librte_eal / linuxapp / eal / eal_pci_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
38 /* sys/un.h with __USE_MISC uses strlen, which is unsafe */
39 #ifdef __USE_MISC
40 #define REMOVED_USE_MISC
41 #undef __USE_MISC
42 #endif
43 #include <sys/un.h>
44 /* make sure we redefine __USE_MISC only if it was previously undefined */
45 #ifdef REMOVED_USE_MISC
46 #define __USE_MISC
47 #undef REMOVED_USE_MISC
48 #endif
49
50 #include <rte_log.h>
51 #include <rte_pci.h>
52 #include <rte_eal_memconfig.h>
53 #include <rte_malloc.h>
54
55 #include "eal_filesystem.h"
56 #include "eal_pci_init.h"
57
58 /**
59  * @file
60  * VFIO socket for communication between primary and secondary processes.
61  *
62  * This file is only compiled if CONFIG_RTE_EAL_VFIO is set to "y".
63  */
64
65 #ifdef VFIO_PRESENT
66
67 #define SOCKET_PATH_FMT "%s/.%s_mp_socket"
68 #define CMSGLEN (CMSG_LEN(sizeof(int)))
69 #define FD_TO_CMSGHDR(fd, chdr) \
70                 do {\
71                         (chdr).cmsg_len = CMSGLEN;\
72                         (chdr).cmsg_level = SOL_SOCKET;\
73                         (chdr).cmsg_type = SCM_RIGHTS;\
74                         memcpy((chdr).__cmsg_data, &(fd), sizeof(fd));\
75                 } while (0)
76 #define CMSGHDR_TO_FD(chdr, fd) \
77                         memcpy(&(fd), (chdr).__cmsg_data, sizeof(fd))
78
79 static pthread_t socket_thread;
80 static int mp_socket_fd;
81
82
83 /* get socket path (/var/run if root, $HOME otherwise) */
84 static void
85 get_socket_path(char *buffer, int bufsz)
86 {
87         const char *dir = "/var/run";
88         const char *home_dir = getenv("HOME");
89
90         if (getuid() != 0 && home_dir != NULL)
91                 dir = home_dir;
92
93         /* use current prefix as file path */
94         snprintf(buffer, bufsz, SOCKET_PATH_FMT, dir,
95                         internal_config.hugefile_prefix);
96 }
97
98
99
100 /*
101  * data flow for socket comm protocol:
102  * 1. client sends SOCKET_REQ_CONTAINER or SOCKET_REQ_GROUP
103  * 1a. in case of SOCKET_REQ_GROUP, client also then sends group number
104  * 2. server receives message
105  * 2a. in case of invalid group, SOCKET_ERR is sent back to client
106  * 2b. in case of unbound group, SOCKET_NO_FD is sent back to client
107  * 2c. in case of valid group, SOCKET_OK is sent and immediately followed by fd
108  *
109  * in case of any error, socket is closed.
110  */
111
112 /* send a request, return -1 on error */
113 int
114 vfio_mp_sync_send_request(int socket, int req)
115 {
116         struct msghdr hdr;
117         struct iovec iov;
118         int buf;
119         int ret;
120
121         memset(&hdr, 0, sizeof(hdr));
122
123         buf = req;
124
125         hdr.msg_iov = &iov;
126         hdr.msg_iovlen = 1;
127         iov.iov_base = (char *) &buf;
128         iov.iov_len = sizeof(buf);
129
130         ret = sendmsg(socket, &hdr, 0);
131         if (ret < 0)
132                 return -1;
133         return 0;
134 }
135
136 /* receive a request and return it */
137 int
138 vfio_mp_sync_receive_request(int socket)
139 {
140         int buf;
141         struct msghdr hdr;
142         struct iovec iov;
143         int ret, req;
144
145         memset(&hdr, 0, sizeof(hdr));
146
147         buf = SOCKET_ERR;
148
149         hdr.msg_iov = &iov;
150         hdr.msg_iovlen = 1;
151         iov.iov_base = (char *) &buf;
152         iov.iov_len = sizeof(buf);
153
154         ret = recvmsg(socket, &hdr, 0);
155         if (ret < 0)
156                 return -1;
157
158         req = buf;
159
160         return req;
161 }
162
163 /* send OK in message, fd in control message */
164 int
165 vfio_mp_sync_send_fd(int socket, int fd)
166 {
167         int buf;
168         struct msghdr hdr;
169         struct cmsghdr *chdr;
170         char chdr_buf[CMSGLEN];
171         struct iovec iov;
172         int ret;
173
174         chdr = (struct cmsghdr *) chdr_buf;
175         memset(chdr, 0, sizeof(chdr_buf));
176         memset(&hdr, 0, sizeof(hdr));
177
178         hdr.msg_iov = &iov;
179         hdr.msg_iovlen = 1;
180         iov.iov_base = (char *) &buf;
181         iov.iov_len = sizeof(buf);
182         hdr.msg_control = chdr;
183         hdr.msg_controllen = CMSGLEN;
184
185         buf = SOCKET_OK;
186         FD_TO_CMSGHDR(fd, *chdr);
187
188         ret = sendmsg(socket, &hdr, 0);
189         if (ret < 0)
190                 return -1;
191         return 0;
192 }
193
194 /* receive OK in message, fd in control message */
195 int
196 vfio_mp_sync_receive_fd(int socket)
197 {
198         int buf;
199         struct msghdr hdr;
200         struct cmsghdr *chdr;
201         char chdr_buf[CMSGLEN];
202         struct iovec iov;
203         int ret, req, fd;
204
205         buf = SOCKET_ERR;
206
207         chdr = (struct cmsghdr *) chdr_buf;
208         memset(chdr, 0, sizeof(chdr_buf));
209         memset(&hdr, 0, sizeof(hdr));
210
211         hdr.msg_iov = &iov;
212         hdr.msg_iovlen = 1;
213         iov.iov_base = (char *) &buf;
214         iov.iov_len = sizeof(buf);
215         hdr.msg_control = chdr;
216         hdr.msg_controllen = CMSGLEN;
217
218         ret = recvmsg(socket, &hdr, 0);
219         if (ret < 0)
220                 return -1;
221
222         req = buf;
223
224         if (req != SOCKET_OK)
225                 return -1;
226
227         CMSGHDR_TO_FD(*chdr, fd);
228
229         return fd;
230 }
231
232 /* connect socket_fd in secondary process to the primary process's socket */
233 int
234 vfio_mp_sync_connect_to_primary(void)
235 {
236         struct sockaddr_un addr;
237         socklen_t sockaddr_len;
238         int socket_fd;
239
240         /* set up a socket */
241         socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
242         if (socket_fd < 0) {
243                 RTE_LOG(ERR, EAL, "Failed to create socket!\n");
244                 return -1;
245         }
246
247         get_socket_path(addr.sun_path, sizeof(addr.sun_path));
248         addr.sun_family = AF_UNIX;
249
250         sockaddr_len = sizeof(struct sockaddr_un);
251
252         if (connect(socket_fd, (struct sockaddr *) &addr, sockaddr_len) == 0)
253                 return socket_fd;
254
255         /* if connect failed */
256         close(socket_fd);
257         return -1;
258 }
259
260
261
262 /*
263  * socket listening thread for primary process
264  */
265 static __attribute__((noreturn)) void *
266 pci_vfio_mp_sync_thread(void __rte_unused * arg)
267 {
268         int ret, fd, vfio_group_no;
269
270         /* wait for requests on the socket */
271         for (;;) {
272                 int conn_sock;
273                 struct sockaddr_un addr;
274                 socklen_t sockaddr_len = sizeof(addr);
275
276                 /* this is a blocking call */
277                 conn_sock = accept(mp_socket_fd, (struct sockaddr *) &addr,
278                                 &sockaddr_len);
279
280                 /* just restart on error */
281                 if (conn_sock == -1)
282                         continue;
283
284                 /* set socket to linger after close */
285                 struct linger l;
286                 l.l_onoff = 1;
287                 l.l_linger = 60;
288                 setsockopt(conn_sock, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
289
290                 ret = vfio_mp_sync_receive_request(conn_sock);
291
292                 switch (ret) {
293                 case SOCKET_REQ_CONTAINER:
294                         fd = pci_vfio_get_container_fd();
295                         if (fd < 0)
296                                 vfio_mp_sync_send_request(conn_sock, SOCKET_ERR);
297                         else
298                                 vfio_mp_sync_send_fd(conn_sock, fd);
299                         break;
300                 case SOCKET_REQ_GROUP:
301                         /* wait for group number */
302                         vfio_group_no = vfio_mp_sync_receive_request(conn_sock);
303                         if (vfio_group_no < 0) {
304                                 close(conn_sock);
305                                 continue;
306                         }
307
308                         fd = pci_vfio_get_group_fd(vfio_group_no);
309
310                         if (fd < 0)
311                                 vfio_mp_sync_send_request(conn_sock, SOCKET_ERR);
312                         /* if VFIO group exists but isn't bound to VFIO driver */
313                         else if (fd == 0)
314                                 vfio_mp_sync_send_request(conn_sock, SOCKET_NO_FD);
315                         /* if group exists and is bound to VFIO driver */
316                         else {
317                                 vfio_mp_sync_send_request(conn_sock, SOCKET_OK);
318                                 vfio_mp_sync_send_fd(conn_sock, fd);
319                         }
320                         break;
321                 default:
322                         vfio_mp_sync_send_request(conn_sock, SOCKET_ERR);
323                         break;
324                 }
325                 close(conn_sock);
326         }
327 }
328
329 static int
330 vfio_mp_sync_socket_setup(void)
331 {
332         int ret, socket_fd;
333         struct sockaddr_un addr;
334         socklen_t sockaddr_len;
335
336         /* set up a socket */
337         socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
338         if (socket_fd < 0) {
339                 RTE_LOG(ERR, EAL, "Failed to create socket!\n");
340                 return -1;
341         }
342
343         get_socket_path(addr.sun_path, sizeof(addr.sun_path));
344         addr.sun_family = AF_UNIX;
345
346         sockaddr_len = sizeof(struct sockaddr_un);
347
348         unlink(addr.sun_path);
349
350         ret = bind(socket_fd, (struct sockaddr *) &addr, sockaddr_len);
351         if (ret) {
352                 RTE_LOG(ERR, EAL, "Failed to bind socket: %s!\n", strerror(errno));
353                 close(socket_fd);
354                 return -1;
355         }
356
357         ret = listen(socket_fd, 50);
358         if (ret) {
359                 RTE_LOG(ERR, EAL, "Failed to listen: %s!\n", strerror(errno));
360                 close(socket_fd);
361                 return -1;
362         }
363
364         /* save the socket in local configuration */
365         mp_socket_fd = socket_fd;
366
367         return 0;
368 }
369
370 /*
371  * set up a local socket and tell it to listen for incoming connections
372  */
373 int
374 pci_vfio_mp_sync_setup(void)
375 {
376         int ret;
377
378         if (vfio_mp_sync_socket_setup() < 0) {
379                 RTE_LOG(ERR, EAL, "Failed to set up local socket!\n");
380                 return -1;
381         }
382
383         ret = pthread_create(&socket_thread, NULL,
384                         pci_vfio_mp_sync_thread, NULL);
385         if (ret) {
386                 RTE_LOG(ERR, EAL, "Failed to create thread for communication with "
387                                 "secondary processes!\n");
388                 close(mp_socket_fd);
389                 return -1;
390         }
391         return 0;
392 }
393
394 #endif