|
5 | 5 | #include <stdio.h> |
6 | 6 | #include <stdlib.h> |
7 | 7 | #include <libgen.h> |
| 8 | +#include <limits.h> |
8 | 9 | #include <stdbool.h> |
9 | 10 | #include <errno.h> |
10 | 11 | #include <unistd.h> |
|
41 | 42 | * pci = 0000:00:0c.4 |
42 | 43 | * Device type = IB |
43 | 44 | * mlx5_0 -> ibp0s12f4 |
| 45 | + * NAME_PCI with sub-function (SF) |
| 46 | + * parent pci = 0000:c1:00.0, sfnum = 88 |
| 47 | + * Device type = RoCE |
| 48 | + * mlx5_5 -> rocep193s0f0S88 |
| 49 | + * NAME_PCI with SF on SR-IOV VF (VF-SF) |
| 50 | + * parent pci = 0000:c1:00.4 (VF of 0000:c1:00.0), sfnum = 99 |
| 51 | + * Device type = RoCE |
| 52 | + * mlx5_6 -> rocep193s0f0v0S99 |
44 | 53 | * NAME_GUID |
45 | 54 | * GUID = 5254:00c0:fe12:3455 |
46 | 55 | * Device type = RoCE |
@@ -245,9 +254,82 @@ struct pci_info { |
245 | 254 | unsigned int func; |
246 | 255 | unsigned int sun; |
247 | 256 | unsigned int vf; |
| 257 | + unsigned int sfnum; |
248 | 258 | bool valid_vf; |
| 259 | + bool valid_sf; |
249 | 260 | }; |
250 | 261 |
|
| 262 | +/* |
| 263 | + * If the ibdev's parent (the "device" symlink) is an auxiliary device that |
| 264 | + * carries a stable 'sfnum' attribute, this is a sub-function (SF) host RDMA |
| 265 | + * device. Read sfnum, then re-point p->pcidev at /sys/bus/pci/devices/<BDF> |
| 266 | + * of the aux device's PCI parent so that the rest of by_pci() (i.e. |
| 267 | + * get_virtfn_info(), fill_pci_info() and the name composition) proceeds |
| 268 | + * unchanged. The PCI parent can be a PF or an SR-IOV VF; both layouts |
| 269 | + * are handled by the same code path. |
| 270 | + */ |
| 271 | +static int detect_sf(struct data *d, struct pci_info *p) |
| 272 | +{ |
| 273 | + char real_path[PATH_MAX]; |
| 274 | + char *sfnum_path = NULL; |
| 275 | + char *device_path = NULL; |
| 276 | + char *last_slash, *bdf; |
| 277 | + unsigned int sfnum; |
| 278 | + FILE *fp; |
| 279 | + int ret; |
| 280 | + |
| 281 | + ret = asprintf(&sfnum_path, |
| 282 | + "/sys/class/infiniband/%s/device/sfnum", d->curr); |
| 283 | + if (ret < 0) |
| 284 | + return -ENOMEM; |
| 285 | + |
| 286 | + fp = fopen(sfnum_path, "r"); |
| 287 | + free(sfnum_path); |
| 288 | + if (!fp) { |
| 289 | + /* Auxiliary parent without 'sfnum'; not an SF we recognize. */ |
| 290 | + pr_dbg("%s: Auxiliary parent has no 'sfnum' attribute\n", |
| 291 | + d->curr); |
| 292 | + return -EINVAL; |
| 293 | + } |
| 294 | + |
| 295 | + if (fscanf(fp, "%u", &sfnum) != 1) { |
| 296 | + fclose(fp); |
| 297 | + return -EINVAL; |
| 298 | + } |
| 299 | + fclose(fp); |
| 300 | + |
| 301 | + ret = asprintf(&device_path, "/sys/class/infiniband/%s/device", |
| 302 | + d->curr); |
| 303 | + if (ret < 0) |
| 304 | + return -ENOMEM; |
| 305 | + |
| 306 | + /* The 'device' symlink target is relative (e.g. "../../../<driver>.sf.<id>") |
| 307 | + * and does not itself encode the PCI BDF. Resolve it to its absolute, |
| 308 | + * canonical form so the path's parent directory exposes the BDF. */ |
| 309 | + if (!realpath(device_path, real_path)) { |
| 310 | + free(device_path); |
| 311 | + return -EINVAL; |
| 312 | + } |
| 313 | + free(device_path); |
| 314 | + |
| 315 | + /* Strip the trailing aux-device component to expose the PCI parent. */ |
| 316 | + last_slash = strrchr(real_path, '/'); |
| 317 | + if (!last_slash || last_slash == real_path) |
| 318 | + return -EINVAL; |
| 319 | + *last_slash = 0; |
| 320 | + |
| 321 | + bdf = basename(real_path); |
| 322 | + ret = asprintf(&p->pcidev, "/sys/bus/pci/devices/%s", bdf); |
| 323 | + if (ret < 0) { |
| 324 | + p->pcidev = NULL; |
| 325 | + return -ENOMEM; |
| 326 | + } |
| 327 | + |
| 328 | + p->valid_sf = true; |
| 329 | + p->sfnum = sfnum; |
| 330 | + return 0; |
| 331 | +} |
| 332 | + |
251 | 333 | static int fill_pci_info(struct data *d, struct pci_info *p) |
252 | 334 | { |
253 | 335 | char buf[256] = {}; |
@@ -379,21 +461,26 @@ static int by_pci(struct data *d) |
379 | 461 | buf[ret] = 0; |
380 | 462 |
|
381 | 463 | subs = basename(buf); |
382 | | - if (strcmp(subs, "pci")) { |
| 464 | + if (!strcmp(subs, "auxiliary")) { |
| 465 | + /* SFs sit one auxiliary hop below the underlying PCI function. */ |
| 466 | + ret = detect_sf(d, &p); |
| 467 | + if (ret) |
| 468 | + goto out; |
| 469 | + } else if (!strcmp(subs, "pci")) { |
| 470 | + ret = asprintf(&p.pcidev, |
| 471 | + "/sys/class/infiniband/%s/device", d->curr); |
| 472 | + if (ret < 0) { |
| 473 | + ret = -ENOMEM; |
| 474 | + p.pcidev = NULL; |
| 475 | + goto out; |
| 476 | + } |
| 477 | + } else { |
383 | 478 | /* Ball out virtual devices */ |
384 | 479 | pr_dbg("%s: Non-PCI device (%s) was detected\n", d->curr, subs); |
385 | 480 | ret = -EINVAL; |
386 | 481 | goto out; |
387 | 482 | } |
388 | 483 |
|
389 | | - /* Real devices */ |
390 | | - ret = asprintf(&p.pcidev, "/sys/class/infiniband/%s/device", d->curr); |
391 | | - if (ret < 0) { |
392 | | - ret = -ENOMEM; |
393 | | - p.pcidev = NULL; |
394 | | - goto out; |
395 | | - } |
396 | | - |
397 | 484 | ret = get_virtfn_info(d, &p); |
398 | 485 | if (ret) |
399 | 486 | goto out; |
@@ -453,6 +540,23 @@ static int by_pci(struct data *d) |
453 | 540 | strcat(d->name, buf); |
454 | 541 | } |
455 | 542 | } |
| 543 | + |
| 544 | + /* |
| 545 | + * SF identity is independent of any preceding f<func>/v<vf> |
| 546 | + * components, so append S<sfnum> last whenever it was set. For an SF |
| 547 | + * whose parent is a multi-function PF this yields |
| 548 | + * <prefix>p<bus>s<slot>f<func>S<sfnum>; for an SF on an SR-IOV VF |
| 549 | + * (VF-SF) the same rule yields |
| 550 | + * <prefix>p<bus>s<slot>f<func>v<vf>S<sfnum>. |
| 551 | + */ |
| 552 | + if (p.valid_sf) { |
| 553 | + ret = sprintf(buf, "S%u", p.sfnum); |
| 554 | + if (ret == -1) { |
| 555 | + ret = -ENOMEM; |
| 556 | + goto out; |
| 557 | + } |
| 558 | + strcat(d->name, buf); |
| 559 | + } |
456 | 560 | ret = 0; |
457 | 561 | out: |
458 | 562 | free(p.pcidev); |
|
0 commit comments