aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-12-29 09:25:48 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-12-29 09:25:48 -0500
commitcaa73ea158de9419f08e456f2716c71d1f06012a (patch)
treeca948019c9a0c142880fbfadf028d9114fafe529
parentd22ddcbc4fb7a483d0721eddfda3f0558821d372 (diff)
ACPI / hotplug / driver core: Handle containers in a special way
ACPI container devices require special hotplug handling, at least on some systems, since generally user space needs to carry out system-specific cleanup before it makes sense to offline devices in the container. However, the current ACPI hotplug code for containers first attempts to offline devices in the container and only then it notifies user space of the container offline. Moreover, after commit 202317a573b2 (ACPI / scan: Add acpi_device objects for all device nodes in the namespace), ACPI device objects representing containers are present as long as the ACPI namespace nodes corresponding to them are present, which may be forever, even if the container devices are physically detached from the system (the return values of the corresponding _STA methods change in those cases, but generally the namespace nodes themselves are still there). Thus it is useful to introduce entities representing containers that will go away during container hot-unplug. The goal of this change is to address both the above issues. The idea is to create a "companion" container system device for each of the ACPI container device objects during the initial namespace scan or on a hotplug event making the container present. That system device will be unregistered on container removal. A new bus type for container devices is added for this purpose, because device offline and online operations need to be defined for them. The online operation is a trivial function that is always successful and the offline uses a callback pointed to by the container device's offline member. For ACPI containers that callback simply walks the list of ACPI device objects right below the container object (its children) and checks if all of their physical companion devices are offline. If that's not the case, it returns -EBUSY and the container system devivce cannot be put offline. Consequently, to put the container system device offline, it is necessary to put all of the physical devices depending on its ACPI companion object offline beforehand. Container system devices created for ACPI container objects are initially online. They are created by the container ACPI scan handler whose hotplug.demand_offline flag is set. That causes acpi_scan_hot_remove() to check if the companion container system device is offline before attempting to remove an ACPI container or any devices below it. If the check fails, a KOBJ_CHANGE uevent is emitted for the container system device in question and user space is expected to offline all devices below the container and the container itself in response to it. Then, user space can finalize the removal of the container with the help of its ACPI device object's eject attribute in sysfs. Tested-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/acpi/container.c48
-rw-r--r--drivers/acpi/internal.h1
-rw-r--r--drivers/acpi/scan.c8
-rw-r--r--drivers/base/Makefile2
-rw-r--r--drivers/base/base.h1
-rw-r--r--drivers/base/container.c44
-rw-r--r--drivers/base/init.c1
-rw-r--r--include/linux/container.h25
8 files changed, 122 insertions, 8 deletions
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c
index 83d232c10f13..0b6ae6eb5c4a 100644
--- a/drivers/acpi/container.c
+++ b/drivers/acpi/container.c
@@ -27,8 +27,7 @@
27 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 27 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28 */ 28 */
29#include <linux/acpi.h> 29#include <linux/acpi.h>
30 30#include <linux/container.h>
31#include "internal.h"
32 31
33#include "internal.h" 32#include "internal.h"
34 33
@@ -44,16 +43,56 @@ static const struct acpi_device_id container_device_ids[] = {
44 {"", 0}, 43 {"", 0},
45}; 44};
46 45
46static int acpi_container_offline(struct container_dev *cdev)
47{
48 struct acpi_device *adev = ACPI_COMPANION(&cdev->dev);
49 struct acpi_device *child;
50
51 /* Check all of the dependent devices' physical companions. */
52 list_for_each_entry(child, &adev->children, node)
53 if (!acpi_scan_is_offline(child, false))
54 return -EBUSY;
55
56 return 0;
57}
58
59static void acpi_container_release(struct device *dev)
60{
61 kfree(to_container_dev(dev));
62}
63
47static int container_device_attach(struct acpi_device *adev, 64static int container_device_attach(struct acpi_device *adev,
48 const struct acpi_device_id *not_used) 65 const struct acpi_device_id *not_used)
49{ 66{
50 kobject_uevent(&adev->dev.kobj, KOBJ_ONLINE); 67 struct container_dev *cdev;
68 struct device *dev;
69 int ret;
70
71 cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
72 if (!cdev)
73 return -ENOMEM;
74
75 cdev->offline = acpi_container_offline;
76 dev = &cdev->dev;
77 dev->bus = &container_subsys;
78 dev_set_name(dev, "%s", dev_name(&adev->dev));
79 ACPI_COMPANION_SET(dev, adev);
80 dev->release = acpi_container_release;
81 ret = device_register(dev);
82 if (ret)
83 return ret;
84
85 adev->driver_data = dev;
51 return 1; 86 return 1;
52} 87}
53 88
54static void container_device_detach(struct acpi_device *adev) 89static void container_device_detach(struct acpi_device *adev)
55{ 90{
56 kobject_uevent(&adev->dev.kobj, KOBJ_OFFLINE); 91 struct device *dev = acpi_driver_data(adev);
92
93 adev->driver_data = NULL;
94 if (dev)
95 device_unregister(dev);
57} 96}
58 97
59static struct acpi_scan_handler container_handler = { 98static struct acpi_scan_handler container_handler = {
@@ -62,6 +101,7 @@ static struct acpi_scan_handler container_handler = {
62 .detach = container_device_detach, 101 .detach = container_device_detach,
63 .hotplug = { 102 .hotplug = {
64 .enabled = true, 103 .enabled = true,
104 .demand_offline = true,
65 }, 105 },
66}; 106};
67 107
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index b125fdb0b30c..3375129bb5b7 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -73,6 +73,7 @@ static inline void acpi_lpss_init(void) {}
73#endif 73#endif
74 74
75bool acpi_queue_hotplug_work(struct work_struct *work); 75bool acpi_queue_hotplug_work(struct work_struct *work);
76bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
76 77
77/* -------------------------------------------------------------------------- 78/* --------------------------------------------------------------------------
78 Device Node Initialization / Removal 79 Device Node Initialization / Removal
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 65243b9dd868..32b340171d41 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -126,7 +126,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
126} 126}
127static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); 127static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
128 128
129static bool acpi_scan_is_offline(struct acpi_device *adev) 129bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
130{ 130{
131 struct acpi_device_physical_node *pn; 131 struct acpi_device_physical_node *pn;
132 bool offline = true; 132 bool offline = true;
@@ -135,7 +135,9 @@ static bool acpi_scan_is_offline(struct acpi_device *adev)
135 135
136 list_for_each_entry(pn, &adev->physical_node_list, node) 136 list_for_each_entry(pn, &adev->physical_node_list, node)
137 if (device_supports_offline(pn->dev) && !pn->dev->offline) { 137 if (device_supports_offline(pn->dev) && !pn->dev->offline) {
138 kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); 138 if (uevent)
139 kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE);
140
139 offline = false; 141 offline = false;
140 break; 142 break;
141 } 143 }
@@ -267,7 +269,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
267 acpi_status status; 269 acpi_status status;
268 270
269 if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) { 271 if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) {
270 if (!acpi_scan_is_offline(device)) 272 if (!acpi_scan_is_offline(device, true))
271 return -EBUSY; 273 return -EBUSY;
272 } else { 274 } else {
273 int error = acpi_scan_try_to_offline(device); 275 int error = acpi_scan_try_to_offline(device);
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 94e8a80e87f8..d08c9d3b1d37 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -4,7 +4,7 @@ obj-y := core.o bus.o dd.o syscore.o \
4 driver.o class.o platform.o \ 4 driver.o class.o platform.o \
5 cpu.o firmware.o init.o map.o devres.o \ 5 cpu.o firmware.o init.o map.o devres.o \
6 attribute_container.o transport_class.o \ 6 attribute_container.o transport_class.o \
7 topology.o 7 topology.o container.o
8obj-$(CONFIG_DEVTMPFS) += devtmpfs.o 8obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
9obj-$(CONFIG_DMA_CMA) += dma-contiguous.o 9obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
10obj-y += power/ 10obj-y += power/
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 2cbc6774f4cd..24f424249d9b 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -100,6 +100,7 @@ static inline int hypervisor_init(void) { return 0; }
100#endif 100#endif
101extern int platform_bus_init(void); 101extern int platform_bus_init(void);
102extern void cpu_dev_init(void); 102extern void cpu_dev_init(void);
103extern void container_dev_init(void);
103 104
104struct kobject *virtual_device_parent(struct device *dev); 105struct kobject *virtual_device_parent(struct device *dev);
105 106
diff --git a/drivers/base/container.c b/drivers/base/container.c
new file mode 100644
index 000000000000..ecbfbe2e908f
--- /dev/null
+++ b/drivers/base/container.c
@@ -0,0 +1,44 @@
1/*
2 * System bus type for containers.
3 *
4 * Copyright (C) 2013, Intel Corporation
5 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/container.h>
13
14#include "base.h"
15
16#define CONTAINER_BUS_NAME "container"
17
18static int trivial_online(struct device *dev)
19{
20 return 0;
21}
22
23static int container_offline(struct device *dev)
24{
25 struct container_dev *cdev = to_container_dev(dev);
26
27 return cdev->offline ? cdev->offline(cdev) : 0;
28}
29
30struct bus_type container_subsys = {
31 .name = CONTAINER_BUS_NAME,
32 .dev_name = CONTAINER_BUS_NAME,
33 .online = trivial_online,
34 .offline = container_offline,
35};
36
37void __init container_dev_init(void)
38{
39 int ret;
40
41 ret = subsys_system_register(&container_subsys, NULL);
42 if (ret)
43 pr_err("%s() failed: %d\n", __func__, ret);
44}
diff --git a/drivers/base/init.c b/drivers/base/init.c
index c16f0b808a17..da033d3bab3c 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -33,4 +33,5 @@ void __init driver_init(void)
33 platform_bus_init(); 33 platform_bus_init();
34 cpu_dev_init(); 34 cpu_dev_init();
35 memory_dev_init(); 35 memory_dev_init();
36 container_dev_init();
36} 37}
diff --git a/include/linux/container.h b/include/linux/container.h
new file mode 100644
index 000000000000..3c03e6fd2035
--- /dev/null
+++ b/include/linux/container.h
@@ -0,0 +1,25 @@
1/*
2 * Definitions for container bus type.
3 *
4 * Copyright (C) 2013, Intel Corporation
5 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/device.h>
13
14/* drivers/base/power/container.c */
15extern struct bus_type container_subsys;
16
17struct container_dev {
18 struct device dev;
19 int (*offline)(struct container_dev *cdev);
20};
21
22static inline struct container_dev *to_container_dev(struct device *dev)
23{
24 return container_of(dev, struct container_dev, dev);
25}