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