diff options
author | Neil Horman <nhorman@tuxdriver.com> | 2011-10-06 14:08:18 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2012-01-06 15:10:25 -0500 |
commit | da8d1c8ba4dcb16d60be54b233deca9a7cac98dc (patch) | |
tree | ac4713f9dd29aab4d4d5b167cf61081aeb2ccdb4 /drivers/pci | |
parent | 8b6a5af92c03b363df050da906480085b6cd6e00 (diff) |
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>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/msi.c | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 0e6d04d7ba4f..e6b6b9c67023 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -323,6 +323,8 @@ static void free_msi_irqs(struct pci_dev *dev) | |||
323 | if (list_is_last(&entry->list, &dev->msi_list)) | 323 | if (list_is_last(&entry->list, &dev->msi_list)) |
324 | iounmap(entry->mask_base); | 324 | iounmap(entry->mask_base); |
325 | } | 325 | } |
326 | kobject_del(&entry->kobj); | ||
327 | kobject_put(&entry->kobj); | ||
326 | list_del(&entry->list); | 328 | list_del(&entry->list); |
327 | kfree(entry); | 329 | kfree(entry); |
328 | } | 330 | } |
@@ -403,6 +405,98 @@ void pci_restore_msi_state(struct pci_dev *dev) | |||
403 | } | 405 | } |
404 | EXPORT_SYMBOL_GPL(pci_restore_msi_state); | 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 | * msi_capability_init - configure device's MSI capability structure | 501 | * msi_capability_init - configure device's MSI capability structure |
408 | * @dev: pointer to the pci_dev data structure of MSI device function | 502 | * @dev: pointer to the pci_dev data structure of MSI device function |
@@ -454,6 +548,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) | |||
454 | return ret; | 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 | /* Set MSI enabled bits */ | 558 | /* Set MSI enabled bits */ |
458 | pci_intx_for_msi(dev, 0); | 559 | pci_intx_for_msi(dev, 0); |
459 | msi_set_enable(dev, pos, 1); | 560 | msi_set_enable(dev, pos, 1); |
@@ -574,6 +675,12 @@ static int msix_capability_init(struct pci_dev *dev, | |||
574 | 675 | ||
575 | msix_program_entries(dev, entries); | 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 | /* Set MSI-X enabled bits and unmask the function */ | 684 | /* Set MSI-X enabled bits and unmask the function */ |
578 | pci_intx_for_msi(dev, 0); | 685 | pci_intx_for_msi(dev, 0); |
579 | dev->msix_enabled = 1; | 686 | dev->msix_enabled = 1; |
@@ -732,6 +839,8 @@ void pci_disable_msi(struct pci_dev *dev) | |||
732 | 839 | ||
733 | pci_msi_shutdown(dev); | 840 | pci_msi_shutdown(dev); |
734 | free_msi_irqs(dev); | 841 | free_msi_irqs(dev); |
842 | kset_unregister(dev->msi_kset); | ||
843 | dev->msi_kset = NULL; | ||
735 | } | 844 | } |
736 | EXPORT_SYMBOL(pci_disable_msi); | 845 | EXPORT_SYMBOL(pci_disable_msi); |
737 | 846 | ||
@@ -830,6 +939,8 @@ void pci_disable_msix(struct pci_dev *dev) | |||
830 | 939 | ||
831 | pci_msix_shutdown(dev); | 940 | pci_msix_shutdown(dev); |
832 | free_msi_irqs(dev); | 941 | free_msi_irqs(dev); |
942 | kset_unregister(dev->msi_kset); | ||
943 | dev->msi_kset = NULL; | ||
833 | } | 944 | } |
834 | EXPORT_SYMBOL(pci_disable_msix); | 945 | EXPORT_SYMBOL(pci_disable_msix); |
835 | 946 | ||