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