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