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 | } |
