caa73ea158
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 202317a573
(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>
153 lines
5.3 KiB
C
153 lines
5.3 KiB
C
#include <linux/notifier.h>
|
|
|
|
/**
|
|
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
|
|
*
|
|
* @subsys - the struct kset that defines this subsystem
|
|
* @devices_kset - the subsystem's 'devices' directory
|
|
* @interfaces - list of subsystem interfaces associated
|
|
* @mutex - protect the devices, and interfaces lists.
|
|
*
|
|
* @drivers_kset - the list of drivers associated
|
|
* @klist_devices - the klist to iterate over the @devices_kset
|
|
* @klist_drivers - the klist to iterate over the @drivers_kset
|
|
* @bus_notifier - the bus notifier list for anything that cares about things
|
|
* on this bus.
|
|
* @bus - pointer back to the struct bus_type that this structure is associated
|
|
* with.
|
|
*
|
|
* @glue_dirs - "glue" directory to put in-between the parent device to
|
|
* avoid namespace conflicts
|
|
* @class - pointer back to the struct class that this structure is associated
|
|
* with.
|
|
*
|
|
* This structure is the one that is the actual kobject allowing struct
|
|
* bus_type/class to be statically allocated safely. Nothing outside of the
|
|
* driver core should ever touch these fields.
|
|
*/
|
|
struct subsys_private {
|
|
struct kset subsys;
|
|
struct kset *devices_kset;
|
|
struct list_head interfaces;
|
|
struct mutex mutex;
|
|
|
|
struct kset *drivers_kset;
|
|
struct klist klist_devices;
|
|
struct klist klist_drivers;
|
|
struct blocking_notifier_head bus_notifier;
|
|
unsigned int drivers_autoprobe:1;
|
|
struct bus_type *bus;
|
|
|
|
struct kset glue_dirs;
|
|
struct class *class;
|
|
};
|
|
#define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj)
|
|
|
|
struct driver_private {
|
|
struct kobject kobj;
|
|
struct klist klist_devices;
|
|
struct klist_node knode_bus;
|
|
struct module_kobject *mkobj;
|
|
struct device_driver *driver;
|
|
};
|
|
#define to_driver(obj) container_of(obj, struct driver_private, kobj)
|
|
|
|
/**
|
|
* struct device_private - structure to hold the private to the driver core portions of the device structure.
|
|
*
|
|
* @klist_children - klist containing all children of this device
|
|
* @knode_parent - node in sibling list
|
|
* @knode_driver - node in driver list
|
|
* @knode_bus - node in bus list
|
|
* @deferred_probe - entry in deferred_probe_list which is used to retry the
|
|
* binding of drivers which were unable to get all the resources needed by
|
|
* the device; typically because it depends on another driver getting
|
|
* probed first.
|
|
* @driver_data - private pointer for driver specific info. Will turn into a
|
|
* list soon.
|
|
* @device - pointer back to the struct class that this structure is
|
|
* associated with.
|
|
*
|
|
* Nothing outside of the driver core should ever touch these fields.
|
|
*/
|
|
struct device_private {
|
|
struct klist klist_children;
|
|
struct klist_node knode_parent;
|
|
struct klist_node knode_driver;
|
|
struct klist_node knode_bus;
|
|
struct list_head deferred_probe;
|
|
void *driver_data;
|
|
struct device *device;
|
|
};
|
|
#define to_device_private_parent(obj) \
|
|
container_of(obj, struct device_private, knode_parent)
|
|
#define to_device_private_driver(obj) \
|
|
container_of(obj, struct device_private, knode_driver)
|
|
#define to_device_private_bus(obj) \
|
|
container_of(obj, struct device_private, knode_bus)
|
|
|
|
extern int device_private_init(struct device *dev);
|
|
|
|
/* initialisation functions */
|
|
extern int devices_init(void);
|
|
extern int buses_init(void);
|
|
extern int classes_init(void);
|
|
extern int firmware_init(void);
|
|
#ifdef CONFIG_SYS_HYPERVISOR
|
|
extern int hypervisor_init(void);
|
|
#else
|
|
static inline int hypervisor_init(void) { return 0; }
|
|
#endif
|
|
extern int platform_bus_init(void);
|
|
extern void cpu_dev_init(void);
|
|
extern void container_dev_init(void);
|
|
|
|
struct kobject *virtual_device_parent(struct device *dev);
|
|
|
|
extern int bus_add_device(struct device *dev);
|
|
extern void bus_probe_device(struct device *dev);
|
|
extern void bus_remove_device(struct device *dev);
|
|
|
|
extern int bus_add_driver(struct device_driver *drv);
|
|
extern void bus_remove_driver(struct device_driver *drv);
|
|
|
|
extern void driver_detach(struct device_driver *drv);
|
|
extern int driver_probe_device(struct device_driver *drv, struct device *dev);
|
|
extern void driver_deferred_probe_del(struct device *dev);
|
|
static inline int driver_match_device(struct device_driver *drv,
|
|
struct device *dev)
|
|
{
|
|
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
|
|
}
|
|
|
|
extern int driver_add_groups(struct device_driver *drv,
|
|
const struct attribute_group **groups);
|
|
extern void driver_remove_groups(struct device_driver *drv,
|
|
const struct attribute_group **groups);
|
|
|
|
extern int device_add_groups(struct device *dev,
|
|
const struct attribute_group **groups);
|
|
extern void device_remove_groups(struct device *dev,
|
|
const struct attribute_group **groups);
|
|
|
|
extern char *make_class_name(const char *name, struct kobject *kobj);
|
|
|
|
extern int devres_release_all(struct device *dev);
|
|
|
|
/* /sys/devices directory */
|
|
extern struct kset *devices_kset;
|
|
|
|
#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
|
|
extern void module_add_driver(struct module *mod, struct device_driver *drv);
|
|
extern void module_remove_driver(struct device_driver *drv);
|
|
#else
|
|
static inline void module_add_driver(struct module *mod,
|
|
struct device_driver *drv) { }
|
|
static inline void module_remove_driver(struct device_driver *drv) { }
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEVTMPFS
|
|
extern int devtmpfs_init(void);
|
|
#else
|
|
static inline int devtmpfs_init(void) { return 0; }
|
|
#endif
|