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