d9167933380f93a484e44ccbcbfbe36cba219984
[dpdk.git] / drivers / net / nfp / nfp_cpp_bridge.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2014-2021 Netronome Systems, Inc.
3  * All rights reserved.
4  *
5  * Small portions derived from code Copyright(c) 2010-2015 Intel Corporation.
6  */
7
8 /*
9  * vim:shiftwidth=8:noexpandtab
10  *
11  * @file dpdk/pmd/nfp_cpp_bridge.c
12  *
13  * Netronome vNIC DPDK Poll-Mode Driver: CPP Bridge
14  */
15
16 #include <rte_service_component.h>
17
18 #include "nfpcore/nfp_cpp.h"
19 #include "nfpcore/nfp_mip.h"
20 #include "nfpcore/nfp_nsp.h"
21
22 #include "nfp_net_logs.h"
23 #include "nfp_cpp_bridge.h"
24
25 #include <sys/ioctl.h>
26
27 /* Prototypes */
28 static int nfp_cpp_bridge_serve_write(int sockfd, struct nfp_cpp *cpp);
29 static int nfp_cpp_bridge_serve_read(int sockfd, struct nfp_cpp *cpp);
30 static int nfp_cpp_bridge_serve_ioctl(int sockfd, struct nfp_cpp *cpp);
31
32 void nfp_register_cpp_service(struct nfp_cpp *cpp)
33 {
34         uint32_t *cpp_service_id = NULL;
35         struct rte_service_spec service;
36
37         memset(&service, 0, sizeof(struct rte_service_spec));
38         snprintf(service.name, sizeof(service.name), "nfp_cpp_service");
39         service.callback = nfp_cpp_bridge_service_func;
40         service.callback_userdata = (void *)cpp;
41
42         if (rte_service_component_register(&service,
43                                            cpp_service_id))
44                 RTE_LOG(WARNING, PMD, "NFP CPP bridge service register() failed");
45         else
46                 RTE_LOG(DEBUG, PMD, "NFP CPP bridge service registered");
47 }
48
49 /*
50  * Serving a write request to NFP from host programs. The request
51  * sends the write size and the CPP target. The bridge makes use
52  * of CPP interface handler configured by the PMD setup.
53  */
54 static int
55 nfp_cpp_bridge_serve_write(int sockfd, struct nfp_cpp *cpp)
56 {
57         struct nfp_cpp_area *area;
58         off_t offset, nfp_offset;
59         uint32_t cpp_id, pos, len;
60         uint32_t tmpbuf[16];
61         size_t count, curlen, totlen = 0;
62         int err = 0;
63
64         PMD_CPP_LOG(DEBUG, "%s: offset size %zu, count_size: %zu\n", __func__,
65                 sizeof(off_t), sizeof(size_t));
66
67         /* Reading the count param */
68         err = recv(sockfd, &count, sizeof(off_t), 0);
69         if (err != sizeof(off_t))
70                 return -EINVAL;
71
72         curlen = count;
73
74         /* Reading the offset param */
75         err = recv(sockfd, &offset, sizeof(off_t), 0);
76         if (err != sizeof(off_t))
77                 return -EINVAL;
78
79         /* Obtain target's CPP ID and offset in target */
80         cpp_id = (offset >> 40) << 8;
81         nfp_offset = offset & ((1ull << 40) - 1);
82
83         PMD_CPP_LOG(DEBUG, "%s: count %zu and offset %jd\n", __func__, count,
84                 offset);
85         PMD_CPP_LOG(DEBUG, "%s: cpp_id %08x and nfp_offset %jd\n", __func__,
86                 cpp_id, nfp_offset);
87
88         /* Adjust length if not aligned */
89         if (((nfp_offset + (off_t)count - 1) & ~(NFP_CPP_MEMIO_BOUNDARY - 1)) !=
90             (nfp_offset & ~(NFP_CPP_MEMIO_BOUNDARY - 1))) {
91                 curlen = NFP_CPP_MEMIO_BOUNDARY -
92                         (nfp_offset & (NFP_CPP_MEMIO_BOUNDARY - 1));
93         }
94
95         while (count > 0) {
96                 /* configure a CPP PCIe2CPP BAR for mapping the CPP target */
97                 area = nfp_cpp_area_alloc_with_name(cpp, cpp_id, "nfp.cdev",
98                                                     nfp_offset, curlen);
99                 if (!area) {
100                         RTE_LOG(ERR, PMD, "%s: area alloc fail\n", __func__);
101                         return -EIO;
102                 }
103
104                 /* mapping the target */
105                 err = nfp_cpp_area_acquire(area);
106                 if (err < 0) {
107                         RTE_LOG(ERR, PMD, "area acquire failed\n");
108                         nfp_cpp_area_free(area);
109                         return -EIO;
110                 }
111
112                 for (pos = 0; pos < curlen; pos += len) {
113                         len = curlen - pos;
114                         if (len > sizeof(tmpbuf))
115                                 len = sizeof(tmpbuf);
116
117                         PMD_CPP_LOG(DEBUG, "%s: Receive %u of %zu\n", __func__,
118                                            len, count);
119                         err = recv(sockfd, tmpbuf, len, MSG_WAITALL);
120                         if (err != (int)len) {
121                                 RTE_LOG(ERR, PMD,
122                                         "%s: error when receiving, %d of %zu\n",
123                                         __func__, err, count);
124                                 nfp_cpp_area_release(area);
125                                 nfp_cpp_area_free(area);
126                                 return -EIO;
127                         }
128                         err = nfp_cpp_area_write(area, pos, tmpbuf, len);
129                         if (err < 0) {
130                                 RTE_LOG(ERR, PMD, "nfp_cpp_area_write error\n");
131                                 nfp_cpp_area_release(area);
132                                 nfp_cpp_area_free(area);
133                                 return -EIO;
134                         }
135                 }
136
137                 nfp_offset += pos;
138                 totlen += pos;
139                 nfp_cpp_area_release(area);
140                 nfp_cpp_area_free(area);
141
142                 count -= pos;
143                 curlen = (count > NFP_CPP_MEMIO_BOUNDARY) ?
144                          NFP_CPP_MEMIO_BOUNDARY : count;
145         }
146
147         return 0;
148 }
149
150 /*
151  * Serving a read request to NFP from host programs. The request
152  * sends the read size and the CPP target. The bridge makes use
153  * of CPP interface handler configured by the PMD setup. The read
154  * data is sent to the requester using the same socket.
155  */
156 static int
157 nfp_cpp_bridge_serve_read(int sockfd, struct nfp_cpp *cpp)
158 {
159         struct nfp_cpp_area *area;
160         off_t offset, nfp_offset;
161         uint32_t cpp_id, pos, len;
162         uint32_t tmpbuf[16];
163         size_t count, curlen, totlen = 0;
164         int err = 0;
165
166         PMD_CPP_LOG(DEBUG, "%s: offset size %zu, count_size: %zu\n", __func__,
167                 sizeof(off_t), sizeof(size_t));
168
169         /* Reading the count param */
170         err = recv(sockfd, &count, sizeof(off_t), 0);
171         if (err != sizeof(off_t))
172                 return -EINVAL;
173
174         curlen = count;
175
176         /* Reading the offset param */
177         err = recv(sockfd, &offset, sizeof(off_t), 0);
178         if (err != sizeof(off_t))
179                 return -EINVAL;
180
181         /* Obtain target's CPP ID and offset in target */
182         cpp_id = (offset >> 40) << 8;
183         nfp_offset = offset & ((1ull << 40) - 1);
184
185         PMD_CPP_LOG(DEBUG, "%s: count %zu and offset %jd\n", __func__, count,
186                            offset);
187         PMD_CPP_LOG(DEBUG, "%s: cpp_id %08x and nfp_offset %jd\n", __func__,
188                            cpp_id, nfp_offset);
189
190         /* Adjust length if not aligned */
191         if (((nfp_offset + (off_t)count - 1) & ~(NFP_CPP_MEMIO_BOUNDARY - 1)) !=
192             (nfp_offset & ~(NFP_CPP_MEMIO_BOUNDARY - 1))) {
193                 curlen = NFP_CPP_MEMIO_BOUNDARY -
194                         (nfp_offset & (NFP_CPP_MEMIO_BOUNDARY - 1));
195         }
196
197         while (count > 0) {
198                 area = nfp_cpp_area_alloc_with_name(cpp, cpp_id, "nfp.cdev",
199                                                     nfp_offset, curlen);
200                 if (!area) {
201                         RTE_LOG(ERR, PMD, "%s: area alloc failed\n", __func__);
202                         return -EIO;
203                 }
204
205                 err = nfp_cpp_area_acquire(area);
206                 if (err < 0) {
207                         RTE_LOG(ERR, PMD, "area acquire failed\n");
208                         nfp_cpp_area_free(area);
209                         return -EIO;
210                 }
211
212                 for (pos = 0; pos < curlen; pos += len) {
213                         len = curlen - pos;
214                         if (len > sizeof(tmpbuf))
215                                 len = sizeof(tmpbuf);
216
217                         err = nfp_cpp_area_read(area, pos, tmpbuf, len);
218                         if (err < 0) {
219                                 RTE_LOG(ERR, PMD, "nfp_cpp_area_read error\n");
220                                 nfp_cpp_area_release(area);
221                                 nfp_cpp_area_free(area);
222                                 return -EIO;
223                         }
224                         PMD_CPP_LOG(DEBUG, "%s: sending %u of %zu\n", __func__,
225                                            len, count);
226
227                         err = send(sockfd, tmpbuf, len, 0);
228                         if (err != (int)len) {
229                                 RTE_LOG(ERR, PMD,
230                                         "%s: error when sending: %d of %zu\n",
231                                         __func__, err, count);
232                                 nfp_cpp_area_release(area);
233                                 nfp_cpp_area_free(area);
234                                 return -EIO;
235                         }
236                 }
237
238                 nfp_offset += pos;
239                 totlen += pos;
240                 nfp_cpp_area_release(area);
241                 nfp_cpp_area_free(area);
242
243                 count -= pos;
244                 curlen = (count > NFP_CPP_MEMIO_BOUNDARY) ?
245                         NFP_CPP_MEMIO_BOUNDARY : count;
246         }
247         return 0;
248 }
249
250 /*
251  * Serving a ioctl command from host NFP tools. This usually goes to
252  * a kernel driver char driver but it is not available when the PF is
253  * bound to the PMD. Currently just one ioctl command is served and it
254  * does not require any CPP access at all.
255  */
256 static int
257 nfp_cpp_bridge_serve_ioctl(int sockfd, struct nfp_cpp *cpp)
258 {
259         uint32_t cmd, ident_size, tmp;
260         int err;
261
262         /* Reading now the IOCTL command */
263         err = recv(sockfd, &cmd, 4, 0);
264         if (err != 4) {
265                 RTE_LOG(ERR, PMD, "%s: read error from socket\n", __func__);
266                 return -EIO;
267         }
268
269         /* Only supporting NFP_IOCTL_CPP_IDENTIFICATION */
270         if (cmd != NFP_IOCTL_CPP_IDENTIFICATION) {
271                 RTE_LOG(ERR, PMD, "%s: unknown cmd %d\n", __func__, cmd);
272                 return -EINVAL;
273         }
274
275         err = recv(sockfd, &ident_size, 4, 0);
276         if (err != 4) {
277                 RTE_LOG(ERR, PMD, "%s: read error from socket\n", __func__);
278                 return -EIO;
279         }
280
281         tmp = nfp_cpp_model(cpp);
282
283         PMD_CPP_LOG(DEBUG, "%s: sending NFP model %08x\n", __func__, tmp);
284
285         err = send(sockfd, &tmp, 4, 0);
286         if (err != 4) {
287                 RTE_LOG(ERR, PMD, "%s: error writing to socket\n", __func__);
288                 return -EIO;
289         }
290
291         tmp = cpp->interface;
292
293         PMD_CPP_LOG(DEBUG, "%s: sending NFP interface %08x\n", __func__, tmp);
294
295         err = send(sockfd, &tmp, 4, 0);
296         if (err != 4) {
297                 RTE_LOG(ERR, PMD, "%s: error writing to socket\n", __func__);
298                 return -EIO;
299         }
300
301         return 0;
302 }
303
304 /*
305  * This is the code to be executed by a service core. The CPP bridge interface
306  * is based on a unix socket and requests usually received by a kernel char
307  * driver, read, write and ioctl, are handled by the CPP bridge. NFP host tools
308  * can be executed with a wrapper library and LD_LIBRARY being completely
309  * unaware of the CPP bridge performing the NFP kernel char driver for CPP
310  * accesses.
311  */
312 int32_t
313 nfp_cpp_bridge_service_func(void *args)
314 {
315         struct sockaddr address;
316         struct nfp_cpp *cpp = args;
317         int sockfd, datafd, op, ret;
318
319         unlink("/tmp/nfp_cpp");
320         sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
321         if (sockfd < 0) {
322                 RTE_LOG(ERR, PMD, "%s: socket creation error. Service failed\n",
323                         __func__);
324                 return -EIO;
325         }
326
327         memset(&address, 0, sizeof(struct sockaddr));
328
329         address.sa_family = AF_UNIX;
330         strcpy(address.sa_data, "/tmp/nfp_cpp");
331
332         ret = bind(sockfd, (const struct sockaddr *)&address,
333                    sizeof(struct sockaddr));
334         if (ret < 0) {
335                 RTE_LOG(ERR, PMD, "%s: bind error (%d). Service failed\n",
336                                   __func__, errno);
337                 close(sockfd);
338                 return ret;
339         }
340
341         ret = listen(sockfd, 20);
342         if (ret < 0) {
343                 RTE_LOG(ERR, PMD, "%s: listen error(%d). Service failed\n",
344                                   __func__, errno);
345                 close(sockfd);
346                 return ret;
347         }
348
349         for (;;) {
350                 datafd = accept(sockfd, NULL, NULL);
351                 if (datafd < 0) {
352                         RTE_LOG(ERR, PMD, "%s: accept call error (%d)\n",
353                                           __func__, errno);
354                         RTE_LOG(ERR, PMD, "%s: service failed\n", __func__);
355                         close(sockfd);
356                         return -EIO;
357                 }
358
359                 while (1) {
360                         ret = recv(datafd, &op, 4, 0);
361                         if (ret <= 0) {
362                                 PMD_CPP_LOG(DEBUG, "%s: socket close\n",
363                                                    __func__);
364                                 break;
365                         }
366
367                         PMD_CPP_LOG(DEBUG, "%s: getting op %u\n", __func__, op);
368
369                         if (op == NFP_BRIDGE_OP_READ)
370                                 nfp_cpp_bridge_serve_read(datafd, cpp);
371
372                         if (op == NFP_BRIDGE_OP_WRITE)
373                                 nfp_cpp_bridge_serve_write(datafd, cpp);
374
375                         if (op == NFP_BRIDGE_OP_IOCTL)
376                                 nfp_cpp_bridge_serve_ioctl(datafd, cpp);
377
378                         if (op == 0)
379                                 break;
380                 }
381                 close(datafd);
382         }
383         close(sockfd);
384
385         return 0;
386 }
387 /*
388  * Local variables:
389  * c-file-style: "Linux"
390  * indent-tabs-mode: t
391  * End:
392  */