aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-07-03 21:09:03 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-07-06 17:52:21 -0400
commit0294112ee3135fbd15eaa70015af8283642dd970 (patch)
tree3350d139f63c77dada50250a6d2affbe8c2a7b33 /drivers/acpi
parentd770e558e21961ad6cfdf0ff7df0eb5d7d4f0754 (diff)
ACPI / PNP: Reserve ACPI resources at the fs_initcall_sync stage
This effectively reverts the following three commits: 7bc10388ccdd ACPI / resources: free memory on error in add_region_before() 0f1b414d1907 ACPI / PNP: Avoid conflicting resource reservations b9a5e5e18fbf ACPI / init: Fix the ordering of acpi_reserve_resources() (commit b9a5e5e18fbf introduced regressions some of which, but not all, were addressed by commit 0f1b414d1907 and commit 7bc10388ccdd was a fixup on top of the latter) and causes ACPI fixed hardware resources to be reserved at the fs_initcall_sync stage of system initialization. The story is as follows. First, a boot regression was reported due to an apparent resource reservation ordering change after a commit that shouldn't lead to such changes. Investigation led to the conclusion that the problem happened because acpi_reserve_resources() was executed at the device_initcall() stage of system initialization which wasn't strictly ordered with respect to driver initialization (and with respect to the initialization of the pcieport driver in particular), so a random change causing the device initcalls to be run in a different order might break things. The response to that was to attempt to run acpi_reserve_resources() as soon as we knew that ACPI would be in use (commit b9a5e5e18fbf). However, that turned out to be too early, because it caused resource reservations made by the PNP system driver to fail on at least one system and that failure was addressed by commit 0f1b414d1907. That fix still turned out to be insufficient, though, because calling acpi_reserve_resources() before the fs_initcall stage of system initialization caused a boot regression to happen on the eCAFE EC-800-H20G/S netbook. That meant that we only could call acpi_reserve_resources() at the fs_initcall initialization stage or later, but then we might just as well call it after the PNP initalization in which case commit 0f1b414d1907 wouldn't be necessary any more. For this reason, the changes made by commit 0f1b414d1907 are reverted (along with a memory leak fixup on top of that commit), the changes made by commit b9a5e5e18fbf that went too far are reverted too and acpi_reserve_resources() is changed into fs_initcall_sync, which will cause it to be executed after the PNP subsystem initialization (which is an fs_initcall) and before device initcalls (including the pcieport driver initialization) which should avoid the initial issue. Link: https://bugzilla.kernel.org/show_bug.cgi?id=100581 Link: http://marc.info/?t=143092384600002&r=1&w=2 Link: https://bugzilla.kernel.org/show_bug.cgi?id=99831 Link: http://marc.info/?t=143389402600001&r=1&w=2 Fixes: b9a5e5e18fbf "ACPI / init: Fix the ordering of acpi_reserve_resources()" Reported-by: Roland Dreier <roland@purestorage.com> Cc: All applicable <stable@vger.kernel.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/osl.c12
-rw-r--r--drivers/acpi/resource.c162
2 files changed, 9 insertions, 165 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index c262e4acd68d..3b8963f21b36 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -175,10 +175,14 @@ static void __init acpi_request_region (struct acpi_generic_address *gas,
175 if (!addr || !length) 175 if (!addr || !length)
176 return; 176 return;
177 177
178 acpi_reserve_region(addr, length, gas->space_id, 0, desc); 178 /* Resources are never freed */
179 if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
180 request_region(addr, length, desc);
181 else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
182 request_mem_region(addr, length, desc);
179} 183}
180 184
181static void __init acpi_reserve_resources(void) 185static int __init acpi_reserve_resources(void)
182{ 186{
183 acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length, 187 acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length,
184 "ACPI PM1a_EVT_BLK"); 188 "ACPI PM1a_EVT_BLK");
@@ -207,7 +211,10 @@ static void __init acpi_reserve_resources(void)
207 if (!(acpi_gbl_FADT.gpe1_block_length & 0x1)) 211 if (!(acpi_gbl_FADT.gpe1_block_length & 0x1))
208 acpi_request_region(&acpi_gbl_FADT.xgpe1_block, 212 acpi_request_region(&acpi_gbl_FADT.xgpe1_block,
209 acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK"); 213 acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK");
214
215 return 0;
210} 216}
217fs_initcall_sync(acpi_reserve_resources);
211 218
212void acpi_os_printf(const char *fmt, ...) 219void acpi_os_printf(const char *fmt, ...)
213{ 220{
@@ -1862,7 +1869,6 @@ acpi_status __init acpi_os_initialize(void)
1862 1869
1863acpi_status __init acpi_os_initialize1(void) 1870acpi_status __init acpi_os_initialize1(void)
1864{ 1871{
1865 acpi_reserve_resources();
1866 kacpid_wq = alloc_workqueue("kacpid", 0, 1); 1872 kacpid_wq = alloc_workqueue("kacpid", 0, 1);
1867 kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1); 1873 kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
1868 kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0); 1874 kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index 10561ce16ed1..8244f013f210 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -26,7 +26,6 @@
26#include <linux/device.h> 26#include <linux/device.h>
27#include <linux/export.h> 27#include <linux/export.h>
28#include <linux/ioport.h> 28#include <linux/ioport.h>
29#include <linux/list.h>
30#include <linux/slab.h> 29#include <linux/slab.h>
31 30
32#ifdef CONFIG_X86 31#ifdef CONFIG_X86
@@ -622,164 +621,3 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares,
622 return (type & types) ? 0 : 1; 621 return (type & types) ? 0 : 1;
623} 622}
624EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); 623EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
625
626struct reserved_region {
627 struct list_head node;
628 u64 start;
629 u64 end;
630};
631
632static LIST_HEAD(reserved_io_regions);
633static LIST_HEAD(reserved_mem_regions);
634
635static int request_range(u64 start, u64 end, u8 space_id, unsigned long flags,
636 char *desc)
637{
638 unsigned int length = end - start + 1;
639 struct resource *res;
640
641 res = space_id == ACPI_ADR_SPACE_SYSTEM_IO ?
642 request_region(start, length, desc) :
643 request_mem_region(start, length, desc);
644 if (!res)
645 return -EIO;
646
647 res->flags &= ~flags;
648 return 0;
649}
650
651static int add_region_before(u64 start, u64 end, u8 space_id,
652 unsigned long flags, char *desc,
653 struct list_head *head)
654{
655 struct reserved_region *reg;
656 int error;
657
658 reg = kmalloc(sizeof(*reg), GFP_KERNEL);
659 if (!reg)
660 return -ENOMEM;
661
662 error = request_range(start, end, space_id, flags, desc);
663 if (error) {
664 kfree(reg);
665 return error;
666 }
667
668 reg->start = start;
669 reg->end = end;
670 list_add_tail(&reg->node, head);
671 return 0;
672}
673
674/**
675 * acpi_reserve_region - Reserve an I/O or memory region as a system resource.
676 * @start: Starting address of the region.
677 * @length: Length of the region.
678 * @space_id: Identifier of address space to reserve the region from.
679 * @flags: Resource flags to clear for the region after requesting it.
680 * @desc: Region description (for messages).
681 *
682 * Reserve an I/O or memory region as a system resource to prevent others from
683 * using it. If the new region overlaps with one of the regions (in the given
684 * address space) already reserved by this routine, only the non-overlapping
685 * parts of it will be reserved.
686 *
687 * Returned is either 0 (success) or a negative error code indicating a resource
688 * reservation problem. It is the code of the first encountered error, but the
689 * routine doesn't abort until it has attempted to request all of the parts of
690 * the new region that don't overlap with other regions reserved previously.
691 *
692 * The resources requested by this routine are never released.
693 */
694int acpi_reserve_region(u64 start, unsigned int length, u8 space_id,
695 unsigned long flags, char *desc)
696{
697 struct list_head *regions;
698 struct reserved_region *reg;
699 u64 end = start + length - 1;
700 int ret = 0, error = 0;
701
702 if (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
703 regions = &reserved_io_regions;
704 else if (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
705 regions = &reserved_mem_regions;
706 else
707 return -EINVAL;
708
709 if (list_empty(regions))
710 return add_region_before(start, end, space_id, flags, desc, regions);
711
712 list_for_each_entry(reg, regions, node)
713 if (reg->start == end + 1) {
714 /* The new region can be prepended to this one. */
715 ret = request_range(start, end, space_id, flags, desc);
716 if (!ret)
717 reg->start = start;
718
719 return ret;
720 } else if (reg->start > end) {
721 /* No overlap. Add the new region here and get out. */
722 return add_region_before(start, end, space_id, flags,
723 desc, &reg->node);
724 } else if (reg->end == start - 1) {
725 goto combine;
726 } else if (reg->end >= start) {
727 goto overlap;
728 }
729
730 /* The new region goes after the last existing one. */
731 return add_region_before(start, end, space_id, flags, desc, regions);
732
733 overlap:
734 /*
735 * The new region overlaps an existing one.
736 *
737 * The head part of the new region immediately preceding the existing
738 * overlapping one can be combined with it right away.
739 */
740 if (reg->start > start) {
741 error = request_range(start, reg->start - 1, space_id, flags, desc);
742 if (error)
743 ret = error;
744 else
745 reg->start = start;
746 }
747
748 combine:
749 /*
750 * The new region is adjacent to an existing one. If it extends beyond
751 * that region all the way to the next one, it is possible to combine
752 * all three of them.
753 */
754 while (reg->end < end) {
755 struct reserved_region *next = NULL;
756 u64 a = reg->end + 1, b = end;
757
758 if (!list_is_last(&reg->node, regions)) {
759 next = list_next_entry(reg, node);
760 if (next->start <= end)
761 b = next->start - 1;
762 }
763 error = request_range(a, b, space_id, flags, desc);
764 if (!error) {
765 if (next && next->start == b + 1) {
766 reg->end = next->end;
767 list_del(&next->node);
768 kfree(next);
769 } else {
770 reg->end = end;
771 break;
772 }
773 } else if (next) {
774 if (!ret)
775 ret = error;
776
777 reg = next;
778 } else {
779 break;
780 }
781 }
782
783 return ret ? ret : error;
784}
785EXPORT_SYMBOL_GPL(acpi_reserve_region);