diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-12-19 15:30:17 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-12-19 17:14:52 -0500 |
commit | 1c51b50c2995f543d145d3bce78029ac9f8ca6b3 (patch) | |
tree | e45ceda96d94d1141919488165eed037e770e92c /drivers/pci | |
parent | 6ce4eac1f600b34f2f7f58f9cd8f0503d79e42ae (diff) |
PCI/MSI: Export MSI mode using attributes, not kobjects
The PCI MSI sysfs code is a mess with kobjects for things that don't really
need to be kobjects. This patch creates attributes dynamically for the MSI
interrupts instead of using kobjects.
Note, this removes a directory from sysfs. Old MSI kobjects:
pci_device
└── msi_irqs
└── 40
└── mode
New MSI attributes:
pci_device
└── msi_irqs
└── 40
As there was only one file "mode" with the kobject model, the interrupt
number is now a file that returns the "mode" of the interrupt (msi vs.
msix).
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/msi.c | 166 |
1 files changed, 91 insertions, 75 deletions
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 | ||