Thread (31 messages) 31 messages, 2 authors, 2012-10-08
STALE5030d

[RFC PATCH v3 05/28] ACPI: introduce interfaces to manage ACPI device reference count

From: Jiang Liu <hidden>
Date: 2012-10-06 15:30:33
Also in: linux-acpi, lkml
Subsystem: acpi, acpi component architecture (acpica), drm driver for nvidia geforce/quadro gpus, drm drivers, drm drivers and misc gpu patches, intel drm i915 driver (meteor lake, dg2 and older excluding poulsbo, moorestown and derivative), pci subsystem, pnp support, the rest, x86 platform drivers · Maintainers: "Rafael J. Wysocki", Saket Dumbre, Lyude Paul, Danilo Krummrich, David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin, Bjorn Helgaas, Linus Torvalds, Hans de Goede, Ilpo Järvinen

Function acpi_bus_get_device() return an ACPI device without holding a
reference count the device, which is not safe to ACPI hotplug operations.

So change acpi_bus_get_device() to hold a reference count on the returned
ACPI device and introduces acpi_get_device()/acpi_put_device() to manage
ACPI device reference count.

This patch is still incomplete, need to check and modify all callers of
acpi_bus_get_device().

Signed-off-by: Jiang Liu <redacted>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/bus.c                     |   22 +++++++++++++++++++---
 drivers/acpi/internal.h                |    3 +++
 drivers/acpi/scan.c                    |    6 ++++++
 drivers/gpu/drm/i915/intel_opregion.c  |    2 ++
 drivers/gpu/drm/nouveau/nouveau_acpi.c |    1 +
 drivers/pci/hotplug/acpiphp_glue.c     |    9 +++++++--
 drivers/pci/hotplug/acpiphp_ibm.c      |    5 ++++-
 drivers/pci/hotplug/sgi_hotplug.c      |    6 +++++-
 drivers/platform/x86/thinkpad_acpi.c   |    2 ++
 drivers/pnp/pnpacpi/core.c             |   23 ++++++++++-------------
 include/acpi/acpi_bus.h                |   16 +++++++++++++++-
 11 files changed, 74 insertions(+), 21 deletions(-)
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index e059695..1f85b0b 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -89,26 +89,42 @@ static struct dmi_system_id dsdt_dmi_table[] __initdata = {
                                 Device Management
    -------------------------------------------------------------------------- */
 
-int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
+bool acpi_bus_has_device(acpi_handle handle)
 {
-	acpi_status status = AE_OK;
+	acpi_status status;
+	struct acpi_device *device = NULL;
+
+	down_read(&acpi_device_data_handler_sem);
+	status = acpi_get_data(handle, acpi_bus_data_handler, (void **)&device);
+	up_read(&acpi_device_data_handler_sem);
+
+	return ACPI_SUCCESS(status) && device;
+}
+EXPORT_SYMBOL(acpi_bus_has_device);
 
+int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
+{
+	acpi_status status;
 
 	if (!device)
 		return -EINVAL;
 
 	/* TBD: Support fixed-feature devices */
 
+	down_read(&acpi_device_data_handler_sem);
 	status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device);
 	if (ACPI_FAILURE(status) || !*device) {
+		up_read(&acpi_device_data_handler_sem);
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
 				  handle));
 		return -ENODEV;
+	} else {
+		get_device(&(*device)->dev);
+		up_read(&acpi_device_data_handler_sem);
 	}
 
 	return 0;
 }
-
 EXPORT_SYMBOL(acpi_bus_get_device);
 
 acpi_status acpi_bus_get_status_handle(acpi_handle handle,
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index ca75b9c..70843c0 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -93,4 +93,7 @@ static inline int suspend_nvs_save(void) { return 0; }
 static inline void suspend_nvs_restore(void) {}
 #endif
 
+extern struct rw_semaphore acpi_device_data_handler_sem;
+void acpi_bus_data_handler(acpi_handle handle, void *context);
+
 #endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index d1ecca2..a1d5188 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -10,6 +10,7 @@
 #include <linux/signal.h>
 #include <linux/kthread.h>
 #include <linux/dmi.h>
+#include <linux/sem.h>
 
 #include <acpi/acpi_drivers.h>
 
@@ -32,6 +33,7 @@ static LIST_HEAD(acpi_device_list);
 static LIST_HEAD(acpi_bus_id_list);
 DEFINE_MUTEX(acpi_device_lock);
 LIST_HEAD(acpi_wakeup_device_list);
+DECLARE_RWSEM(acpi_device_data_handler_sem);
 
 struct acpi_device_bus_id{
 	char bus_id[15];
@@ -552,7 +554,9 @@ static void acpi_device_unregister(struct acpi_device *device, int type)
 	list_del(&device->wakeup_list);
 	mutex_unlock(&acpi_device_lock);
 
+	down_write(&acpi_device_data_handler_sem);
 	acpi_detach_data(device->handle, acpi_bus_data_handler);
+	up_write(&acpi_device_data_handler_sem);
 
 	acpi_device_remove_files(device);
 	device_unregister(&device->dev);
@@ -1215,8 +1219,10 @@ static int acpi_device_set_context(struct acpi_device *device)
 	if (!device->handle)
 		return 0;
 
+	down_write(&acpi_device_data_handler_sem);
 	status = acpi_attach_data(device->handle,
 				  acpi_bus_data_handler, device);
+	up_write(&acpi_device_data_handler_sem);
 	if (ACPI_SUCCESS(status))
 		return 0;
 
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index 18bd0af..da7dd48 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -359,6 +359,8 @@ static void intel_didl_outputs(struct drm_device *dev)
 		}
 	}
 
+	acpi_device_put(acpi_dev);
+
 	if (!acpi_video_bus) {
 		pr_warn("No ACPI video bus found\n");
 		return;
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index 26ebffe..69d2e22 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -417,6 +417,7 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
 		return -ENODEV;
 
 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
+	acpi_device_put(acpidev);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 3d6d4fd..1016c38 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -749,11 +749,13 @@ static int acpiphp_bus_add(struct acpiphp_func *func)
 		 * the bus then re-add it...
 		 */
 		ret_val = acpi_bus_trim(device, 1);
+		acpi_device_put(device);
 		dbg("acpi_bus_trim return %x\n", ret_val);
 	}
 
 	ret_val = acpi_bus_add(&device, pdevice, func->handle,
 		ACPI_BUS_TYPE_DEVICE);
+	acpi_device_put(pdevice);
 	if (ret_val) {
 		dbg("error adding bus, %x\n",
 			-ret_val);
@@ -785,6 +787,8 @@ static int acpiphp_bus_trim(acpi_handle handle)
 	if (retval)
 		err("cannot remove from acpi list\n");
 
+	acpi_device_put(device);
+
 	return retval;
 }
 
@@ -1146,8 +1150,10 @@ static void handle_bridge_insertion(acpi_handle handle, u32 type)
 	}
 	if (acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE)) {
 		err("cannot add bridge to acpi list\n");
+		acpi_device_put(pdevice);
 		return;
 	}
+	acpi_device_put(pdevice);
 	if (!acpiphp_configure_bridge(handle) &&
 		!acpi_bus_start(device))
 		add_bridge(handle);
@@ -1224,7 +1230,6 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
 	char objname[64];
 	struct acpi_buffer buffer = { .length = sizeof(objname),
 				      .pointer = objname };
-	struct acpi_device *device;
 	int num_sub_bridges = 0;
 	struct acpiphp_hp_work *hp_work;
 	acpi_handle handle;
@@ -1234,7 +1239,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
 	handle = hp_work->handle;
 	type = hp_work->type;
 
-	if (acpi_bus_get_device(handle, &device)) {
+	if (!acpi_bus_has_device(handle)) {
 		/* This bridge must have just been physically inserted */
 		handle_bridge_insertion(handle, type);
 		goto out;
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index c35e8ad..ecd4b8d 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -450,7 +450,7 @@ static int __init ibm_acpiphp_init(void)
 	}
 	if (acpiphp_register_attention(&ibm_attention_info)) {
 		retval = -ENODEV;
-		goto init_return;
+		goto put_device;
 	}
 
 	ibm_note.device = device;
@@ -471,6 +471,8 @@ static int __init ibm_acpiphp_init(void)
 
 init_cleanup:
 	acpiphp_unregister_attention(&ibm_attention_info);
+put_device:
+	acpi_device_put(device);
 init_return:
 	return retval;
 }
@@ -493,6 +495,7 @@ static void __exit ibm_acpiphp_exit(void)
 		err("%s: Notification handler removal failed\n", __func__);
 	/* remove the /sys entries */
 	sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
+	acpi_device_put(ibm_note.device);
 }
 
 module_init(ibm_acpiphp_init);
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index f64ca92..b02c99e 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -462,6 +462,8 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
 				}
 			}
 		}
+
+		acpi_device_put(pdevice);
 	}
 
 	/* Call the driver for the new device */
@@ -538,8 +540,10 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
 
 				ret = acpi_bus_get_device(chandle,
 							  &device);
-				if (ACPI_SUCCESS(ret))
+				if (ACPI_SUCCESS(ret)) {
 					acpi_bus_trim(device, 1);
+					acpi_device_put(device);
+				}
 			}
 		}
 
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 52daaa8..56e421a 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -782,6 +782,7 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
 			pr_err("acpi_install_notify_handler(%s) failed: %s\n",
 			       ibm->name, acpi_format_exception(status));
 		}
+		acpi_device_put(ibm->acpi->device);
 		return -ENODEV;
 	}
 	ibm->flags.acpi_notify_installed = 1;
@@ -8464,6 +8465,7 @@ static void ibm_exit(struct ibm_struct *ibm)
 		acpi_remove_notify_handler(*ibm->acpi->handle,
 					   ibm->acpi->type,
 					   dispatch_acpi_notify);
+		acpi_device_put(ibm->acpi->device);
 		ibm->flags.acpi_notify_installed = 0;
 	}
 
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index 507a8e2..0c5c0f4 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/drivers/pnp/pnpacpi/core.c
@@ -82,7 +82,6 @@ static int pnpacpi_get_resources(struct pnp_dev *dev)
 
 static int pnpacpi_set_resources(struct pnp_dev *dev)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle;
 	struct acpi_buffer buffer;
 	int ret;
@@ -90,7 +89,7 @@ static int pnpacpi_set_resources(struct pnp_dev *dev)
 	pnp_dbg(&dev->dev, "set resources\n");
 
 	handle = DEVICE_ACPI_HANDLE(&dev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return -ENODEV;
 	}
@@ -113,14 +112,13 @@ static int pnpacpi_set_resources(struct pnp_dev *dev)
 
 static int pnpacpi_disable_resources(struct pnp_dev *dev)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle;
 	int ret;
 
 	dev_dbg(&dev->dev, "disable resources\n");
 
 	handle = DEVICE_ACPI_HANDLE(&dev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return 0;
 	}
@@ -138,11 +136,10 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev)
 #ifdef CONFIG_ACPI_SLEEP
 static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle;
 
 	handle = DEVICE_ACPI_HANDLE(&dev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return false;
 	}
@@ -152,12 +149,11 @@ static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
 
 static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle;
 	int error = 0;
 
 	handle = DEVICE_ACPI_HANDLE(&dev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return 0;
 	}
@@ -190,11 +186,10 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 
 static int pnpacpi_resume(struct pnp_dev *dev)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
 	int error = 0;
 
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return -ENODEV;
 	}
@@ -310,10 +305,12 @@ static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
 {
 	struct acpi_device *device;
 
-	if (!acpi_bus_get_device(handle, &device))
-		pnpacpi_add_device(device);
-	else
+	if (acpi_bus_get_device(handle, &device))
 		return AE_CTRL_DEPTH;
+
+	pnpacpi_add_device(device);
+	acpi_device_put(device);
+
 	return AE_OK;
 }
 
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index bde976e..0a109fc 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -349,8 +349,22 @@ extern void unregister_acpi_bus_notifier(struct notifier_block *nb);
  * External Functions
  */
 
+static inline struct acpi_device *acpi_device_get(struct acpi_device *d)
+{
+	if (d)
+		get_device(&d->dev);
+
+	return d;
+}
+
+static inline void acpi_device_put(struct acpi_device *d)
+{
+	if (d)
+		put_device(&d->dev);
+}
+
+bool acpi_bus_has_device(acpi_handle handle);
 int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device);
-void acpi_bus_data_handler(acpi_handle handle, void *context);
 acpi_status acpi_bus_get_status_handle(acpi_handle handle,
 				       unsigned long long *sta);
 int acpi_bus_get_status(struct acpi_device *device);
-- 
1.7.9.5
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help