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