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