Skip to content

Commit c183619

Browse files
Jiang Liurafaeljw
authored andcommitted
x86/irq, ACPI: Implement ACPI driver to support IOAPIC hotplug
Enable support of IOAPIC hotplug by: 1) reintroducing ACPI based IOAPIC driver 2) enhance pci_root driver to hook hotplug events The ACPI IOAPIC driver is always enabled if all of ACPI, PCI and IOAPIC are enabled. Signed-off-by: Jiang Liu <[email protected]> Cc: Konrad Rzeszutek Wilk <[email protected]> Cc: Tony Luck <[email protected]> Cc: Joerg Roedel <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Cc: Benjamin Herrenschmidt <[email protected]> Cc: Rafael J. Wysocki <[email protected]> Cc: Bjorn Helgaas <[email protected]> Cc: Randy Dunlap <[email protected]> Cc: Yinghai Lu <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Len Brown <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent ecf5636 commit c183619

File tree

5 files changed

+246
-0
lines changed

5 files changed

+246
-0
lines changed

drivers/acpi/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,12 @@ config ACPI_HOTPLUG_MEMORY
315315
To compile this driver as a module, choose M here:
316316
the module will be called acpi_memhotplug.
317317

318+
config ACPI_HOTPLUG_IOAPIC
319+
bool
320+
depends on PCI
321+
depends on X86_IO_APIC
322+
default y
323+
318324
config ACPI_SBS
319325
tristate "Smart Battery System"
320326
depends on X86

drivers/acpi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
7070
obj-y += container.o
7171
obj-$(CONFIG_ACPI_THERMAL) += thermal.o
7272
obj-y += acpi_memhotplug.o
73+
obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o
7374
obj-$(CONFIG_ACPI_BATTERY) += battery.o
7475
obj-$(CONFIG_ACPI_SBS) += sbshc.o
7576
obj-$(CONFIG_ACPI_SBS) += sbs.o

drivers/acpi/internal.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ void acpi_int340x_thermal_init(void);
3535
int acpi_sysfs_init(void);
3636
void acpi_container_init(void);
3737
void acpi_memory_hotplug_init(void);
38+
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
39+
int acpi_ioapic_add(struct acpi_pci_root *root);
40+
int acpi_ioapic_remove(struct acpi_pci_root *root);
41+
#else
42+
static inline int acpi_ioapic_add(struct acpi_pci_root *root) { return 0; }
43+
static inline int acpi_ioapic_remove(struct acpi_pci_root *root) { return 0; }
44+
#endif
3845
#ifdef CONFIG_ACPI_DOCK
3946
void register_dock_dependent_device(struct acpi_device *adev,
4047
acpi_handle dshandle);

drivers/acpi/ioapic.c

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
* IOAPIC/IOxAPIC/IOSAPIC driver
3+
*
4+
* Copyright (C) 2009 Fujitsu Limited.
5+
* (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
6+
*
7+
* Copyright (C) 2014 Intel Corporation
8+
*
9+
* This program is free software; you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License version 2 as
11+
* published by the Free Software Foundation.
12+
*
13+
* Based on original drivers/pci/ioapic.c
14+
* Yinghai Lu <[email protected]>
15+
* Jiang Liu <[email protected]>
16+
*/
17+
18+
/*
19+
* This driver manages I/O APICs added by hotplug after boot.
20+
* We try to claim all I/O APIC devices, but those present at boot were
21+
* registered when we parsed the ACPI MADT.
22+
*/
23+
24+
#define pr_fmt(fmt) "ACPI : IOAPIC: " fmt
25+
26+
#include <linux/slab.h>
27+
#include <linux/acpi.h>
28+
#include <linux/pci.h>
29+
#include <acpi/acpi.h>
30+
31+
struct acpi_pci_ioapic {
32+
acpi_handle root_handle;
33+
acpi_handle handle;
34+
u32 gsi_base;
35+
struct resource res;
36+
struct pci_dev *pdev;
37+
struct list_head list;
38+
};
39+
40+
static LIST_HEAD(ioapic_list);
41+
static DEFINE_MUTEX(ioapic_list_lock);
42+
43+
static acpi_status setup_res(struct acpi_resource *acpi_res, void *data)
44+
{
45+
struct resource *res = data;
46+
struct resource_win win;
47+
48+
res->flags = 0;
49+
if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM) == 0)
50+
return AE_OK;
51+
52+
if (!acpi_dev_resource_memory(acpi_res, res)) {
53+
if (acpi_dev_resource_address_space(acpi_res, &win) ||
54+
acpi_dev_resource_ext_address_space(acpi_res, &win))
55+
*res = win.res;
56+
}
57+
if ((res->flags & IORESOURCE_PREFETCH) ||
58+
(res->flags & IORESOURCE_DISABLED))
59+
res->flags = 0;
60+
61+
return AE_CTRL_TERMINATE;
62+
}
63+
64+
static bool acpi_is_ioapic(acpi_handle handle, char **type)
65+
{
66+
acpi_status status;
67+
struct acpi_device_info *info;
68+
char *hid = NULL;
69+
bool match = false;
70+
71+
if (!acpi_has_method(handle, "_GSB"))
72+
return false;
73+
74+
status = acpi_get_object_info(handle, &info);
75+
if (ACPI_SUCCESS(status)) {
76+
if (info->valid & ACPI_VALID_HID)
77+
hid = info->hardware_id.string;
78+
if (hid) {
79+
if (strcmp(hid, "ACPI0009") == 0) {
80+
*type = "IOxAPIC";
81+
match = true;
82+
} else if (strcmp(hid, "ACPI000A") == 0) {
83+
*type = "IOAPIC";
84+
match = true;
85+
}
86+
}
87+
kfree(info);
88+
}
89+
90+
return match;
91+
}
92+
93+
static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl,
94+
void *context, void **rv)
95+
{
96+
acpi_status status;
97+
unsigned long long gsi_base;
98+
struct acpi_pci_ioapic *ioapic;
99+
struct pci_dev *dev = NULL;
100+
struct resource *res = NULL;
101+
char *type = NULL;
102+
103+
if (!acpi_is_ioapic(handle, &type))
104+
return AE_OK;
105+
106+
mutex_lock(&ioapic_list_lock);
107+
list_for_each_entry(ioapic, &ioapic_list, list)
108+
if (ioapic->handle == handle) {
109+
mutex_unlock(&ioapic_list_lock);
110+
return AE_OK;
111+
}
112+
113+
status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base);
114+
if (ACPI_FAILURE(status)) {
115+
acpi_handle_warn(handle, "failed to evaluate _GSB method\n");
116+
goto exit;
117+
}
118+
119+
ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
120+
if (!ioapic) {
121+
pr_err("cannot allocate memory for new IOAPIC\n");
122+
goto exit;
123+
} else {
124+
ioapic->root_handle = (acpi_handle)context;
125+
ioapic->handle = handle;
126+
ioapic->gsi_base = (u32)gsi_base;
127+
INIT_LIST_HEAD(&ioapic->list);
128+
}
129+
130+
if (acpi_ioapic_registered(handle, (u32)gsi_base))
131+
goto done;
132+
133+
dev = acpi_get_pci_dev(handle);
134+
if (dev && pci_resource_len(dev, 0)) {
135+
if (pci_enable_device(dev) < 0)
136+
goto exit_put;
137+
pci_set_master(dev);
138+
if (pci_request_region(dev, 0, type))
139+
goto exit_disable;
140+
res = &dev->resource[0];
141+
ioapic->pdev = dev;
142+
} else {
143+
pci_dev_put(dev);
144+
dev = NULL;
145+
146+
res = &ioapic->res;
147+
acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, res);
148+
if (res->flags == 0) {
149+
acpi_handle_warn(handle, "failed to get resource\n");
150+
goto exit_free;
151+
} else if (request_resource(&iomem_resource, res)) {
152+
acpi_handle_warn(handle, "failed to insert resource\n");
153+
goto exit_free;
154+
}
155+
}
156+
157+
if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) {
158+
acpi_handle_warn(handle, "failed to register IOAPIC\n");
159+
goto exit_release;
160+
}
161+
done:
162+
list_add(&ioapic->list, &ioapic_list);
163+
mutex_unlock(&ioapic_list_lock);
164+
165+
if (dev)
166+
dev_info(&dev->dev, "%s at %pR, GSI %u\n",
167+
type, res, (u32)gsi_base);
168+
else
169+
acpi_handle_info(handle, "%s at %pR, GSI %u\n",
170+
type, res, (u32)gsi_base);
171+
172+
return AE_OK;
173+
174+
exit_release:
175+
if (dev)
176+
pci_release_region(dev, 0);
177+
else
178+
release_resource(res);
179+
exit_disable:
180+
if (dev)
181+
pci_disable_device(dev);
182+
exit_put:
183+
pci_dev_put(dev);
184+
exit_free:
185+
kfree(ioapic);
186+
exit:
187+
mutex_unlock(&ioapic_list_lock);
188+
*(acpi_status *)rv = AE_ERROR;
189+
return AE_OK;
190+
}
191+
192+
int acpi_ioapic_add(struct acpi_pci_root *root)
193+
{
194+
acpi_status status, retval = AE_OK;
195+
196+
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle,
197+
UINT_MAX, handle_ioapic_add, NULL,
198+
root->device->handle, (void **)&retval);
199+
200+
return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV;
201+
}
202+
203+
int acpi_ioapic_remove(struct acpi_pci_root *root)
204+
{
205+
int retval = 0;
206+
struct acpi_pci_ioapic *ioapic, *tmp;
207+
208+
mutex_lock(&ioapic_list_lock);
209+
list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) {
210+
if (root->device->handle != ioapic->root_handle)
211+
continue;
212+
213+
if (acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base))
214+
retval = -EBUSY;
215+
216+
if (ioapic->pdev) {
217+
pci_release_region(ioapic->pdev, 0);
218+
pci_disable_device(ioapic->pdev);
219+
pci_dev_put(ioapic->pdev);
220+
} else if (ioapic->res.flags && ioapic->res.parent) {
221+
release_resource(&ioapic->res);
222+
}
223+
list_del(&ioapic->list);
224+
kfree(ioapic);
225+
}
226+
mutex_unlock(&ioapic_list_lock);
227+
228+
return retval;
229+
}

drivers/acpi/pci_root.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
621621
if (hotadd) {
622622
pcibios_resource_survey_bus(root->bus);
623623
pci_assign_unassigned_root_bus_resources(root->bus);
624+
acpi_ioapic_add(root);
624625
}
625626

626627
pci_lock_rescan_remove();
@@ -644,6 +645,8 @@ static void acpi_pci_root_remove(struct acpi_device *device)
644645

645646
pci_stop_root_bus(root->bus);
646647

648+
WARN_ON(acpi_ioapic_remove(root));
649+
647650
device_set_run_wake(root->bus->bridge, false);
648651
pci_acpi_remove_bus_pm_notifier(device);
649652

0 commit comments

Comments
 (0)