diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-bus-pci | 11 | ||||
-rw-r--r-- | drivers/pci/msi.c | 166 | ||||
-rw-r--r-- | include/linux/pci.h | 2 |
3 files changed, 96 insertions, 83 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 5210a51c90fd..a3c5a6685036 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci | |||
@@ -70,18 +70,15 @@ Date: September, 2011 | |||
70 | Contact: Neil Horman <nhorman@tuxdriver.com> | 70 | Contact: Neil Horman <nhorman@tuxdriver.com> |
71 | Description: | 71 | Description: |
72 | The /sys/devices/.../msi_irqs directory contains a variable set | 72 | The /sys/devices/.../msi_irqs directory contains a variable set |
73 | of sub-directories, with each sub-directory being named after a | 73 | of files, with each file being named after a corresponding msi |
74 | corresponding msi irq vector allocated to that device. Each | 74 | irq vector allocated to that device. |
75 | numbered sub-directory N contains attributes of that irq. | ||
76 | Note that this directory is not created for device drivers which | ||
77 | do not support msi irqs | ||
78 | 75 | ||
79 | What: /sys/bus/pci/devices/.../msi_irqs/<N>/mode | 76 | What: /sys/bus/pci/devices/.../msi_irqs/<N> |
80 | Date: September 2011 | 77 | Date: September 2011 |
81 | Contact: Neil Horman <nhorman@tuxdriver.com> | 78 | Contact: Neil Horman <nhorman@tuxdriver.com> |
82 | Description: | 79 | Description: |
83 | This attribute indicates the mode that the irq vector named by | 80 | This attribute indicates the mode that the irq vector named by |
84 | the parent directory is in (msi vs. msix) | 81 | the file is in (msi vs. msix) |
85 | 82 | ||
86 | What: /sys/bus/pci/devices/.../remove | 83 | What: /sys/bus/pci/devices/.../remove |
87 | Date: January 2009 | 84 | Date: January 2009 |
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 3fcd67a16677..f88fa1277802 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -363,6 +363,9 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) | |||
363 | static void free_msi_irqs(struct pci_dev *dev) | 363 | static void free_msi_irqs(struct pci_dev *dev) |
364 | { | 364 | { |
365 | struct msi_desc *entry, *tmp; | 365 | struct msi_desc *entry, *tmp; |
366 | struct attribute **msi_attrs; | ||
367 | struct device_attribute *dev_attr; | ||
368 | int count = 0; | ||
366 | 369 | ||
367 | list_for_each_entry(entry, &dev->msi_list, list) { | 370 | list_for_each_entry(entry, &dev->msi_list, list) { |
368 | int i, nvec; | 371 | int i, nvec; |
@@ -398,6 +401,22 @@ static void free_msi_irqs(struct pci_dev *dev) | |||
398 | list_del(&entry->list); | 401 | list_del(&entry->list); |
399 | kfree(entry); | 402 | kfree(entry); |
400 | } | 403 | } |
404 | |||
405 | if (dev->msi_irq_groups) { | ||
406 | sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups); | ||
407 | msi_attrs = dev->msi_irq_groups[0]->attrs; | ||
408 | list_for_each_entry(entry, &dev->msi_list, list) { | ||
409 | dev_attr = container_of(msi_attrs[count], | ||
410 | struct device_attribute, attr); | ||
411 | kfree(dev_attr->attr.name); | ||
412 | kfree(dev_attr); | ||
413 | ++count; | ||
414 | } | ||
415 | kfree(msi_attrs); | ||
416 | kfree(dev->msi_irq_groups[0]); | ||
417 | kfree(dev->msi_irq_groups); | ||
418 | dev->msi_irq_groups = NULL; | ||
419 | } | ||
401 | } | 420 | } |
402 | 421 | ||
403 | static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) | 422 | static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) |
@@ -471,94 +490,95 @@ void pci_restore_msi_state(struct pci_dev *dev) | |||
471 | } | 490 | } |
472 | EXPORT_SYMBOL_GPL(pci_restore_msi_state); | 491 | EXPORT_SYMBOL_GPL(pci_restore_msi_state); |
473 | 492 | ||
474 | 493 | static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr, | |
475 | #define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr) | ||
476 | #define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj) | ||
477 | |||
478 | struct msi_attribute { | ||
479 | struct attribute attr; | ||
480 | ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr, | ||
481 | char *buf); | ||
482 | ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr, | ||
483 | const char *buf, size_t count); | ||
484 | }; | ||
485 | |||
486 | static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr, | ||
487 | char *buf) | 494 | char *buf) |
488 | { | 495 | { |
489 | return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi"); | 496 | struct pci_dev *pdev = to_pci_dev(dev); |
490 | } | 497 | struct msi_desc *entry; |
491 | 498 | unsigned long irq; | |
492 | static ssize_t msi_irq_attr_show(struct kobject *kobj, | 499 | int retval; |
493 | struct attribute *attr, char *buf) | ||
494 | { | ||
495 | struct msi_attribute *attribute = to_msi_attr(attr); | ||
496 | struct msi_desc *entry = to_msi_desc(kobj); | ||
497 | |||
498 | if (!attribute->show) | ||
499 | return -EIO; | ||
500 | |||
501 | return attribute->show(entry, attribute, buf); | ||
502 | } | ||
503 | |||
504 | static const struct sysfs_ops msi_irq_sysfs_ops = { | ||
505 | .show = msi_irq_attr_show, | ||
506 | }; | ||
507 | |||
508 | static struct msi_attribute mode_attribute = | ||
509 | __ATTR(mode, S_IRUGO, show_msi_mode, NULL); | ||
510 | |||
511 | |||
512 | static struct attribute *msi_irq_default_attrs[] = { | ||
513 | &mode_attribute.attr, | ||
514 | NULL | ||
515 | }; | ||
516 | 500 | ||
517 | static void msi_kobj_release(struct kobject *kobj) | 501 | retval = kstrtoul(attr->attr.name, 10, &irq); |
518 | { | 502 | if (retval) |
519 | struct msi_desc *entry = to_msi_desc(kobj); | 503 | return retval; |
520 | 504 | ||
521 | pci_dev_put(entry->dev); | 505 | list_for_each_entry(entry, &pdev->msi_list, list) { |
506 | if (entry->irq == irq) { | ||
507 | return sprintf(buf, "%s\n", | ||
508 | entry->msi_attrib.is_msix ? "msix" : "msi"); | ||
509 | } | ||
510 | } | ||
511 | return -ENODEV; | ||
522 | } | 512 | } |
523 | 513 | ||
524 | static struct kobj_type msi_irq_ktype = { | ||
525 | .release = msi_kobj_release, | ||
526 | .sysfs_ops = &msi_irq_sysfs_ops, | ||
527 | .default_attrs = msi_irq_default_attrs, | ||
528 | }; | ||
529 | |||
530 | static int populate_msi_sysfs(struct pci_dev *pdev) | 514 | static int populate_msi_sysfs(struct pci_dev *pdev) |
531 | { | 515 | { |
516 | struct attribute **msi_attrs; | ||
517 | struct attribute *msi_attr; | ||
518 | struct device_attribute *msi_dev_attr; | ||
519 | struct attribute_group *msi_irq_group; | ||
520 | const struct attribute_group **msi_irq_groups; | ||
532 | struct msi_desc *entry; | 521 | struct msi_desc *entry; |
533 | struct kobject *kobj; | 522 | int ret = -ENOMEM; |
534 | int ret; | 523 | int num_msi = 0; |
535 | int count = 0; | 524 | int count = 0; |
536 | 525 | ||
537 | pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); | 526 | /* Determine how many msi entries we have */ |
538 | if (!pdev->msi_kset) | 527 | list_for_each_entry(entry, &pdev->msi_list, list) { |
539 | return -ENOMEM; | 528 | ++num_msi; |
529 | } | ||
530 | if (!num_msi) | ||
531 | return 0; | ||
540 | 532 | ||
533 | /* Dynamically create the MSI attributes for the PCI device */ | ||
534 | msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL); | ||
535 | if (!msi_attrs) | ||
536 | return -ENOMEM; | ||
541 | list_for_each_entry(entry, &pdev->msi_list, list) { | 537 | list_for_each_entry(entry, &pdev->msi_list, list) { |
542 | kobj = &entry->kobj; | 538 | char *name = kmalloc(20, GFP_KERNEL); |
543 | kobj->kset = pdev->msi_kset; | 539 | msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL); |
544 | pci_dev_get(pdev); | 540 | if (!msi_dev_attr) |
545 | ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, | 541 | goto error_attrs; |
546 | "%u", entry->irq); | 542 | sprintf(name, "%d", entry->irq); |
547 | if (ret) | 543 | sysfs_attr_init(&msi_dev_attr->attr); |
548 | goto out_unroll; | 544 | msi_dev_attr->attr.name = name; |
549 | 545 | msi_dev_attr->attr.mode = S_IRUGO; | |
550 | count++; | 546 | msi_dev_attr->show = msi_mode_show; |
547 | msi_attrs[count] = &msi_dev_attr->attr; | ||
548 | ++count; | ||
551 | } | 549 | } |
552 | 550 | ||
551 | msi_irq_group = kzalloc(sizeof(*msi_irq_group), GFP_KERNEL); | ||
552 | if (!msi_irq_group) | ||
553 | goto error_attrs; | ||
554 | msi_irq_group->name = "msi_irqs"; | ||
555 | msi_irq_group->attrs = msi_attrs; | ||
556 | |||
557 | msi_irq_groups = kzalloc(sizeof(void *) * 2, GFP_KERNEL); | ||
558 | if (!msi_irq_groups) | ||
559 | goto error_irq_group; | ||
560 | msi_irq_groups[0] = msi_irq_group; | ||
561 | |||
562 | ret = sysfs_create_groups(&pdev->dev.kobj, msi_irq_groups); | ||
563 | if (ret) | ||
564 | goto error_irq_groups; | ||
565 | pdev->msi_irq_groups = msi_irq_groups; | ||
566 | |||
553 | return 0; | 567 | return 0; |
554 | 568 | ||
555 | out_unroll: | 569 | error_irq_groups: |
556 | list_for_each_entry(entry, &pdev->msi_list, list) { | 570 | kfree(msi_irq_groups); |
557 | if (!count) | 571 | error_irq_group: |
558 | break; | 572 | kfree(msi_irq_group); |
559 | kobject_del(&entry->kobj); | 573 | error_attrs: |
560 | kobject_put(&entry->kobj); | 574 | count = 0; |
561 | count--; | 575 | msi_attr = msi_attrs[count]; |
576 | while (msi_attr) { | ||
577 | msi_dev_attr = container_of(msi_attr, struct device_attribute, attr); | ||
578 | kfree(msi_attr->name); | ||
579 | kfree(msi_dev_attr); | ||
580 | ++count; | ||
581 | msi_attr = msi_attrs[count]; | ||
562 | } | 582 | } |
563 | return ret; | 583 | return ret; |
564 | } | 584 | } |
@@ -925,8 +945,6 @@ void pci_disable_msi(struct pci_dev *dev) | |||
925 | 945 | ||
926 | pci_msi_shutdown(dev); | 946 | pci_msi_shutdown(dev); |
927 | free_msi_irqs(dev); | 947 | free_msi_irqs(dev); |
928 | kset_unregister(dev->msi_kset); | ||
929 | dev->msi_kset = NULL; | ||
930 | } | 948 | } |
931 | EXPORT_SYMBOL(pci_disable_msi); | 949 | EXPORT_SYMBOL(pci_disable_msi); |
932 | 950 | ||
@@ -1023,8 +1041,6 @@ void pci_disable_msix(struct pci_dev *dev) | |||
1023 | 1041 | ||
1024 | pci_msix_shutdown(dev); | 1042 | pci_msix_shutdown(dev); |
1025 | free_msi_irqs(dev); | 1043 | free_msi_irqs(dev); |
1026 | kset_unregister(dev->msi_kset); | ||
1027 | dev->msi_kset = NULL; | ||
1028 | } | 1044 | } |
1029 | EXPORT_SYMBOL(pci_disable_msix); | 1045 | EXPORT_SYMBOL(pci_disable_msix); |
1030 | 1046 | ||
diff --git a/include/linux/pci.h b/include/linux/pci.h index 1084a15175e0..36a5b1828f91 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -351,7 +351,7 @@ struct pci_dev { | |||
351 | struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ | 351 | struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ |
352 | #ifdef CONFIG_PCI_MSI | 352 | #ifdef CONFIG_PCI_MSI |
353 | struct list_head msi_list; | 353 | struct list_head msi_list; |
354 | struct kset *msi_kset; | 354 | const struct attribute_group **msi_irq_groups; |
355 | #endif | 355 | #endif |
356 | struct pci_vpd *vpd; | 356 | struct pci_vpd *vpd; |
357 | #ifdef CONFIG_PCI_ATS | 357 | #ifdef CONFIG_PCI_ATS |