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