Driver core: Add offline/online device operations

In some cases, graceful hot-removal of devices is not possible,
although in principle the devices in question support hotplug.
For example, that may happen for the last CPU in the system or
for memory modules holding kernel memory.

In those cases it is nice to be able to check if the given device
can be gracefully hot-removed before triggering a removal procedure
that cannot be aborted or reversed.  Unfortunately, however, the
kernel currently doesn't provide any support for that.

To address that deficiency, introduce support for offline and
online operations that can be performed on devices, respectively,
before a hot-removal and in case when it is necessary (or convenient)
to put a device back online after a successful offline (that has not
been followed by removal).  The idea is that the offline will fail
whenever the given device cannot be gracefully removed from the
system and it will not be allowed to use the device after a
successful offline (until a subsequent online) in analogy with the
existing CPU offline/online mechanism.

For now, the offline and online operations are introduced at the
bus type level, as that should be sufficient for the most urgent use
cases (CPUs and memory modules).  In the future, however, the
approach may be extended to cover some more complicated device
offline/online scenarios involving device drivers etc.

The lock_device_hotplug() and unlock_device_hotplug() functions are
introduced because subsequent patches need to put larger pieces of
code under device_hotplug_lock to prevent race conditions between
device offline and removal from happening.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Toshi Kani <toshi.kani@hp.com>
diff --git a/include/linux/device.h b/include/linux/device.h
index c0a1261..eeb3331 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -71,6 +71,10 @@
  *		the specific driver's probe to initial the matched device.
  * @remove:	Called when a device removed from this bus.
  * @shutdown:	Called at shut-down time to quiesce the device.
+ *
+ * @online:	Called to put the device back online (after offlining it).
+ * @offline:	Called to put the device offline for hot-removal. May fail.
+ *
  * @suspend:	Called when a device on this bus wants to go to sleep mode.
  * @resume:	Called to bring a device on this bus out of sleep mode.
  * @pm:		Power management operations of this bus, callback the specific
@@ -104,6 +108,9 @@
 	int (*remove)(struct device *dev);
 	void (*shutdown)(struct device *dev);
 
+	int (*online)(struct device *dev);
+	int (*offline)(struct device *dev);
+
 	int (*suspend)(struct device *dev, pm_message_t state);
 	int (*resume)(struct device *dev);
 
@@ -648,6 +655,8 @@
  * @release:	Callback to free the device after all references have
  * 		gone away. This should be set by the allocator of the
  * 		device (i.e. the bus driver that discovered the device).
+ * @offline_disabled: If set, the device is permanently online.
+ * @offline:	Set after successful invocation of bus type's .offline().
  *
  * At the lowest level, every device in a Linux system is represented by an
  * instance of struct device. The device structure contains the information
@@ -720,6 +729,9 @@
 
 	void	(*release)(struct device *dev);
 	struct iommu_group	*iommu_group;
+
+	bool			offline_disabled:1;
+	bool			offline:1;
 };
 
 static inline struct device *kobj_to_dev(struct kobject *kobj)
@@ -856,6 +868,15 @@
 extern void *dev_get_drvdata(const struct device *dev);
 extern int dev_set_drvdata(struct device *dev, void *data);
 
+static inline bool device_supports_offline(struct device *dev)
+{
+	return dev->bus && dev->bus->offline && dev->bus->online;
+}
+
+extern void lock_device_hotplug(void);
+extern void unlock_device_hotplug(void);
+extern int device_offline(struct device *dev);
+extern int device_online(struct device *dev);
 /*
  * Root device objects for grouping under /sys/devices
  */