diff options
Diffstat (limited to 'drivers/pci/pci-sysfs.c')
-rw-r--r-- | drivers/pci/pci-sysfs.c | 158 |
1 files changed, 116 insertions, 42 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index bc405c035ce..a1d2e979b17 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -15,7 +15,6 @@ | |||
15 | */ | 15 | */ |
16 | 16 | ||
17 | 17 | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
20 | #include <linux/pci.h> | 19 | #include <linux/pci.h> |
21 | #include <linux/stat.h> | 20 | #include <linux/stat.h> |
@@ -87,7 +86,7 @@ resource_show(struct device * dev, struct device_attribute *attr, char * buf) | |||
87 | char * str = buf; | 86 | char * str = buf; |
88 | int i; | 87 | int i; |
89 | int max = 7; | 88 | int max = 7; |
90 | u64 start, end; | 89 | resource_size_t start, end; |
91 | 90 | ||
92 | if (pci_dev->subordinate) | 91 | if (pci_dev->subordinate) |
93 | max = DEVICE_COUNT_RESOURCE; | 92 | max = DEVICE_COUNT_RESOURCE; |
@@ -118,6 +117,7 @@ is_enabled_store(struct device *dev, struct device_attribute *attr, | |||
118 | const char *buf, size_t count) | 117 | const char *buf, size_t count) |
119 | { | 118 | { |
120 | struct pci_dev *pdev = to_pci_dev(dev); | 119 | struct pci_dev *pdev = to_pci_dev(dev); |
120 | int retval = 0; | ||
121 | 121 | ||
122 | /* this can crash the machine when done on the "wrong" device */ | 122 | /* this can crash the machine when done on the "wrong" device */ |
123 | if (!capable(CAP_SYS_ADMIN)) | 123 | if (!capable(CAP_SYS_ADMIN)) |
@@ -127,11 +127,53 @@ is_enabled_store(struct device *dev, struct device_attribute *attr, | |||
127 | pci_disable_device(pdev); | 127 | pci_disable_device(pdev); |
128 | 128 | ||
129 | if (*buf == '1') | 129 | if (*buf == '1') |
130 | pci_enable_device(pdev); | 130 | retval = pci_enable_device(pdev); |
131 | 131 | ||
132 | if (retval) | ||
133 | return retval; | ||
132 | return count; | 134 | return count; |
133 | } | 135 | } |
134 | 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 | } | ||
135 | 177 | ||
136 | struct device_attribute pci_dev_attrs[] = { | 178 | struct device_attribute pci_dev_attrs[] = { |
137 | __ATTR_RO(resource), | 179 | __ATTR_RO(resource), |
@@ -146,6 +188,7 @@ struct device_attribute pci_dev_attrs[] = { | |||
146 | __ATTR(enable, 0600, is_enabled_show, is_enabled_store), | 188 | __ATTR(enable, 0600, is_enabled_show, is_enabled_store), |
147 | __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), | 189 | __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), |
148 | 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), | ||
149 | __ATTR_NULL, | 192 | __ATTR_NULL, |
150 | }; | 193 | }; |
151 | 194 | ||
@@ -365,7 +408,7 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, | |||
365 | struct device, kobj)); | 408 | struct device, kobj)); |
366 | struct resource *res = (struct resource *)attr->private; | 409 | struct resource *res = (struct resource *)attr->private; |
367 | enum pci_mmap_state mmap_type; | 410 | enum pci_mmap_state mmap_type; |
368 | u64 start, end; | 411 | resource_size_t start, end; |
369 | int i; | 412 | int i; |
370 | 413 | ||
371 | for (i = 0; i < PCI_ROM_RESOURCE; i++) | 414 | for (i = 0; i < PCI_ROM_RESOURCE; i++) |
@@ -386,15 +429,38 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, | |||
386 | } | 429 | } |
387 | 430 | ||
388 | /** | 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 | /** | ||
389 | * pci_create_resource_files - create resource files in sysfs for @dev | 455 | * pci_create_resource_files - create resource files in sysfs for @dev |
390 | * @dev: dev in question | 456 | * @dev: dev in question |
391 | * | 457 | * |
392 | * Walk the resources in @dev creating files for each resource available. | 458 | * Walk the resources in @dev creating files for each resource available. |
393 | */ | 459 | */ |
394 | static void | 460 | static int pci_create_resource_files(struct pci_dev *pdev) |
395 | pci_create_resource_files(struct pci_dev *pdev) | ||
396 | { | 461 | { |
397 | int i; | 462 | int i; |
463 | int retval; | ||
398 | 464 | ||
399 | /* Expose the PCI resources from this device as files */ | 465 | /* Expose the PCI resources from this device as files */ |
400 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | 466 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { |
@@ -417,35 +483,19 @@ pci_create_resource_files(struct pci_dev *pdev) | |||
417 | res_attr->size = pci_resource_len(pdev, i); | 483 | res_attr->size = pci_resource_len(pdev, i); |
418 | res_attr->mmap = pci_mmap_resource; | 484 | res_attr->mmap = pci_mmap_resource; |
419 | res_attr->private = &pdev->resource[i]; | 485 | res_attr->private = &pdev->resource[i]; |
420 | sysfs_create_bin_file(&pdev->dev.kobj, res_attr); | 486 | retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); |
421 | } | 487 | if (retval) { |
422 | } | 488 | pci_remove_resource_files(pdev); |
423 | } | 489 | return retval; |
424 | 490 | } | |
425 | /** | 491 | } else { |
426 | * pci_remove_resource_files - cleanup resource files | 492 | return -ENOMEM; |
427 | * @dev: dev to cleanup | ||
428 | * | ||
429 | * If we created resource files for @dev, remove them from sysfs and | ||
430 | * free their resources. | ||
431 | */ | ||
432 | static void | ||
433 | pci_remove_resource_files(struct pci_dev *pdev) | ||
434 | { | ||
435 | int i; | ||
436 | |||
437 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
438 | struct bin_attribute *res_attr; | ||
439 | |||
440 | res_attr = pdev->res_attr[i]; | ||
441 | if (res_attr) { | ||
442 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); | ||
443 | kfree(res_attr); | ||
444 | } | 493 | } |
445 | } | 494 | } |
495 | return 0; | ||
446 | } | 496 | } |
447 | #else /* !HAVE_PCI_MMAP */ | 497 | #else /* !HAVE_PCI_MMAP */ |
448 | 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; } |
449 | 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; } |
450 | #endif /* HAVE_PCI_MMAP */ | 500 | #endif /* HAVE_PCI_MMAP */ |
451 | 501 | ||
@@ -530,22 +580,27 @@ static struct bin_attribute pcie_config_attr = { | |||
530 | .write = pci_write_config, | 580 | .write = pci_write_config, |
531 | }; | 581 | }; |
532 | 582 | ||
533 | int pci_create_sysfs_dev_files (struct pci_dev *pdev) | 583 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) |
534 | { | 584 | { |
585 | struct bin_attribute *rom_attr = NULL; | ||
586 | int retval; | ||
587 | |||
535 | if (!sysfs_initialized) | 588 | if (!sysfs_initialized) |
536 | return -EACCES; | 589 | return -EACCES; |
537 | 590 | ||
538 | if (pdev->cfg_size < 4096) | 591 | if (pdev->cfg_size < 4096) |
539 | sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); | 592 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); |
540 | else | 593 | else |
541 | 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; | ||
542 | 597 | ||
543 | pci_create_resource_files(pdev); | 598 | retval = pci_create_resource_files(pdev); |
599 | if (retval) | ||
600 | goto err_bin_file; | ||
544 | 601 | ||
545 | /* 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. */ |
546 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { | 603 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { |
547 | struct bin_attribute *rom_attr; | ||
548 | |||
549 | rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); | 604 | rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); |
550 | if (rom_attr) { | 605 | if (rom_attr) { |
551 | pdev->rom_attr = rom_attr; | 606 | pdev->rom_attr = rom_attr; |
@@ -555,13 +610,28 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev) | |||
555 | rom_attr->attr.owner = THIS_MODULE; | 610 | rom_attr->attr.owner = THIS_MODULE; |
556 | rom_attr->read = pci_read_rom; | 611 | rom_attr->read = pci_read_rom; |
557 | rom_attr->write = pci_write_rom; | 612 | rom_attr->write = pci_write_rom; |
558 | 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; | ||
559 | } | 619 | } |
560 | } | 620 | } |
561 | /* add platform-specific attributes */ | 621 | /* add platform-specific attributes */ |
562 | pcibios_add_platform_entries(pdev); | 622 | pcibios_add_platform_entries(pdev); |
563 | 623 | ||
564 | 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; | ||
565 | } | 635 | } |
566 | 636 | ||
567 | /** | 637 | /** |
@@ -590,10 +660,14 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) | |||
590 | static int __init pci_sysfs_init(void) | 660 | static int __init pci_sysfs_init(void) |
591 | { | 661 | { |
592 | struct pci_dev *pdev = NULL; | 662 | struct pci_dev *pdev = NULL; |
593 | 663 | int retval; | |
664 | |||
594 | sysfs_initialized = 1; | 665 | sysfs_initialized = 1; |
595 | for_each_pci_dev(pdev) | 666 | for_each_pci_dev(pdev) { |
596 | pci_create_sysfs_dev_files(pdev); | 667 | retval = pci_create_sysfs_dev_files(pdev); |
668 | if (retval) | ||
669 | return retval; | ||
670 | } | ||
597 | 671 | ||
598 | return 0; | 672 | return 0; |
599 | } | 673 | } |