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_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
nfp_register_cpp_service(struct nfp_cpp * cpp)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
nfp_cpp_bridge_serve_write(int sockfd,struct nfp_cpp * cpp)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;
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 nfp_cpp_area_release(area);
139 nfp_cpp_area_free(area);
140
141 count -= pos;
142 curlen = (count > NFP_CPP_MEMIO_BOUNDARY) ?
143 NFP_CPP_MEMIO_BOUNDARY : count;
144 }
145
146 return 0;
147 }
148
149 /*
150 * Serving a read request to NFP from host programs. The request
151 * sends the read size and the CPP target. The bridge makes use
152 * of CPP interface handler configured by the PMD setup. The read
153 * data is sent to the requester using the same socket.
154 */
155 static int
nfp_cpp_bridge_serve_read(int sockfd,struct nfp_cpp * cpp)156 nfp_cpp_bridge_serve_read(int sockfd, struct nfp_cpp *cpp)
157 {
158 struct nfp_cpp_area *area;
159 off_t offset, nfp_offset;
160 uint32_t cpp_id, pos, len;
161 uint32_t tmpbuf[16];
162 size_t count, curlen;
163 int err = 0;
164
165 PMD_CPP_LOG(DEBUG, "%s: offset size %zu, count_size: %zu\n", __func__,
166 sizeof(off_t), sizeof(size_t));
167
168 /* Reading the count param */
169 err = recv(sockfd, &count, sizeof(off_t), 0);
170 if (err != sizeof(off_t))
171 return -EINVAL;
172
173 curlen = count;
174
175 /* Reading the offset param */
176 err = recv(sockfd, &offset, sizeof(off_t), 0);
177 if (err != sizeof(off_t))
178 return -EINVAL;
179
180 /* Obtain target's CPP ID and offset in target */
181 cpp_id = (offset >> 40) << 8;
182 nfp_offset = offset & ((1ull << 40) - 1);
183
184 PMD_CPP_LOG(DEBUG, "%s: count %zu and offset %jd\n", __func__, count,
185 offset);
186 PMD_CPP_LOG(DEBUG, "%s: cpp_id %08x and nfp_offset %jd\n", __func__,
187 cpp_id, nfp_offset);
188
189 /* Adjust length if not aligned */
190 if (((nfp_offset + (off_t)count - 1) & ~(NFP_CPP_MEMIO_BOUNDARY - 1)) !=
191 (nfp_offset & ~(NFP_CPP_MEMIO_BOUNDARY - 1))) {
192 curlen = NFP_CPP_MEMIO_BOUNDARY -
193 (nfp_offset & (NFP_CPP_MEMIO_BOUNDARY - 1));
194 }
195
196 while (count > 0) {
197 area = nfp_cpp_area_alloc_with_name(cpp, cpp_id, "nfp.cdev",
198 nfp_offset, curlen);
199 if (!area) {
200 RTE_LOG(ERR, PMD, "%s: area alloc failed\n", __func__);
201 return -EIO;
202 }
203
204 err = nfp_cpp_area_acquire(area);
205 if (err < 0) {
206 RTE_LOG(ERR, PMD, "area acquire failed\n");
207 nfp_cpp_area_free(area);
208 return -EIO;
209 }
210
211 for (pos = 0; pos < curlen; pos += len) {
212 len = curlen - pos;
213 if (len > sizeof(tmpbuf))
214 len = sizeof(tmpbuf);
215
216 err = nfp_cpp_area_read(area, pos, tmpbuf, len);
217 if (err < 0) {
218 RTE_LOG(ERR, PMD, "nfp_cpp_area_read error\n");
219 nfp_cpp_area_release(area);
220 nfp_cpp_area_free(area);
221 return -EIO;
222 }
223 PMD_CPP_LOG(DEBUG, "%s: sending %u of %zu\n", __func__,
224 len, count);
225
226 err = send(sockfd, tmpbuf, len, 0);
227 if (err != (int)len) {
228 RTE_LOG(ERR, PMD,
229 "%s: error when sending: %d of %zu\n",
230 __func__, err, count);
231 nfp_cpp_area_release(area);
232 nfp_cpp_area_free(area);
233 return -EIO;
234 }
235 }
236
237 nfp_offset += pos;
238 nfp_cpp_area_release(area);
239 nfp_cpp_area_free(area);
240
241 count -= pos;
242 curlen = (count > NFP_CPP_MEMIO_BOUNDARY) ?
243 NFP_CPP_MEMIO_BOUNDARY : count;
244 }
245 return 0;
246 }
247
248 /*
249 * Serving a ioctl command from host NFP tools. This usually goes to
250 * a kernel driver char driver but it is not available when the PF is
251 * bound to the PMD. Currently just one ioctl command is served and it
252 * does not require any CPP access at all.
253 */
254 static int
nfp_cpp_bridge_serve_ioctl(int sockfd,struct nfp_cpp * cpp)255 nfp_cpp_bridge_serve_ioctl(int sockfd, struct nfp_cpp *cpp)
256 {
257 uint32_t cmd, ident_size, tmp;
258 int err;
259
260 /* Reading now the IOCTL command */
261 err = recv(sockfd, &cmd, 4, 0);
262 if (err != 4) {
263 RTE_LOG(ERR, PMD, "%s: read error from socket\n", __func__);
264 return -EIO;
265 }
266
267 /* Only supporting NFP_IOCTL_CPP_IDENTIFICATION */
268 if (cmd != NFP_IOCTL_CPP_IDENTIFICATION) {
269 RTE_LOG(ERR, PMD, "%s: unknown cmd %d\n", __func__, cmd);
270 return -EINVAL;
271 }
272
273 err = recv(sockfd, &ident_size, 4, 0);
274 if (err != 4) {
275 RTE_LOG(ERR, PMD, "%s: read error from socket\n", __func__);
276 return -EIO;
277 }
278
279 tmp = nfp_cpp_model(cpp);
280
281 PMD_CPP_LOG(DEBUG, "%s: sending NFP model %08x\n", __func__, tmp);
282
283 err = send(sockfd, &tmp, 4, 0);
284 if (err != 4) {
285 RTE_LOG(ERR, PMD, "%s: error writing to socket\n", __func__);
286 return -EIO;
287 }
288
289 tmp = cpp->interface;
290
291 PMD_CPP_LOG(DEBUG, "%s: sending NFP interface %08x\n", __func__, tmp);
292
293 err = send(sockfd, &tmp, 4, 0);
294 if (err != 4) {
295 RTE_LOG(ERR, PMD, "%s: error writing to socket\n", __func__);
296 return -EIO;
297 }
298
299 return 0;
300 }
301
302 /*
303 * This is the code to be executed by a service core. The CPP bridge interface
304 * is based on a unix socket and requests usually received by a kernel char
305 * driver, read, write and ioctl, are handled by the CPP bridge. NFP host tools
306 * can be executed with a wrapper library and LD_LIBRARY being completely
307 * unaware of the CPP bridge performing the NFP kernel char driver for CPP
308 * accesses.
309 */
310 int32_t
nfp_cpp_bridge_service_func(void * args)311 nfp_cpp_bridge_service_func(void *args)
312 {
313 struct sockaddr address;
314 struct nfp_cpp *cpp = args;
315 int sockfd, datafd, op, ret;
316
317 unlink("/tmp/nfp_cpp");
318 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
319 if (sockfd < 0) {
320 RTE_LOG(ERR, PMD, "%s: socket creation error. Service failed\n",
321 __func__);
322 return -EIO;
323 }
324
325 memset(&address, 0, sizeof(struct sockaddr));
326
327 address.sa_family = AF_UNIX;
328 strcpy(address.sa_data, "/tmp/nfp_cpp");
329
330 ret = bind(sockfd, (const struct sockaddr *)&address,
331 sizeof(struct sockaddr));
332 if (ret < 0) {
333 RTE_LOG(ERR, PMD, "%s: bind error (%d). Service failed\n",
334 __func__, errno);
335 close(sockfd);
336 return ret;
337 }
338
339 ret = listen(sockfd, 20);
340 if (ret < 0) {
341 RTE_LOG(ERR, PMD, "%s: listen error(%d). Service failed\n",
342 __func__, errno);
343 close(sockfd);
344 return ret;
345 }
346
347 for (;;) {
348 datafd = accept(sockfd, NULL, NULL);
349 if (datafd < 0) {
350 RTE_LOG(ERR, PMD, "%s: accept call error (%d)\n",
351 __func__, errno);
352 RTE_LOG(ERR, PMD, "%s: service failed\n", __func__);
353 close(sockfd);
354 return -EIO;
355 }
356
357 while (1) {
358 ret = recv(datafd, &op, 4, 0);
359 if (ret <= 0) {
360 PMD_CPP_LOG(DEBUG, "%s: socket close\n",
361 __func__);
362 break;
363 }
364
365 PMD_CPP_LOG(DEBUG, "%s: getting op %u\n", __func__, op);
366
367 if (op == NFP_BRIDGE_OP_READ)
368 nfp_cpp_bridge_serve_read(datafd, cpp);
369
370 if (op == NFP_BRIDGE_OP_WRITE)
371 nfp_cpp_bridge_serve_write(datafd, cpp);
372
373 if (op == NFP_BRIDGE_OP_IOCTL)
374 nfp_cpp_bridge_serve_ioctl(datafd, cpp);
375
376 if (op == 0)
377 break;
378 }
379 close(datafd);
380 }
381 close(sockfd);
382
383 return 0;
384 }
385 /*
386 * Local variables:
387 * c-file-style: "Linux"
388 * indent-tabs-mode: t
389 * End:
390 */
391