Commit b50cac55bf859d5b2fdcc1803a553a251b703456
Committed by
Jesse Barnes
1 parent
8e8da023f5
Exists in
master
and in
6 other branches
PCI/sysfs: add per pci device msi[x] irq listing (v5)
This patch adds a per-pci-device subdirectory in sysfs called: /sys/bus/pci/devices/<device>/msi_irqs This sub-directory exports the set of msi vectors allocated by a given pci device, by creating a numbered sub-directory for each vector beneath msi_irqs. For each vector various attributes can be exported. Currently the only attribute is called mode, which tracks the operational mode of that vector (msi vs. msix) Acked-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Showing 4 changed files with 133 additions and 0 deletions Side-by-side Diff
Documentation/ABI/testing/sysfs-bus-pci
... | ... | @@ -66,6 +66,24 @@ |
66 | 66 | re-discover previously removed devices. |
67 | 67 | Depends on CONFIG_HOTPLUG. |
68 | 68 | |
69 | +What: /sys/bus/pci/devices/.../msi_irqs/ | |
70 | +Date: September, 2011 | |
71 | +Contact: Neil Horman <nhorman@tuxdriver.com> | |
72 | +Description: | |
73 | + The /sys/devices/.../msi_irqs directory contains a variable set | |
74 | + of sub-directories, with each sub-directory being named after a | |
75 | + corresponding msi irq vector allocated to that device. Each | |
76 | + numbered sub-directory N contains attributes of that irq. | |
77 | + Note that this directory is not created for device drivers which | |
78 | + do not support msi irqs | |
79 | + | |
80 | +What: /sys/bus/pci/devices/.../msi_irqs/<N>/mode | |
81 | +Date: September 2011 | |
82 | +Contact: Neil Horman <nhorman@tuxdriver.com> | |
83 | +Description: | |
84 | + This attribute indicates the mode that the irq vector named by | |
85 | + the parent directory is in (msi vs. msix) | |
86 | + | |
69 | 87 | What: /sys/bus/pci/devices/.../remove |
70 | 88 | Date: January 2009 |
71 | 89 | Contact: Linux PCI developers <linux-pci@vger.kernel.org> |
drivers/pci/msi.c
... | ... | @@ -323,6 +323,8 @@ |
323 | 323 | if (list_is_last(&entry->list, &dev->msi_list)) |
324 | 324 | iounmap(entry->mask_base); |
325 | 325 | } |
326 | + kobject_del(&entry->kobj); | |
327 | + kobject_put(&entry->kobj); | |
326 | 328 | list_del(&entry->list); |
327 | 329 | kfree(entry); |
328 | 330 | } |
... | ... | @@ -403,6 +405,98 @@ |
403 | 405 | } |
404 | 406 | EXPORT_SYMBOL_GPL(pci_restore_msi_state); |
405 | 407 | |
408 | + | |
409 | +#define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr) | |
410 | +#define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj) | |
411 | + | |
412 | +struct msi_attribute { | |
413 | + struct attribute attr; | |
414 | + ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr, | |
415 | + char *buf); | |
416 | + ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr, | |
417 | + const char *buf, size_t count); | |
418 | +}; | |
419 | + | |
420 | +static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr, | |
421 | + char *buf) | |
422 | +{ | |
423 | + return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi"); | |
424 | +} | |
425 | + | |
426 | +static ssize_t msi_irq_attr_show(struct kobject *kobj, | |
427 | + struct attribute *attr, char *buf) | |
428 | +{ | |
429 | + struct msi_attribute *attribute = to_msi_attr(attr); | |
430 | + struct msi_desc *entry = to_msi_desc(kobj); | |
431 | + | |
432 | + if (!attribute->show) | |
433 | + return -EIO; | |
434 | + | |
435 | + return attribute->show(entry, attribute, buf); | |
436 | +} | |
437 | + | |
438 | +static const struct sysfs_ops msi_irq_sysfs_ops = { | |
439 | + .show = msi_irq_attr_show, | |
440 | +}; | |
441 | + | |
442 | +static struct msi_attribute mode_attribute = | |
443 | + __ATTR(mode, S_IRUGO, show_msi_mode, NULL); | |
444 | + | |
445 | + | |
446 | +struct attribute *msi_irq_default_attrs[] = { | |
447 | + &mode_attribute.attr, | |
448 | + NULL | |
449 | +}; | |
450 | + | |
451 | +void msi_kobj_release(struct kobject *kobj) | |
452 | +{ | |
453 | + struct msi_desc *entry = to_msi_desc(kobj); | |
454 | + | |
455 | + pci_dev_put(entry->dev); | |
456 | +} | |
457 | + | |
458 | +static struct kobj_type msi_irq_ktype = { | |
459 | + .release = msi_kobj_release, | |
460 | + .sysfs_ops = &msi_irq_sysfs_ops, | |
461 | + .default_attrs = msi_irq_default_attrs, | |
462 | +}; | |
463 | + | |
464 | +static int populate_msi_sysfs(struct pci_dev *pdev) | |
465 | +{ | |
466 | + struct msi_desc *entry; | |
467 | + struct kobject *kobj; | |
468 | + int ret; | |
469 | + int count = 0; | |
470 | + | |
471 | + pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); | |
472 | + if (!pdev->msi_kset) | |
473 | + return -ENOMEM; | |
474 | + | |
475 | + list_for_each_entry(entry, &pdev->msi_list, list) { | |
476 | + kobj = &entry->kobj; | |
477 | + kobj->kset = pdev->msi_kset; | |
478 | + pci_dev_get(pdev); | |
479 | + ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, | |
480 | + "%u", entry->irq); | |
481 | + if (ret) | |
482 | + goto out_unroll; | |
483 | + | |
484 | + count++; | |
485 | + } | |
486 | + | |
487 | + return 0; | |
488 | + | |
489 | +out_unroll: | |
490 | + list_for_each_entry(entry, &pdev->msi_list, list) { | |
491 | + if (!count) | |
492 | + break; | |
493 | + kobject_del(&entry->kobj); | |
494 | + kobject_put(&entry->kobj); | |
495 | + count--; | |
496 | + } | |
497 | + return ret; | |
498 | +} | |
499 | + | |
406 | 500 | /** |
407 | 501 | * msi_capability_init - configure device's MSI capability structure |
408 | 502 | * @dev: pointer to the pci_dev data structure of MSI device function |
... | ... | @@ -454,6 +548,13 @@ |
454 | 548 | return ret; |
455 | 549 | } |
456 | 550 | |
551 | + ret = populate_msi_sysfs(dev); | |
552 | + if (ret) { | |
553 | + msi_mask_irq(entry, mask, ~mask); | |
554 | + free_msi_irqs(dev); | |
555 | + return ret; | |
556 | + } | |
557 | + | |
457 | 558 | /* Set MSI enabled bits */ |
458 | 559 | pci_intx_for_msi(dev, 0); |
459 | 560 | msi_set_enable(dev, pos, 1); |
... | ... | @@ -574,6 +675,12 @@ |
574 | 675 | |
575 | 676 | msix_program_entries(dev, entries); |
576 | 677 | |
678 | + ret = populate_msi_sysfs(dev); | |
679 | + if (ret) { | |
680 | + ret = 0; | |
681 | + goto error; | |
682 | + } | |
683 | + | |
577 | 684 | /* Set MSI-X enabled bits and unmask the function */ |
578 | 685 | pci_intx_for_msi(dev, 0); |
579 | 686 | dev->msix_enabled = 1; |
... | ... | @@ -732,6 +839,8 @@ |
732 | 839 | |
733 | 840 | pci_msi_shutdown(dev); |
734 | 841 | free_msi_irqs(dev); |
842 | + kset_unregister(dev->msi_kset); | |
843 | + dev->msi_kset = NULL; | |
735 | 844 | } |
736 | 845 | EXPORT_SYMBOL(pci_disable_msi); |
737 | 846 | |
... | ... | @@ -830,6 +939,8 @@ |
830 | 939 | |
831 | 940 | pci_msix_shutdown(dev); |
832 | 941 | free_msi_irqs(dev); |
942 | + kset_unregister(dev->msi_kset); | |
943 | + dev->msi_kset = NULL; | |
833 | 944 | } |
834 | 945 | EXPORT_SYMBOL(pci_disable_msix); |
835 | 946 |
include/linux/msi.h
include/linux/pci.h
... | ... | @@ -336,6 +336,7 @@ |
336 | 336 | struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ |
337 | 337 | #ifdef CONFIG_PCI_MSI |
338 | 338 | struct list_head msi_list; |
339 | + struct kset *msi_kset; | |
339 | 340 | #endif |
340 | 341 | struct pci_vpd *vpd; |
341 | 342 | #ifdef CONFIG_PCI_ATS |