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