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