a3a5229112ff644ce6da078972c6a74acc2a83c5
[dpdk.git] / drivers / net / mlx5 / mlx5_socket.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016 6WIND S.A.
3  * Copyright 2016 Mellanox Technologies, Ltd
4  */
5
6 #define _GNU_SOURCE
7
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/un.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <sys/stat.h>
15
16 #include "mlx5.h"
17 #include "mlx5_utils.h"
18
19 /**
20  * Initialise the socket to communicate with the secondary process
21  *
22  * @param[in] dev
23  *   Pointer to Ethernet device.
24  *
25  * @return
26  *   0 on success, a negative errno value otherwise and rte_errno is set.
27  */
28 int
29 mlx5_socket_init(struct rte_eth_dev *dev)
30 {
31         struct priv *priv = dev->data->dev_private;
32         struct sockaddr_un sun = {
33                 .sun_family = AF_UNIX,
34         };
35         int ret;
36         int flags;
37
38         /*
39          * Close the last socket that was used to communicate
40          * with the secondary process
41          */
42         if (priv->primary_socket)
43                 mlx5_socket_uninit(dev);
44         /*
45          * Initialise the socket to communicate with the secondary
46          * process.
47          */
48         ret = socket(AF_UNIX, SOCK_STREAM, 0);
49         if (ret < 0) {
50                 rte_errno = errno;
51                 DRV_LOG(WARNING, "port %u secondary process not supported: %s",
52                         dev->data->port_id, strerror(errno));
53                 goto error;
54         }
55         priv->primary_socket = ret;
56         flags = fcntl(priv->primary_socket, F_GETFL, 0);
57         if (flags == -1) {
58                 rte_errno = errno;
59                 goto error;
60         }
61         ret = fcntl(priv->primary_socket, F_SETFL, flags | O_NONBLOCK);
62         if (ret < 0) {
63                 rte_errno = errno;
64                 goto error;
65         }
66         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
67                  MLX5_DRIVER_NAME, priv->primary_socket);
68         remove(sun.sun_path);
69         ret = bind(priv->primary_socket, (const struct sockaddr *)&sun,
70                    sizeof(sun));
71         if (ret < 0) {
72                 rte_errno = errno;
73                 DRV_LOG(WARNING,
74                         "port %u cannot bind socket, secondary process not"
75                         " supported: %s",
76                         dev->data->port_id, strerror(errno));
77                 goto close;
78         }
79         ret = listen(priv->primary_socket, 0);
80         if (ret < 0) {
81                 rte_errno = errno;
82                 DRV_LOG(WARNING, "port %u secondary process not supported: %s",
83                         dev->data->port_id, strerror(errno));
84                 goto close;
85         }
86         return 0;
87 close:
88         remove(sun.sun_path);
89 error:
90         claim_zero(close(priv->primary_socket));
91         priv->primary_socket = 0;
92         return -rte_errno;
93 }
94
95 /**
96  * Un-Initialise the socket to communicate with the secondary process
97  *
98  * @param[in] dev
99  */
100 void
101 mlx5_socket_uninit(struct rte_eth_dev *dev)
102 {
103         struct priv *priv = dev->data->dev_private;
104
105         MKSTR(path, "/var/tmp/%s_%d", MLX5_DRIVER_NAME, priv->primary_socket);
106         claim_zero(close(priv->primary_socket));
107         priv->primary_socket = 0;
108         claim_zero(remove(path));
109 }
110
111 /**
112  * Handle socket interrupts.
113  *
114  * @param dev
115  *   Pointer to Ethernet device.
116  */
117 void
118 mlx5_socket_handle(struct rte_eth_dev *dev)
119 {
120         struct priv *priv = dev->data->dev_private;
121         int conn_sock;
122         int ret = 0;
123         struct cmsghdr *cmsg = NULL;
124         struct ucred *cred = NULL;
125         char buf[CMSG_SPACE(sizeof(struct ucred))] = { 0 };
126         char vbuf[1024] = { 0 };
127         struct iovec io = {
128                 .iov_base = vbuf,
129                 .iov_len = sizeof(*vbuf),
130         };
131         struct msghdr msg = {
132                 .msg_iov = &io,
133                 .msg_iovlen = 1,
134                 .msg_control = buf,
135                 .msg_controllen = sizeof(buf),
136         };
137         int *fd;
138
139         /* Accept the connection from the client. */
140         conn_sock = accept(priv->primary_socket, NULL, NULL);
141         if (conn_sock < 0) {
142                 DRV_LOG(WARNING, "port %u connection failed: %s",
143                         dev->data->port_id, strerror(errno));
144                 return;
145         }
146         ret = setsockopt(conn_sock, SOL_SOCKET, SO_PASSCRED, &(int){1},
147                                          sizeof(int));
148         if (ret < 0) {
149                 ret = errno;
150                 DRV_LOG(WARNING, "port %u cannot change socket options: %s",
151                         dev->data->port_id, strerror(rte_errno));
152                 goto error;
153         }
154         ret = recvmsg(conn_sock, &msg, MSG_WAITALL);
155         if (ret < 0) {
156                 ret = errno;
157                 DRV_LOG(WARNING, "port %u received an empty message: %s",
158                         dev->data->port_id, strerror(rte_errno));
159                 goto error;
160         }
161         /* Expect to receive credentials only. */
162         cmsg = CMSG_FIRSTHDR(&msg);
163         if (cmsg == NULL) {
164                 DRV_LOG(WARNING, "port %u no message", dev->data->port_id);
165                 goto error;
166         }
167         if ((cmsg->cmsg_type == SCM_CREDENTIALS) &&
168                 (cmsg->cmsg_len >= sizeof(*cred))) {
169                 cred = (struct ucred *)CMSG_DATA(cmsg);
170                 assert(cred != NULL);
171         }
172         cmsg = CMSG_NXTHDR(&msg, cmsg);
173         if (cmsg != NULL) {
174                 DRV_LOG(WARNING, "port %u message wrongly formatted",
175                         dev->data->port_id);
176                 goto error;
177         }
178         /* Make sure all the ancillary data was received and valid. */
179         if ((cred == NULL) || (cred->uid != getuid()) ||
180             (cred->gid != getgid())) {
181                 DRV_LOG(WARNING, "port %u wrong credentials",
182                         dev->data->port_id);
183                 goto error;
184         }
185         /* Set-up the ancillary data. */
186         cmsg = CMSG_FIRSTHDR(&msg);
187         assert(cmsg != NULL);
188         cmsg->cmsg_level = SOL_SOCKET;
189         cmsg->cmsg_type = SCM_RIGHTS;
190         cmsg->cmsg_len = CMSG_LEN(sizeof(priv->ctx->cmd_fd));
191         fd = (int *)CMSG_DATA(cmsg);
192         *fd = priv->ctx->cmd_fd;
193         ret = sendmsg(conn_sock, &msg, 0);
194         if (ret < 0)
195                 DRV_LOG(WARNING, "port %u cannot send response",
196                         dev->data->port_id);
197 error:
198         close(conn_sock);
199 }
200
201 /**
202  * Connect to the primary process.
203  *
204  * @param[in] dev
205  *   Pointer to Ethernet structure.
206  *
207  * @return
208  *   fd on success, negative errno value otherwise and rte_errno is set.
209  */
210 int
211 mlx5_socket_connect(struct rte_eth_dev *dev)
212 {
213         struct priv *priv = dev->data->dev_private;
214         struct sockaddr_un sun = {
215                 .sun_family = AF_UNIX,
216         };
217         int socket_fd = -1;
218         int *fd = NULL;
219         int ret;
220         struct ucred *cred;
221         char buf[CMSG_SPACE(sizeof(*cred))] = { 0 };
222         char vbuf[1024] = { 0 };
223         struct iovec io = {
224                 .iov_base = vbuf,
225                 .iov_len = sizeof(*vbuf),
226         };
227         struct msghdr msg = {
228                 .msg_control = buf,
229                 .msg_controllen = sizeof(buf),
230                 .msg_iov = &io,
231                 .msg_iovlen = 1,
232         };
233         struct cmsghdr *cmsg;
234
235         ret = socket(AF_UNIX, SOCK_STREAM, 0);
236         if (ret < 0) {
237                 rte_errno = errno;
238                 DRV_LOG(WARNING, "port %u cannot connect to primary",
239                         dev->data->port_id);
240                 goto error;
241         }
242         socket_fd = ret;
243         snprintf(sun.sun_path, sizeof(sun.sun_path), "/var/tmp/%s_%d",
244                  MLX5_DRIVER_NAME, priv->primary_socket);
245         ret = connect(socket_fd, (const struct sockaddr *)&sun, sizeof(sun));
246         if (ret < 0) {
247                 rte_errno = errno;
248                 DRV_LOG(WARNING, "port %u cannot connect to primary",
249                         dev->data->port_id);
250                 goto error;
251         }
252         cmsg = CMSG_FIRSTHDR(&msg);
253         if (cmsg == NULL) {
254                 rte_errno = EINVAL;
255                 DRV_LOG(DEBUG, "port %u cannot get first message",
256                         dev->data->port_id);
257                 goto error;
258         }
259         cmsg->cmsg_level = SOL_SOCKET;
260         cmsg->cmsg_type = SCM_CREDENTIALS;
261         cmsg->cmsg_len = CMSG_LEN(sizeof(*cred));
262         cred = (struct ucred *)CMSG_DATA(cmsg);
263         if (cred == NULL) {
264                 rte_errno = EINVAL;
265                 DRV_LOG(DEBUG, "port %u no credentials received",
266                         dev->data->port_id);
267                 goto error;
268         }
269         cred->pid = getpid();
270         cred->uid = getuid();
271         cred->gid = getgid();
272         ret = sendmsg(socket_fd, &msg, MSG_DONTWAIT);
273         if (ret < 0) {
274                 rte_errno = errno;
275                 DRV_LOG(WARNING,
276                         "port %u cannot send credentials to primary: %s",
277                         dev->data->port_id, strerror(errno));
278                 goto error;
279         }
280         ret = recvmsg(socket_fd, &msg, MSG_WAITALL);
281         if (ret <= 0) {
282                 rte_errno = errno;
283                 DRV_LOG(WARNING, "port %u no message from primary: %s",
284                         dev->data->port_id, strerror(errno));
285                 goto error;
286         }
287         cmsg = CMSG_FIRSTHDR(&msg);
288         if (cmsg == NULL) {
289                 rte_errno = EINVAL;
290                 DRV_LOG(WARNING, "port %u no file descriptor received",
291                         dev->data->port_id);
292                 goto error;
293         }
294         fd = (int *)CMSG_DATA(cmsg);
295         if (*fd < 0) {
296                 DRV_LOG(WARNING, "port %u no file descriptor received: %s",
297                         dev->data->port_id, strerror(errno));
298                 rte_errno = *fd;
299                 goto error;
300         }
301         ret = *fd;
302         close(socket_fd);
303         return ret;
304 error:
305         if (socket_fd != -1)
306                 close(socket_fd);
307         return -rte_errno;
308 }