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