aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-pci11
-rw-r--r--drivers/pci/msi.c166
-rw-r--r--include/linux/pci.h2
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
70Contact: Neil Horman <nhorman@tuxdriver.com> 70Contact: Neil Horman <nhorman@tuxdriver.com>
71Description: 71Description:
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
79What: /sys/bus/pci/devices/.../msi_irqs/<N>/mode 76What: /sys/bus/pci/devices/.../msi_irqs/<N>
80Date: September 2011 77Date: September 2011
81Contact: Neil Horman <nhorman@tuxdriver.com> 78Contact: Neil Horman <nhorman@tuxdriver.com>
82Description: 79Description:
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
86What: /sys/bus/pci/devices/.../remove 83What: /sys/bus/pci/devices/.../remove
87Date: January 2009 84Date: 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)
363static void free_msi_irqs(struct pci_dev *dev) 363static 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
403static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) 422static 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}
472EXPORT_SYMBOL_GPL(pci_restore_msi_state); 491EXPORT_SYMBOL_GPL(pci_restore_msi_state);
473 492
474 493static 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
478struct 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
486static 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;
492static 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
504static const struct sysfs_ops msi_irq_sysfs_ops = {
505 .show = msi_irq_attr_show,
506};
507
508static struct msi_attribute mode_attribute =
509 __ATTR(mode, S_IRUGO, show_msi_mode, NULL);
510
511
512static struct attribute *msi_irq_default_attrs[] = {
513 &mode_attribute.attr,
514 NULL
515};
516 500
517static 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
524static 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
530static int populate_msi_sysfs(struct pci_dev *pdev) 514static 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
555out_unroll: 569error_irq_groups:
556 list_for_each_entry(entry, &pdev->msi_list, list) { 570 kfree(msi_irq_groups);
557 if (!count) 571error_irq_group:
558 break; 572 kfree(msi_irq_group);
559 kobject_del(&entry->kobj); 573error_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}
931EXPORT_SYMBOL(pci_disable_msi); 949EXPORT_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}
1029EXPORT_SYMBOL(pci_disable_msix); 1045EXPORT_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