New upstream version 17.11.4
[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          * Close the last socket that was used to communicate
66          * with the secondary process
67          */
68         if (priv->primary_socket)
69                 mlx5_socket_uninit(dev);
70         /*
71          * Initialise the socket to communicate with the secondary
72          * process.
73          */
74         ret = socket(AF_UNIX, SOCK_STREAM, 0);
75         if (ret < 0) {
76                 rte_errno = errno;
77                 DRV_LOG(WARNING, "port %u secondary process not supported: %s",
78                         dev->data->port_id, strerror(errno));
79                 goto error;
80         }
81         priv->primary_socket = ret;
82         flags = fcntl(priv->primary_socket, F_GETFL, 0);
83         if (flags == -1) {
84                 rte_errno = errno;
85                 goto error;
86         }
87         ret = fcntl(priv->primary_socket, F_SETFL, flags | O_NONBLOCK);
88         if (ret < 0) {
89                 rte_errno = errno;
90                 goto error;
91         }
92         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
93                  MLX5_DRIVER_NAME, priv->primary_socket);
94         remove(sun.sun_path);
95         ret = bind(priv->primary_socket, (const struct sockaddr *)&sun,
96                    sizeof(sun));
97         if (ret < 0) {
98                 rte_errno = errno;
99                 DRV_LOG(WARNING,
100                         "port %u cannot bind socket, secondary process not"
101                         " supported: %s",
102                         dev->data->port_id, strerror(errno));
103                 goto close;
104         }
105         ret = listen(priv->primary_socket, 0);
106         if (ret < 0) {
107                 rte_errno = errno;
108                 DRV_LOG(WARNING, "port %u secondary process not supported: %s",
109                         dev->data->port_id, strerror(errno));
110                 goto close;
111         }
112         return 0;
113 close:
114         remove(sun.sun_path);
115 error:
116         claim_zero(close(priv->primary_socket));
117         priv->primary_socket = 0;
118         return -rte_errno;
119 }
120
121 /**
122  * Un-Initialise the socket to communicate with the secondary process
123  *
124  * @param[in] dev
125  */
126 void
127 mlx5_socket_uninit(struct rte_eth_dev *dev)
128 {
129         struct priv *priv = dev->data->dev_private;
130
131         MKSTR(path, "/var/tmp/%s_%d", MLX5_DRIVER_NAME, priv->primary_socket);
132         claim_zero(close(priv->primary_socket));
133         priv->primary_socket = 0;
134         claim_zero(remove(path));
135 }
136
137 /**
138  * Handle socket interrupts.
139  *
140  * @param dev
141  *   Pointer to Ethernet device.
142  */
143 void
144 mlx5_socket_handle(struct rte_eth_dev *dev)
145 {
146         struct priv *priv = dev->data->dev_private;
147         int conn_sock;
148         int ret = 0;
149         struct cmsghdr *cmsg = NULL;
150         struct ucred *cred = NULL;
151         char buf[CMSG_SPACE(sizeof(struct ucred))] = { 0 };
152         char vbuf[1024] = { 0 };
153         struct iovec io = {
154                 .iov_base = vbuf,
155                 .iov_len = sizeof(*vbuf),
156         };
157         struct msghdr msg = {
158                 .msg_iov = &io,
159                 .msg_iovlen = 1,
160                 .msg_control = buf,
161                 .msg_controllen = sizeof(buf),
162         };
163         int *fd;
164
165         /* Accept the connection from the client. */
166         conn_sock = accept(priv->primary_socket, NULL, NULL);
167         if (conn_sock < 0) {
168                 DRV_LOG(WARNING, "port %u connection failed: %s",
169                         dev->data->port_id, strerror(errno));
170                 return;
171         }
172         ret = setsockopt(conn_sock, SOL_SOCKET, SO_PASSCRED, &(int){1},
173                                          sizeof(int));
174         if (ret < 0) {
175                 ret = errno;
176                 DRV_LOG(WARNING, "port %u cannot change socket options: %s",
177                         dev->data->port_id, strerror(rte_errno));
178                 goto error;
179         }
180         ret = recvmsg(conn_sock, &msg, MSG_WAITALL);
181         if (ret < 0) {
182                 ret = errno;
183                 DRV_LOG(WARNING, "port %u received an empty message: %s",
184                         dev->data->port_id, strerror(rte_errno));
185                 goto error;
186         }
187         /* Expect to receive credentials only. */
188         cmsg = CMSG_FIRSTHDR(&msg);
189         if (cmsg == NULL) {
190                 DRV_LOG(WARNING, "port %u no message", dev->data->port_id);
191                 goto error;
192         }
193         if ((cmsg->cmsg_type == SCM_CREDENTIALS) &&
194                 (cmsg->cmsg_len >= sizeof(*cred))) {
195                 cred = (struct ucred *)CMSG_DATA(cmsg);
196                 assert(cred != NULL);
197         }
198         cmsg = CMSG_NXTHDR(&msg, cmsg);
199         if (cmsg != NULL) {
200                 DRV_LOG(WARNING, "port %u message wrongly formatted",
201                         dev->data->port_id);
202                 goto error;
203         }
204         /* Make sure all the ancillary data was received and valid. */
205         if ((cred == NULL) || (cred->uid != getuid()) ||
206             (cred->gid != getgid())) {
207                 DRV_LOG(WARNING, "port %u wrong credentials",
208                         dev->data->port_id);
209                 goto error;
210         }
211         /* Set-up the ancillary data. */
212         cmsg = CMSG_FIRSTHDR(&msg);
213         assert(cmsg != NULL);
214         cmsg->cmsg_level = SOL_SOCKET;
215         cmsg->cmsg_type = SCM_RIGHTS;
216         cmsg->cmsg_len = CMSG_LEN(sizeof(priv->ctx->cmd_fd));
217         fd = (int *)CMSG_DATA(cmsg);
218         *fd = priv->ctx->cmd_fd;
219         ret = sendmsg(conn_sock, &msg, 0);
220         if (ret < 0)
221                 DRV_LOG(WARNING, "port %u cannot send response",
222                         dev->data->port_id);
223 error:
224         close(conn_sock);
225 }
226
227 /**
228  * Connect to the primary process.
229  *
230  * @param[in] dev
231  *   Pointer to Ethernet structure.
232  *
233  * @return
234  *   fd on success, negative errno value otherwise and rte_errno is set.
235  */
236 int
237 mlx5_socket_connect(struct rte_eth_dev *dev)
238 {
239         struct priv *priv = dev->data->dev_private;
240         struct sockaddr_un sun = {
241                 .sun_family = AF_UNIX,
242         };
243         int socket_fd = -1;
244         int *fd = NULL;
245         int ret;
246         struct ucred *cred;
247         char buf[CMSG_SPACE(sizeof(*cred))] = { 0 };
248         char vbuf[1024] = { 0 };
249         struct iovec io = {
250                 .iov_base = vbuf,
251                 .iov_len = sizeof(*vbuf),
252         };
253         struct msghdr msg = {
254                 .msg_control = buf,
255                 .msg_controllen = sizeof(buf),
256                 .msg_iov = &io,
257                 .msg_iovlen = 1,
258         };
259         struct cmsghdr *cmsg;
260
261         ret = socket(AF_UNIX, SOCK_STREAM, 0);
262         if (ret < 0) {
263                 rte_errno = errno;
264                 DRV_LOG(WARNING, "port %u cannot connect to primary",
265                         dev->data->port_id);
266                 goto error;
267         }
268         socket_fd = ret;
269         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
270                  MLX5_DRIVER_NAME, priv->primary_socket);
271         ret = connect(socket_fd, (const struct sockaddr *)&sun, sizeof(sun));
272         if (ret < 0) {
273                 rte_errno = errno;
274                 DRV_LOG(WARNING, "port %u cannot connect to primary",
275                         dev->data->port_id);
276                 goto error;
277         }
278         cmsg = CMSG_FIRSTHDR(&msg);
279         if (cmsg == NULL) {
280                 rte_errno = EINVAL;
281                 DRV_LOG(DEBUG, "port %u cannot get first message",
282                         dev->data->port_id);
283                 goto error;
284         }
285         cmsg->cmsg_level = SOL_SOCKET;
286         cmsg->cmsg_type = SCM_CREDENTIALS;
287         cmsg->cmsg_len = CMSG_LEN(sizeof(*cred));
288         cred = (struct ucred *)CMSG_DATA(cmsg);
289         if (cred == NULL) {
290                 rte_errno = EINVAL;
291                 DRV_LOG(DEBUG, "port %u no credentials received",
292                         dev->data->port_id);
293                 goto error;
294         }
295         cred->pid = getpid();
296         cred->uid = getuid();
297         cred->gid = getgid();
298         ret = sendmsg(socket_fd, &msg, MSG_DONTWAIT);
299         if (ret < 0) {
300                 rte_errno = errno;
301                 DRV_LOG(WARNING,
302                         "port %u cannot send credentials to primary: %s",
303                         dev->data->port_id, strerror(errno));
304                 goto error;
305         }
306         ret = recvmsg(socket_fd, &msg, MSG_WAITALL);
307         if (ret <= 0) {
308                 rte_errno = errno;
309                 DRV_LOG(WARNING, "port %u no message from primary: %s",
310                         dev->data->port_id, strerror(errno));
311                 goto error;
312         }
313         cmsg = CMSG_FIRSTHDR(&msg);
314         if (cmsg == NULL) {
315                 rte_errno = EINVAL;
316                 DRV_LOG(WARNING, "port %u no file descriptor received",
317                         dev->data->port_id);
318                 goto error;
319         }
320         fd = (int *)CMSG_DATA(cmsg);
321         if (*fd < 0) {
322                 DRV_LOG(WARNING, "port %u no file descriptor received: %s",
323                         dev->data->port_id, strerror(errno));
324                 rte_errno = *fd;
325                 goto error;
326         }
327         ret = *fd;
328         close(socket_fd);
329         return ret;
330 error:
331         if (socket_fd != -1)
332                 close(socket_fd);
333         return -rte_errno;
334 }