New upstream version 17.11.3
[deb_dpdk.git] / drivers / net / mlx5 / mlx5_socket.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2016 6WIND S.A.
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in
14  *       the documentation and/or other materials provided with the
15  *       distribution.
16  *     * Neither the name of 6WIND S.A. nor the names of its
17  *       contributors may be used to endorse or promote products derived
18  *       from this software without specific prior written permission.
19  *
20  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 #define _GNU_SOURCE
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41
42 #include "mlx5.h"
43 #include "mlx5_utils.h"
44
45 /**
46  * Initialise the socket to communicate with the secondary process
47  *
48  * @param[in] dev
49  *   Pointer to Ethernet device.
50  *
51  * @return
52  *   0 on success, a negative errno value otherwise and rte_errno is set.
53  */
54 int
55 mlx5_socket_init(struct rte_eth_dev *dev)
56 {
57         struct priv *priv = dev->data->dev_private;
58         struct sockaddr_un sun = {
59                 .sun_family = AF_UNIX,
60         };
61         int ret;
62         int flags;
63
64         /*
65          * Initialise the socket to communicate with the secondary
66          * process.
67          */
68         ret = socket(AF_UNIX, SOCK_STREAM, 0);
69         if (ret < 0) {
70                 rte_errno = errno;
71                 DRV_LOG(WARNING, "port %u secondary process not supported: %s",
72                         dev->data->port_id, strerror(errno));
73                 goto error;
74         }
75         priv->primary_socket = ret;
76         flags = fcntl(priv->primary_socket, F_GETFL, 0);
77         if (flags == -1) {
78                 rte_errno = errno;
79                 goto error;
80         }
81         ret = fcntl(priv->primary_socket, F_SETFL, flags | O_NONBLOCK);
82         if (ret < 0) {
83                 rte_errno = errno;
84                 goto error;
85         }
86         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
87                  MLX5_DRIVER_NAME, priv->primary_socket);
88         remove(sun.sun_path);
89         ret = bind(priv->primary_socket, (const struct sockaddr *)&sun,
90                    sizeof(sun));
91         if (ret < 0) {
92                 rte_errno = errno;
93                 DRV_LOG(WARNING,
94                         "port %u cannot bind socket, secondary process not"
95                         " supported: %s",
96                         dev->data->port_id, strerror(errno));
97                 goto close;
98         }
99         ret = listen(priv->primary_socket, 0);
100         if (ret < 0) {
101                 rte_errno = errno;
102                 DRV_LOG(WARNING, "port %u secondary process not supported: %s",
103                         dev->data->port_id, strerror(errno));
104                 goto close;
105         }
106         return 0;
107 close:
108         remove(sun.sun_path);
109 error:
110         claim_zero(close(priv->primary_socket));
111         priv->primary_socket = 0;
112         return -rte_errno;
113 }
114
115 /**
116  * Un-Initialise the socket to communicate with the secondary process
117  *
118  * @param[in] dev
119  */
120 void
121 mlx5_socket_uninit(struct rte_eth_dev *dev)
122 {
123         struct priv *priv = dev->data->dev_private;
124
125         MKSTR(path, "/var/tmp/%s_%d", MLX5_DRIVER_NAME, priv->primary_socket);
126         claim_zero(close(priv->primary_socket));
127         priv->primary_socket = 0;
128         claim_zero(remove(path));
129 }
130
131 /**
132  * Handle socket interrupts.
133  *
134  * @param dev
135  *   Pointer to Ethernet device.
136  */
137 void
138 mlx5_socket_handle(struct rte_eth_dev *dev)
139 {
140         struct priv *priv = dev->data->dev_private;
141         int conn_sock;
142         int ret = 0;
143         struct cmsghdr *cmsg = NULL;
144         struct ucred *cred = NULL;
145         char buf[CMSG_SPACE(sizeof(struct ucred))] = { 0 };
146         char vbuf[1024] = { 0 };
147         struct iovec io = {
148                 .iov_base = vbuf,
149                 .iov_len = sizeof(*vbuf),
150         };
151         struct msghdr msg = {
152                 .msg_iov = &io,
153                 .msg_iovlen = 1,
154                 .msg_control = buf,
155                 .msg_controllen = sizeof(buf),
156         };
157         int *fd;
158
159         /* Accept the connection from the client. */
160         conn_sock = accept(priv->primary_socket, NULL, NULL);
161         if (conn_sock < 0) {
162                 DRV_LOG(WARNING, "port %u connection failed: %s",
163                         dev->data->port_id, strerror(errno));
164                 return;
165         }
166         ret = setsockopt(conn_sock, SOL_SOCKET, SO_PASSCRED, &(int){1},
167                                          sizeof(int));
168         if (ret < 0) {
169                 ret = errno;
170                 DRV_LOG(WARNING, "port %u cannot change socket options: %s",
171                         dev->data->port_id, strerror(rte_errno));
172                 goto error;
173         }
174         ret = recvmsg(conn_sock, &msg, MSG_WAITALL);
175         if (ret < 0) {
176                 ret = errno;
177                 DRV_LOG(WARNING, "port %u received an empty message: %s",
178                         dev->data->port_id, strerror(rte_errno));
179                 goto error;
180         }
181         /* Expect to receive credentials only. */
182         cmsg = CMSG_FIRSTHDR(&msg);
183         if (cmsg == NULL) {
184                 DRV_LOG(WARNING, "port %u no message", dev->data->port_id);
185                 goto error;
186         }
187         if ((cmsg->cmsg_type == SCM_CREDENTIALS) &&
188                 (cmsg->cmsg_len >= sizeof(*cred))) {
189                 cred = (struct ucred *)CMSG_DATA(cmsg);
190                 assert(cred != NULL);
191         }
192         cmsg = CMSG_NXTHDR(&msg, cmsg);
193         if (cmsg != NULL) {
194                 DRV_LOG(WARNING, "port %u message wrongly formatted",
195                         dev->data->port_id);
196                 goto error;
197         }
198         /* Make sure all the ancillary data was received and valid. */
199         if ((cred == NULL) || (cred->uid != getuid()) ||
200             (cred->gid != getgid())) {
201                 DRV_LOG(WARNING, "port %u wrong credentials",
202                         dev->data->port_id);
203                 goto error;
204         }
205         /* Set-up the ancillary data. */
206         cmsg = CMSG_FIRSTHDR(&msg);
207         assert(cmsg != NULL);
208         cmsg->cmsg_level = SOL_SOCKET;
209         cmsg->cmsg_type = SCM_RIGHTS;
210         cmsg->cmsg_len = CMSG_LEN(sizeof(priv->ctx->cmd_fd));
211         fd = (int *)CMSG_DATA(cmsg);
212         *fd = priv->ctx->cmd_fd;
213         ret = sendmsg(conn_sock, &msg, 0);
214         if (ret < 0)
215                 DRV_LOG(WARNING, "port %u cannot send response",
216                         dev->data->port_id);
217 error:
218         close(conn_sock);
219 }
220
221 /**
222  * Connect to the primary process.
223  *
224  * @param[in] dev
225  *   Pointer to Ethernet structure.
226  *
227  * @return
228  *   fd on success, negative errno value otherwise and rte_errno is set.
229  */
230 int
231 mlx5_socket_connect(struct rte_eth_dev *dev)
232 {
233         struct priv *priv = dev->data->dev_private;
234         struct sockaddr_un sun = {
235                 .sun_family = AF_UNIX,
236         };
237         int socket_fd = -1;
238         int *fd = NULL;
239         int ret;
240         struct ucred *cred;
241         char buf[CMSG_SPACE(sizeof(*cred))] = { 0 };
242         char vbuf[1024] = { 0 };
243         struct iovec io = {
244                 .iov_base = vbuf,
245                 .iov_len = sizeof(*vbuf),
246         };
247         struct msghdr msg = {
248                 .msg_control = buf,
249                 .msg_controllen = sizeof(buf),
250                 .msg_iov = &io,
251                 .msg_iovlen = 1,
252         };
253         struct cmsghdr *cmsg;
254
255         ret = socket(AF_UNIX, SOCK_STREAM, 0);
256         if (ret < 0) {
257                 rte_errno = errno;
258                 DRV_LOG(WARNING, "port %u cannot connect to primary",
259                         dev->data->port_id);
260                 goto error;
261         }
262         socket_fd = ret;
263         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
264                  MLX5_DRIVER_NAME, priv->primary_socket);
265         ret = connect(socket_fd, (const struct sockaddr *)&sun, sizeof(sun));
266         if (ret < 0) {
267                 rte_errno = errno;
268                 DRV_LOG(WARNING, "port %u cannot connect to primary",
269                         dev->data->port_id);
270                 goto error;
271         }
272         cmsg = CMSG_FIRSTHDR(&msg);
273         if (cmsg == NULL) {
274                 rte_errno = EINVAL;
275                 DRV_LOG(DEBUG, "port %u cannot get first message",
276                         dev->data->port_id);
277                 goto error;
278         }
279         cmsg->cmsg_level = SOL_SOCKET;
280         cmsg->cmsg_type = SCM_CREDENTIALS;
281         cmsg->cmsg_len = CMSG_LEN(sizeof(*cred));
282         cred = (struct ucred *)CMSG_DATA(cmsg);
283         if (cred == NULL) {
284                 rte_errno = EINVAL;
285                 DRV_LOG(DEBUG, "port %u no credentials received",
286                         dev->data->port_id);
287                 goto error;
288         }
289         cred->pid = getpid();
290         cred->uid = getuid();
291         cred->gid = getgid();
292         ret = sendmsg(socket_fd, &msg, MSG_DONTWAIT);
293         if (ret < 0) {
294                 rte_errno = errno;
295                 DRV_LOG(WARNING,
296                         "port %u cannot send credentials to primary: %s",
297                         dev->data->port_id, strerror(errno));
298                 goto error;
299         }
300         ret = recvmsg(socket_fd, &msg, MSG_WAITALL);
301         if (ret <= 0) {
302                 rte_errno = errno;
303                 DRV_LOG(WARNING, "port %u no message from primary: %s",
304                         dev->data->port_id, strerror(errno));
305                 goto error;
306         }
307         cmsg = CMSG_FIRSTHDR(&msg);
308         if (cmsg == NULL) {
309                 rte_errno = EINVAL;
310                 DRV_LOG(WARNING, "port %u no file descriptor received",
311                         dev->data->port_id);
312                 goto error;
313         }
314         fd = (int *)CMSG_DATA(cmsg);
315         if (*fd < 0) {
316                 DRV_LOG(WARNING, "port %u no file descriptor received: %s",
317                         dev->data->port_id, strerror(errno));
318                 rte_errno = *fd;
319                 goto error;
320         }
321         ret = *fd;
322         close(socket_fd);
323         return ret;
324 error:
325         if (socket_fd != -1)
326                 close(socket_fd);
327         return -rte_errno;
328 }