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