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