Skip to content

Commit 687058a

Browse files
committed
Merge tag 'pm+acpi-3.10-late' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull late power management and ACPI fixes from Rafael Wysocki: "Sorry about the timing of this, but ACPI-based docking stations with PCI devices on them and ATA bays would be hardly usable with 3.10 without it. We've been working on these fixes for the last couple of weeks and everyone involved appears to be reasonably comfortable with them now. The PM part is one fix for a cpufreq regression introduced recently - Fix for an ACPI dock regression introduced by the recent rework of the ACPI-based PCI hotplug code (acpiphp) that caused it to be initialized before the ACPI dock driver, which is incorrect (ACPI dock has to be initialized before acpiphp so that acpiphp can register PCI devices on docking stations with it for PCI hotplug on re-dock to work). From Jiang Liu. - Fix for PCI resources allocation in the ACPI-based PCI hotplug code (acpiphp) that makes it use the same PCI resources assignment rules during runtime hotplug that are used during boot (the BIOS' choices are now respected in both cases). This prevents PCI resource allocation failures during hotplug from happening in some cases. From Jiang Liu. - Fix for ordering and synchronization issues during hot-removal of PCI devices on docking stations. It makes the ACPI dock code carry out the PCI devices removal synchronously during undock instead of spawning a separate asynchronous work item to remove each of them without even bothering to wait for all those work items to complete. The hot-addition part is changed analogously. - Fix for a regression (introduced a few releases ago) that removed the code to register a hotplug notificaion handler for for ATA ports/devices inadvertently which prevented ATA bays hotplug from working. The missing code is added back with some improvements. From Aaron Lu. - Fix for a recent cpufreq regression causing a NULL pointer dereference to trigger in od_set_powersave_bias() in some situations from Jacob Shin" * tag 'pm+acpi-3.10-late' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: cpufreq: fix NULL pointer deference at od_set_powersave_bias() libata-acpi: add back ACPI based hotplug functionality ACPI / dock / PCI: Synchronous handling of dock events for PCI devices PCI / ACPI: Use boot-time resource allocation rules during hotplug ACPI / dock: Initialize ACPI dock subsystem upfront
2 parents 54faf77 + 45e0037 commit 687058a

File tree

11 files changed

+207
-110
lines changed

11 files changed

+207
-110
lines changed

drivers/acpi/dock.c

Lines changed: 96 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,21 @@ struct dock_station {
6666
spinlock_t dd_lock;
6767
struct mutex hp_lock;
6868
struct list_head dependent_devices;
69-
struct list_head hotplug_devices;
7069

7170
struct list_head sibling;
7271
struct platform_device *dock_device;
7372
};
7473
static LIST_HEAD(dock_stations);
7574
static int dock_station_count;
75+
static DEFINE_MUTEX(hotplug_lock);
7676

7777
struct dock_dependent_device {
7878
struct list_head list;
79-
struct list_head hotplug_list;
8079
acpi_handle handle;
81-
const struct acpi_dock_ops *ops;
82-
void *context;
80+
const struct acpi_dock_ops *hp_ops;
81+
void *hp_context;
82+
unsigned int hp_refcount;
83+
void (*hp_release)(void *);
8384
};
8485

8586
#define DOCK_DOCKING 0x00000001
@@ -111,7 +112,6 @@ add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
111112

112113
dd->handle = handle;
113114
INIT_LIST_HEAD(&dd->list);
114-
INIT_LIST_HEAD(&dd->hotplug_list);
115115

116116
spin_lock(&ds->dd_lock);
117117
list_add_tail(&dd->list, &ds->dependent_devices);
@@ -121,35 +121,90 @@ add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
121121
}
122122

123123
/**
124-
* dock_add_hotplug_device - associate a hotplug handler with the dock station
125-
* @ds: The dock station
126-
* @dd: The dependent device struct
127-
*
128-
* Add the dependent device to the dock's hotplug device list
124+
* dock_init_hotplug - Initialize a hotplug device on a docking station.
125+
* @dd: Dock-dependent device.
126+
* @ops: Dock operations to attach to the dependent device.
127+
* @context: Data to pass to the @ops callbacks and @release.
128+
* @init: Optional initialization routine to run after setting up context.
129+
* @release: Optional release routine to run on removal.
129130
*/
130-
static void
131-
dock_add_hotplug_device(struct dock_station *ds,
132-
struct dock_dependent_device *dd)
131+
static int dock_init_hotplug(struct dock_dependent_device *dd,
132+
const struct acpi_dock_ops *ops, void *context,
133+
void (*init)(void *), void (*release)(void *))
133134
{
134-
mutex_lock(&ds->hp_lock);
135-
list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
136-
mutex_unlock(&ds->hp_lock);
135+
int ret = 0;
136+
137+
mutex_lock(&hotplug_lock);
138+
139+
if (dd->hp_context) {
140+
ret = -EEXIST;
141+
} else {
142+
dd->hp_refcount = 1;
143+
dd->hp_ops = ops;
144+
dd->hp_context = context;
145+
dd->hp_release = release;
146+
}
147+
148+
if (!WARN_ON(ret) && init)
149+
init(context);
150+
151+
mutex_unlock(&hotplug_lock);
152+
return ret;
137153
}
138154

139155
/**
140-
* dock_del_hotplug_device - remove a hotplug handler from the dock station
141-
* @ds: The dock station
142-
* @dd: the dependent device struct
156+
* dock_release_hotplug - Decrement hotplug reference counter of dock device.
157+
* @dd: Dock-dependent device.
143158
*
144-
* Delete the dependent device from the dock's hotplug device list
159+
* Decrement the reference counter of @dd and if 0, detach its hotplug
160+
* operations from it, reset its context pointer and run the optional release
161+
* routine if present.
145162
*/
146-
static void
147-
dock_del_hotplug_device(struct dock_station *ds,
148-
struct dock_dependent_device *dd)
163+
static void dock_release_hotplug(struct dock_dependent_device *dd)
149164
{
150-
mutex_lock(&ds->hp_lock);
151-
list_del(&dd->hotplug_list);
152-
mutex_unlock(&ds->hp_lock);
165+
void (*release)(void *) = NULL;
166+
void *context = NULL;
167+
168+
mutex_lock(&hotplug_lock);
169+
170+
if (dd->hp_context && !--dd->hp_refcount) {
171+
dd->hp_ops = NULL;
172+
context = dd->hp_context;
173+
dd->hp_context = NULL;
174+
release = dd->hp_release;
175+
dd->hp_release = NULL;
176+
}
177+
178+
if (release && context)
179+
release(context);
180+
181+
mutex_unlock(&hotplug_lock);
182+
}
183+
184+
static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
185+
bool uevent)
186+
{
187+
acpi_notify_handler cb = NULL;
188+
bool run = false;
189+
190+
mutex_lock(&hotplug_lock);
191+
192+
if (dd->hp_context) {
193+
run = true;
194+
dd->hp_refcount++;
195+
if (dd->hp_ops)
196+
cb = uevent ? dd->hp_ops->uevent : dd->hp_ops->handler;
197+
}
198+
199+
mutex_unlock(&hotplug_lock);
200+
201+
if (!run)
202+
return;
203+
204+
if (cb)
205+
cb(dd->handle, event, dd->hp_context);
206+
207+
dock_release_hotplug(dd);
153208
}
154209

155210
/**
@@ -360,9 +415,8 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
360415
/*
361416
* First call driver specific hotplug functions
362417
*/
363-
list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
364-
if (dd->ops && dd->ops->handler)
365-
dd->ops->handler(dd->handle, event, dd->context);
418+
list_for_each_entry(dd, &ds->dependent_devices, list)
419+
dock_hotplug_event(dd, event, false);
366420

367421
/*
368422
* Now make sure that an acpi_device is created for each
@@ -398,9 +452,8 @@ static void dock_event(struct dock_station *ds, u32 event, int num)
398452
if (num == DOCK_EVENT)
399453
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
400454

401-
list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
402-
if (dd->ops && dd->ops->uevent)
403-
dd->ops->uevent(dd->handle, event, dd->context);
455+
list_for_each_entry(dd, &ds->dependent_devices, list)
456+
dock_hotplug_event(dd, event, true);
404457

405458
if (num != DOCK_EVENT)
406459
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
@@ -570,19 +623,24 @@ EXPORT_SYMBOL_GPL(unregister_dock_notifier);
570623
* @handle: the handle of the device
571624
* @ops: handlers to call after docking
572625
* @context: device specific data
626+
* @init: Optional initialization routine to run after registration
627+
* @release: Optional release routine to run on unregistration
573628
*
574629
* If a driver would like to perform a hotplug operation after a dock
575630
* event, they can register an acpi_notifiy_handler to be called by
576631
* the dock driver after _DCK is executed.
577632
*/
578-
int
579-
register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops,
580-
void *context)
633+
int register_hotplug_dock_device(acpi_handle handle,
634+
const struct acpi_dock_ops *ops, void *context,
635+
void (*init)(void *), void (*release)(void *))
581636
{
582637
struct dock_dependent_device *dd;
583638
struct dock_station *dock_station;
584639
int ret = -EINVAL;
585640

641+
if (WARN_ON(!context))
642+
return -EINVAL;
643+
586644
if (!dock_station_count)
587645
return -ENODEV;
588646

@@ -597,12 +655,8 @@ register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops
597655
* ops
598656
*/
599657
dd = find_dock_dependent_device(dock_station, handle);
600-
if (dd) {
601-
dd->ops = ops;
602-
dd->context = context;
603-
dock_add_hotplug_device(dock_station, dd);
658+
if (dd && !dock_init_hotplug(dd, ops, context, init, release))
604659
ret = 0;
605-
}
606660
}
607661

608662
return ret;
@@ -624,7 +678,7 @@ void unregister_hotplug_dock_device(acpi_handle handle)
624678
list_for_each_entry(dock_station, &dock_stations, sibling) {
625679
dd = find_dock_dependent_device(dock_station, handle);
626680
if (dd)
627-
dock_del_hotplug_device(dock_station, dd);
681+
dock_release_hotplug(dd);
628682
}
629683
}
630684
EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
@@ -953,7 +1007,6 @@ static int __init dock_add(acpi_handle handle)
9531007
mutex_init(&dock_station->hp_lock);
9541008
spin_lock_init(&dock_station->dd_lock);
9551009
INIT_LIST_HEAD(&dock_station->sibling);
956-
INIT_LIST_HEAD(&dock_station->hotplug_devices);
9571010
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
9581011
INIT_LIST_HEAD(&dock_station->dependent_devices);
9591012

@@ -993,30 +1046,6 @@ static int __init dock_add(acpi_handle handle)
9931046
return ret;
9941047
}
9951048

996-
/**
997-
* dock_remove - free up resources related to the dock station
998-
*/
999-
static int dock_remove(struct dock_station *ds)
1000-
{
1001-
struct dock_dependent_device *dd, *tmp;
1002-
struct platform_device *dock_device = ds->dock_device;
1003-
1004-
if (!dock_station_count)
1005-
return 0;
1006-
1007-
/* remove dependent devices */
1008-
list_for_each_entry_safe(dd, tmp, &ds->dependent_devices, list)
1009-
kfree(dd);
1010-
1011-
list_del(&ds->sibling);
1012-
1013-
/* cleanup sysfs */
1014-
sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group);
1015-
platform_device_unregister(dock_device);
1016-
1017-
return 0;
1018-
}
1019-
10201049
/**
10211050
* find_dock_and_bay - look for dock stations and bays
10221051
* @handle: acpi handle of a device
@@ -1035,7 +1064,7 @@ find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
10351064
return AE_OK;
10361065
}
10371066

1038-
static int __init dock_init(void)
1067+
int __init acpi_dock_init(void)
10391068
{
10401069
if (acpi_disabled)
10411070
return 0;
@@ -1054,19 +1083,3 @@ static int __init dock_init(void)
10541083
ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
10551084
return 0;
10561085
}
1057-
1058-
static void __exit dock_exit(void)
1059-
{
1060-
struct dock_station *tmp, *dock_station;
1061-
1062-
unregister_acpi_bus_notifier(&dock_acpi_notifier);
1063-
list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling)
1064-
dock_remove(dock_station);
1065-
}
1066-
1067-
/*
1068-
* Must be called before drivers of devices in dock, otherwise we can't know
1069-
* which devices are in a dock
1070-
*/
1071-
subsys_initcall(dock_init);
1072-
module_exit(dock_exit);

drivers/acpi/internal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ void acpi_container_init(void);
4040
#else
4141
static inline void acpi_container_init(void) {}
4242
#endif
43+
#ifdef CONFIG_ACPI_DOCK
44+
void acpi_dock_init(void);
45+
#else
46+
static inline void acpi_dock_init(void) {}
47+
#endif
4348
#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
4449
void acpi_memory_hotplug_init(void);
4550
#else

drivers/acpi/scan.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,6 +2042,7 @@ int __init acpi_scan_init(void)
20422042
acpi_lpss_init();
20432043
acpi_container_init();
20442044
acpi_memory_hotplug_init();
2045+
acpi_dock_init();
20452046

20462047
mutex_lock(&acpi_scan_lock);
20472048
/*

drivers/ata/libata-acpi.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,10 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
156156

157157
spin_unlock_irqrestore(ap->lock, flags);
158158

159-
if (wait)
159+
if (wait) {
160160
ata_port_wait_eh(ap);
161+
flush_work(&ap->hotplug_task.work);
162+
}
161163
}
162164

163165
static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data)
@@ -214,6 +216,39 @@ static const struct acpi_dock_ops ata_acpi_ap_dock_ops = {
214216
.uevent = ata_acpi_ap_uevent,
215217
};
216218

219+
void ata_acpi_hotplug_init(struct ata_host *host)
220+
{
221+
int i;
222+
223+
for (i = 0; i < host->n_ports; i++) {
224+
struct ata_port *ap = host->ports[i];
225+
acpi_handle handle;
226+
struct ata_device *dev;
227+
228+
if (!ap)
229+
continue;
230+
231+
handle = ata_ap_acpi_handle(ap);
232+
if (handle) {
233+
/* we might be on a docking station */
234+
register_hotplug_dock_device(handle,
235+
&ata_acpi_ap_dock_ops, ap,
236+
NULL, NULL);
237+
}
238+
239+
ata_for_each_dev(dev, &ap->link, ALL) {
240+
handle = ata_dev_acpi_handle(dev);
241+
if (!handle)
242+
continue;
243+
244+
/* we might be on a docking station */
245+
register_hotplug_dock_device(handle,
246+
&ata_acpi_dev_dock_ops,
247+
dev, NULL, NULL);
248+
}
249+
}
250+
}
251+
217252
/**
218253
* ata_acpi_dissociate - dissociate ATA host from ACPI objects
219254
* @host: target ATA host

drivers/ata/libata-core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6148,6 +6148,8 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
61486148
if (rc)
61496149
goto err_tadd;
61506150

6151+
ata_acpi_hotplug_init(host);
6152+
61516153
/* set cable, sata_spd_limit and report */
61526154
for (i = 0; i < host->n_ports; i++) {
61536155
struct ata_port *ap = host->ports[i];

drivers/ata/libata.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ extern int ata_acpi_register(void);
122122
extern void ata_acpi_unregister(void);
123123
extern void ata_acpi_bind(struct ata_device *dev);
124124
extern void ata_acpi_unbind(struct ata_device *dev);
125+
extern void ata_acpi_hotplug_init(struct ata_host *host);
125126
#else
126127
static inline void ata_acpi_dissociate(struct ata_host *host) { }
127128
static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; }
@@ -134,6 +135,7 @@ static inline int ata_acpi_register(void) { return 0; }
134135
static inline void ata_acpi_unregister(void) { }
135136
static inline void ata_acpi_bind(struct ata_device *dev) { }
136137
static inline void ata_acpi_unbind(struct ata_device *dev) { }
138+
static inline void ata_acpi_hotplug_init(struct ata_host *host) {}
137139
#endif
138140

139141
/* libata-scsi.c */

0 commit comments

Comments
 (0)