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