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