diff options
Diffstat (limited to 'drivers/pci/pci-sysfs.c')
-rw-r--r-- | drivers/pci/pci-sysfs.c | 153 |
1 files changed, 114 insertions, 39 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index fdefa7dcd156..a1d2e979b17f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -117,6 +117,7 @@ is_enabled_store(struct device *dev, struct device_attribute *attr, | |||
117 | const char *buf, size_t count) | 117 | const char *buf, size_t count) |
118 | { | 118 | { |
119 | struct pci_dev *pdev = to_pci_dev(dev); | 119 | struct pci_dev *pdev = to_pci_dev(dev); |
120 | int retval = 0; | ||
120 | 121 | ||
121 | /* this can crash the machine when done on the "wrong" device */ | 122 | /* this can crash the machine when done on the "wrong" device */ |
122 | if (!capable(CAP_SYS_ADMIN)) | 123 | if (!capable(CAP_SYS_ADMIN)) |
@@ -126,11 +127,53 @@ is_enabled_store(struct device *dev, struct device_attribute *attr, | |||
126 | pci_disable_device(pdev); | 127 | pci_disable_device(pdev); |
127 | 128 | ||
128 | if (*buf == '1') | 129 | if (*buf == '1') |
129 | pci_enable_device(pdev); | 130 | retval = pci_enable_device(pdev); |
130 | 131 | ||
132 | if (retval) | ||
133 | return retval; | ||
131 | return count; | 134 | return count; |
132 | } | 135 | } |
133 | 136 | ||
137 | static ssize_t | ||
138 | msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
139 | { | ||
140 | struct pci_dev *pdev = to_pci_dev(dev); | ||
141 | |||
142 | if (!pdev->subordinate) | ||
143 | return 0; | ||
144 | |||
145 | return sprintf (buf, "%u\n", | ||
146 | !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI)); | ||
147 | } | ||
148 | |||
149 | static ssize_t | ||
150 | msi_bus_store(struct device *dev, struct device_attribute *attr, | ||
151 | const char *buf, size_t count) | ||
152 | { | ||
153 | struct pci_dev *pdev = to_pci_dev(dev); | ||
154 | |||
155 | /* bad things may happen if the no_msi flag is changed | ||
156 | * while some drivers are loaded */ | ||
157 | if (!capable(CAP_SYS_ADMIN)) | ||
158 | return count; | ||
159 | |||
160 | if (!pdev->subordinate) | ||
161 | return count; | ||
162 | |||
163 | if (*buf == '0') { | ||
164 | pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; | ||
165 | dev_warn(&pdev->dev, "forced subordinate bus to not support MSI," | ||
166 | " bad things could happen.\n"); | ||
167 | } | ||
168 | |||
169 | if (*buf == '1') { | ||
170 | pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI; | ||
171 | dev_warn(&pdev->dev, "forced subordinate bus to support MSI," | ||
172 | " bad things could happen.\n"); | ||
173 | } | ||
174 | |||
175 | return count; | ||
176 | } | ||
134 | 177 | ||
135 | struct device_attribute pci_dev_attrs[] = { | 178 | struct device_attribute pci_dev_attrs[] = { |
136 | __ATTR_RO(resource), | 179 | __ATTR_RO(resource), |
@@ -145,6 +188,7 @@ struct device_attribute pci_dev_attrs[] = { | |||
145 | __ATTR(enable, 0600, is_enabled_show, is_enabled_store), | 188 | __ATTR(enable, 0600, is_enabled_show, is_enabled_store), |
146 | __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), | 189 | __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), |
147 | broken_parity_status_show,broken_parity_status_store), | 190 | broken_parity_status_show,broken_parity_status_store), |
191 | __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), | ||
148 | __ATTR_NULL, | 192 | __ATTR_NULL, |
149 | }; | 193 | }; |
150 | 194 | ||
@@ -385,15 +429,38 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, | |||
385 | } | 429 | } |
386 | 430 | ||
387 | /** | 431 | /** |
432 | * pci_remove_resource_files - cleanup resource files | ||
433 | * @dev: dev to cleanup | ||
434 | * | ||
435 | * If we created resource files for @dev, remove them from sysfs and | ||
436 | * free their resources. | ||
437 | */ | ||
438 | static void | ||
439 | pci_remove_resource_files(struct pci_dev *pdev) | ||
440 | { | ||
441 | int i; | ||
442 | |||
443 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
444 | struct bin_attribute *res_attr; | ||
445 | |||
446 | res_attr = pdev->res_attr[i]; | ||
447 | if (res_attr) { | ||
448 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); | ||
449 | kfree(res_attr); | ||
450 | } | ||
451 | } | ||
452 | } | ||
453 | |||
454 | /** | ||
388 | * pci_create_resource_files - create resource files in sysfs for @dev | 455 | * pci_create_resource_files - create resource files in sysfs for @dev |
389 | * @dev: dev in question | 456 | * @dev: dev in question |
390 | * | 457 | * |
391 | * Walk the resources in @dev creating files for each resource available. | 458 | * Walk the resources in @dev creating files for each resource available. |
392 | */ | 459 | */ |
393 | static void | 460 | static int pci_create_resource_files(struct pci_dev *pdev) |
394 | pci_create_resource_files(struct pci_dev *pdev) | ||
395 | { | 461 | { |
396 | int i; | 462 | int i; |
463 | int retval; | ||
397 | 464 | ||
398 | /* Expose the PCI resources from this device as files */ | 465 | /* Expose the PCI resources from this device as files */ |
399 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | 466 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { |
@@ -416,35 +483,19 @@ pci_create_resource_files(struct pci_dev *pdev) | |||
416 | res_attr->size = pci_resource_len(pdev, i); | 483 | res_attr->size = pci_resource_len(pdev, i); |
417 | res_attr->mmap = pci_mmap_resource; | 484 | res_attr->mmap = pci_mmap_resource; |
418 | res_attr->private = &pdev->resource[i]; | 485 | res_attr->private = &pdev->resource[i]; |
419 | sysfs_create_bin_file(&pdev->dev.kobj, res_attr); | 486 | retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); |
420 | } | 487 | if (retval) { |
421 | } | 488 | pci_remove_resource_files(pdev); |
422 | } | 489 | return retval; |
423 | 490 | } | |
424 | /** | 491 | } else { |
425 | * pci_remove_resource_files - cleanup resource files | 492 | return -ENOMEM; |
426 | * @dev: dev to cleanup | ||
427 | * | ||
428 | * If we created resource files for @dev, remove them from sysfs and | ||
429 | * free their resources. | ||
430 | */ | ||
431 | static void | ||
432 | pci_remove_resource_files(struct pci_dev *pdev) | ||
433 | { | ||
434 | int i; | ||
435 | |||
436 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
437 | struct bin_attribute *res_attr; | ||
438 | |||
439 | res_attr = pdev->res_attr[i]; | ||
440 | if (res_attr) { | ||
441 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); | ||
442 | kfree(res_attr); | ||
443 | } | 493 | } |
444 | } | 494 | } |
495 | return 0; | ||
445 | } | 496 | } |
446 | #else /* !HAVE_PCI_MMAP */ | 497 | #else /* !HAVE_PCI_MMAP */ |
447 | static inline void pci_create_resource_files(struct pci_dev *dev) { return; } | 498 | static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; } |
448 | static inline void pci_remove_resource_files(struct pci_dev *dev) { return; } | 499 | static inline void pci_remove_resource_files(struct pci_dev *dev) { return; } |
449 | #endif /* HAVE_PCI_MMAP */ | 500 | #endif /* HAVE_PCI_MMAP */ |
450 | 501 | ||
@@ -529,22 +580,27 @@ static struct bin_attribute pcie_config_attr = { | |||
529 | .write = pci_write_config, | 580 | .write = pci_write_config, |
530 | }; | 581 | }; |
531 | 582 | ||
532 | int pci_create_sysfs_dev_files (struct pci_dev *pdev) | 583 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) |
533 | { | 584 | { |
585 | struct bin_attribute *rom_attr = NULL; | ||
586 | int retval; | ||
587 | |||
534 | if (!sysfs_initialized) | 588 | if (!sysfs_initialized) |
535 | return -EACCES; | 589 | return -EACCES; |
536 | 590 | ||
537 | if (pdev->cfg_size < 4096) | 591 | if (pdev->cfg_size < 4096) |
538 | sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); | 592 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); |
539 | else | 593 | else |
540 | sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); | 594 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
595 | if (retval) | ||
596 | goto err; | ||
541 | 597 | ||
542 | pci_create_resource_files(pdev); | 598 | retval = pci_create_resource_files(pdev); |
599 | if (retval) | ||
600 | goto err_bin_file; | ||
543 | 601 | ||
544 | /* If the device has a ROM, try to expose it in sysfs. */ | 602 | /* If the device has a ROM, try to expose it in sysfs. */ |
545 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { | 603 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { |
546 | struct bin_attribute *rom_attr; | ||
547 | |||
548 | rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); | 604 | rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); |
549 | if (rom_attr) { | 605 | if (rom_attr) { |
550 | pdev->rom_attr = rom_attr; | 606 | pdev->rom_attr = rom_attr; |
@@ -554,13 +610,28 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev) | |||
554 | rom_attr->attr.owner = THIS_MODULE; | 610 | rom_attr->attr.owner = THIS_MODULE; |
555 | rom_attr->read = pci_read_rom; | 611 | rom_attr->read = pci_read_rom; |
556 | rom_attr->write = pci_write_rom; | 612 | rom_attr->write = pci_write_rom; |
557 | sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); | 613 | retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); |
614 | if (retval) | ||
615 | goto err_rom; | ||
616 | } else { | ||
617 | retval = -ENOMEM; | ||
618 | goto err_bin_file; | ||
558 | } | 619 | } |
559 | } | 620 | } |
560 | /* add platform-specific attributes */ | 621 | /* add platform-specific attributes */ |
561 | pcibios_add_platform_entries(pdev); | 622 | pcibios_add_platform_entries(pdev); |
562 | 623 | ||
563 | return 0; | 624 | return 0; |
625 | |||
626 | err_rom: | ||
627 | kfree(rom_attr); | ||
628 | err_bin_file: | ||
629 | if (pdev->cfg_size < 4096) | ||
630 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); | ||
631 | else | ||
632 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); | ||
633 | err: | ||
634 | return retval; | ||
564 | } | 635 | } |
565 | 636 | ||
566 | /** | 637 | /** |
@@ -589,10 +660,14 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) | |||
589 | static int __init pci_sysfs_init(void) | 660 | static int __init pci_sysfs_init(void) |
590 | { | 661 | { |
591 | struct pci_dev *pdev = NULL; | 662 | struct pci_dev *pdev = NULL; |
592 | 663 | int retval; | |
664 | |||
593 | sysfs_initialized = 1; | 665 | sysfs_initialized = 1; |
594 | for_each_pci_dev(pdev) | 666 | for_each_pci_dev(pdev) { |
595 | pci_create_sysfs_dev_files(pdev); | 667 | retval = pci_create_sysfs_dev_files(pdev); |
668 | if (retval) | ||
669 | return retval; | ||
670 | } | ||
596 | 671 | ||
597 | return 0; | 672 | return 0; |
598 | } | 673 | } |