diff options
author | Sinan Kaya <okaya@codeaurora.org> | 2017-12-12 19:37:27 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-12-15 21:05:37 -0500 |
commit | 099caa9137624e69d936a62ce585d1adaec385ab (patch) | |
tree | 4875d09fcbe21718eb39b4c408b6b1bc02797ae3 | |
parent | 50c4c4e268a2d7a3e58ebb698ac74da0de40ae36 (diff) |
ACPI: GED: unregister interrupts during shutdown
Some GED interrupts could be pending by the time we are doing a reboot.
Even though GED driver uses devm_request_irq() to register the interrupt
handler, the handler is not being freed on time during a shutdown since
the driver is missing a shutdown callback.
If the ACPI handler is no longer available, this causes an interrupt
storm and delays shutdown.
1. Don't use devm family of functions for IRQ registration/free
2. Keep track of the events since free_irq() requires the dev_id
parameter passed into the request_irq() function.
3. Call free_irq() on both remove and shutdown explicitly.
Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/acpi/evged.c | 47 |
1 files changed, 41 insertions, 6 deletions
diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c index 46f060356a22..f13ba2c07667 100644 --- a/drivers/acpi/evged.c +++ b/drivers/acpi/evged.c | |||
@@ -49,6 +49,11 @@ | |||
49 | 49 | ||
50 | #define MODULE_NAME "acpi-ged" | 50 | #define MODULE_NAME "acpi-ged" |
51 | 51 | ||
52 | struct acpi_ged_device { | ||
53 | struct device *dev; | ||
54 | struct list_head event_list; | ||
55 | }; | ||
56 | |||
52 | struct acpi_ged_event { | 57 | struct acpi_ged_event { |
53 | struct list_head node; | 58 | struct list_head node; |
54 | struct device *dev; | 59 | struct device *dev; |
@@ -76,7 +81,8 @@ static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares, | |||
76 | unsigned int irq; | 81 | unsigned int irq; |
77 | unsigned int gsi; | 82 | unsigned int gsi; |
78 | unsigned int irqflags = IRQF_ONESHOT; | 83 | unsigned int irqflags = IRQF_ONESHOT; |
79 | struct device *dev = context; | 84 | struct acpi_ged_device *geddev = context; |
85 | struct device *dev = geddev->dev; | ||
80 | acpi_handle handle = ACPI_HANDLE(dev); | 86 | acpi_handle handle = ACPI_HANDLE(dev); |
81 | acpi_handle evt_handle; | 87 | acpi_handle evt_handle; |
82 | struct resource r; | 88 | struct resource r; |
@@ -102,8 +108,6 @@ static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares, | |||
102 | return AE_ERROR; | 108 | return AE_ERROR; |
103 | } | 109 | } |
104 | 110 | ||
105 | dev_info(dev, "GED listening GSI %u @ IRQ %u\n", gsi, irq); | ||
106 | |||
107 | event = devm_kzalloc(dev, sizeof(*event), GFP_KERNEL); | 111 | event = devm_kzalloc(dev, sizeof(*event), GFP_KERNEL); |
108 | if (!event) | 112 | if (!event) |
109 | return AE_ERROR; | 113 | return AE_ERROR; |
@@ -116,29 +120,58 @@ static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares, | |||
116 | if (r.flags & IORESOURCE_IRQ_SHAREABLE) | 120 | if (r.flags & IORESOURCE_IRQ_SHAREABLE) |
117 | irqflags |= IRQF_SHARED; | 121 | irqflags |= IRQF_SHARED; |
118 | 122 | ||
119 | if (devm_request_threaded_irq(dev, irq, NULL, acpi_ged_irq_handler, | 123 | if (request_threaded_irq(irq, NULL, acpi_ged_irq_handler, |
120 | irqflags, "ACPI:Ged", event)) { | 124 | irqflags, "ACPI:Ged", event)) { |
121 | dev_err(dev, "failed to setup event handler for irq %u\n", irq); | 125 | dev_err(dev, "failed to setup event handler for irq %u\n", irq); |
122 | return AE_ERROR; | 126 | return AE_ERROR; |
123 | } | 127 | } |
124 | 128 | ||
129 | dev_dbg(dev, "GED listening GSI %u @ IRQ %u\n", gsi, irq); | ||
130 | list_add_tail(&event->node, &geddev->event_list); | ||
125 | return AE_OK; | 131 | return AE_OK; |
126 | } | 132 | } |
127 | 133 | ||
128 | static int ged_probe(struct platform_device *pdev) | 134 | static int ged_probe(struct platform_device *pdev) |
129 | { | 135 | { |
136 | struct acpi_ged_device *geddev; | ||
130 | acpi_status acpi_ret; | 137 | acpi_status acpi_ret; |
131 | 138 | ||
139 | geddev = devm_kzalloc(&pdev->dev, sizeof(*geddev), GFP_KERNEL); | ||
140 | if (!geddev) | ||
141 | return -ENOMEM; | ||
142 | |||
143 | geddev->dev = &pdev->dev; | ||
144 | INIT_LIST_HEAD(&geddev->event_list); | ||
132 | acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), "_CRS", | 145 | acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), "_CRS", |
133 | acpi_ged_request_interrupt, &pdev->dev); | 146 | acpi_ged_request_interrupt, geddev); |
134 | if (ACPI_FAILURE(acpi_ret)) { | 147 | if (ACPI_FAILURE(acpi_ret)) { |
135 | dev_err(&pdev->dev, "unable to parse the _CRS record\n"); | 148 | dev_err(&pdev->dev, "unable to parse the _CRS record\n"); |
136 | return -EINVAL; | 149 | return -EINVAL; |
137 | } | 150 | } |
151 | platform_set_drvdata(pdev, geddev); | ||
138 | 152 | ||
139 | return 0; | 153 | return 0; |
140 | } | 154 | } |
141 | 155 | ||
156 | static void ged_shutdown(struct platform_device *pdev) | ||
157 | { | ||
158 | struct acpi_ged_device *geddev = platform_get_drvdata(pdev); | ||
159 | struct acpi_ged_event *event, *next; | ||
160 | |||
161 | list_for_each_entry_safe(event, next, &geddev->event_list, node) { | ||
162 | free_irq(event->irq, event); | ||
163 | list_del(&event->node); | ||
164 | dev_dbg(geddev->dev, "GED releasing GSI %u @ IRQ %u\n", | ||
165 | event->gsi, event->irq); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | static int ged_remove(struct platform_device *pdev) | ||
170 | { | ||
171 | ged_shutdown(pdev); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
142 | static const struct acpi_device_id ged_acpi_ids[] = { | 175 | static const struct acpi_device_id ged_acpi_ids[] = { |
143 | {"ACPI0013"}, | 176 | {"ACPI0013"}, |
144 | {}, | 177 | {}, |
@@ -146,6 +179,8 @@ static const struct acpi_device_id ged_acpi_ids[] = { | |||
146 | 179 | ||
147 | static struct platform_driver ged_driver = { | 180 | static struct platform_driver ged_driver = { |
148 | .probe = ged_probe, | 181 | .probe = ged_probe, |
182 | .remove = ged_remove, | ||
183 | .shutdown = ged_shutdown, | ||
149 | .driver = { | 184 | .driver = { |
150 | .name = MODULE_NAME, | 185 | .name = MODULE_NAME, |
151 | .acpi_match_table = ACPI_PTR(ged_acpi_ids), | 186 | .acpi_match_table = ACPI_PTR(ged_acpi_ids), |