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