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