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