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