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