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 PMD_CPP_LOG(ERR, "area alloc fail"); 166 return -EIO; 167 } 168 169 /* mapping the target */ 170 err = nfp_cpp_area_acquire(area); 171 if (err < 0) { 172 PMD_CPP_LOG(ERR, "area acquire failed"); 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 PMD_CPP_LOG(ERR, 187 "error when receiving, %d of %zu", 188 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 PMD_CPP_LOG(ERR, "nfp_cpp_area_write error"); 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 PMD_CPP_LOG(ERR, "area alloc failed"); 266 return -EIO; 267 } 268 269 err = nfp_cpp_area_acquire(area); 270 if (err < 0) { 271 PMD_CPP_LOG(ERR, "area acquire failed"); 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 PMD_CPP_LOG(ERR, "nfp_cpp_area_read error"); 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 PMD_CPP_LOG(ERR, 294 "error when sending: %d of %zu", 295 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 PMD_CPP_LOG(ERR, "read error from socket"); 329 return -EIO; 330 } 331 332 /* Only supporting NFP_IOCTL_CPP_IDENTIFICATION */ 333 if (cmd != NFP_IOCTL_CPP_IDENTIFICATION) { 334 PMD_CPP_LOG(ERR, "unknown cmd %d", cmd); 335 return -EINVAL; 336 } 337 338 err = recv(sockfd, &ident_size, 4, 0); 339 if (err != 4) { 340 PMD_CPP_LOG(ERR, "read error from socket"); 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 PMD_CPP_LOG(ERR, "error writing to socket"); 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 PMD_CPP_LOG(ERR, "error writing to socket"); 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 PMD_CPP_LOG(ERR, "socket creation error. Service failed"); 388 return -EIO; 389 } 390 391 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); 392 393 memset(&address, 0, sizeof(struct sockaddr)); 394 395 address.sa_family = AF_UNIX; 396 strcpy(address.sa_data, "/tmp/nfp_cpp"); 397 398 ret = bind(sockfd, (const struct sockaddr *)&address, 399 sizeof(struct sockaddr)); 400 if (ret < 0) { 401 PMD_CPP_LOG(ERR, "bind error (%d). Service failed", errno); 402 close(sockfd); 403 return ret; 404 } 405 406 ret = listen(sockfd, 20); 407 if (ret < 0) { 408 PMD_CPP_LOG(ERR, "listen error(%d). Service failed", errno); 409 close(sockfd); 410 return ret; 411 } 412 413 pf_dev = args; 414 cpp = pf_dev->cpp; 415 while (rte_service_runstate_get(pf_dev->cpp_bridge_id) != 0) { 416 datafd = accept(sockfd, NULL, NULL); 417 if (datafd < 0) { 418 if (errno == EAGAIN || errno == EWOULDBLOCK) 419 continue; 420 421 PMD_CPP_LOG(ERR, "accept call error (%d)", errno); 422 PMD_CPP_LOG(ERR, "service failed"); 423 close(sockfd); 424 return -EIO; 425 } 426 427 while (1) { 428 ret = recv(datafd, &op, 4, 0); 429 if (ret <= 0) { 430 PMD_CPP_LOG(DEBUG, "%s: socket close\n", 431 __func__); 432 break; 433 } 434 435 PMD_CPP_LOG(DEBUG, "%s: getting op %u\n", __func__, op); 436 437 if (op == NFP_BRIDGE_OP_READ) 438 nfp_cpp_bridge_serve_read(datafd, cpp); 439 440 if (op == NFP_BRIDGE_OP_WRITE) 441 nfp_cpp_bridge_serve_write(datafd, cpp); 442 443 if (op == NFP_BRIDGE_OP_IOCTL) 444 nfp_cpp_bridge_serve_ioctl(datafd, cpp); 445 446 if (op == 0) 447 break; 448 } 449 close(datafd); 450 } 451 close(sockfd); 452 453 return 0; 454 } 455 /* 456 * Local variables: 457 * c-file-style: "Linux" 458 * indent-tabs-mode: t 459 * End: 460 */ 461