All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Rafael J. Wysocki" <rjw@sisk.pl>
To: ACPI Devel Maling List <linux-acpi@vger.kernel.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>,
	LKML <linux-kernel@vger.kernel.org>,
	Yinghai Lu <yinghai@kernel.org>, Toshi Kani <toshi.kani@hp.com>,
	Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>,
	Jiang Liu <liuj97@gmail.com>
Subject: [PATCH 2/7] ACPI / scan: Introduce common code for ACPI-based device hotplug
Date: Sun, 17 Feb 2013 16:21:45 +0100	[thread overview]
Message-ID: <2540040.4NPGdn6Df0@vostro.rjw.lan> (raw)
In-Reply-To: <3260206.bhaAobGhpZ@vostro.rjw.lan>

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Multiple drivers handling hotplug-capable ACPI device nodes install
notify handlers covering the same types of events in a very similar
way.  Moreover, those handlers are installed in separate namespace
walks, although that really should be done during namespace scans
carried out by acpi_bus_scan().  This leads to substantial code
duplication, unnecessary overhead and behavior that is hard to
follow.

For this reason, introduce common code in drivers/acpi/scan.c for
handling hotplug-related notification and carrying out device
insertion and eject operations in a generic fashion, such that it
may be used by all of the relevant drivers in the future.  To cover
the existing differences between those drivers introduce struct
acpi_hotplug_profile for representing collections of hotplug
settings associated with different ACPI scan handlers that can be
used by the drivers to make the common code reflect their current
behavior.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/acpi/scan.c     |  271 +++++++++++++++++++++++++++++++++++++-----------
 include/acpi/acpi_bus.h |    7 +
 2 files changed, 220 insertions(+), 58 deletions(-)

Index: test/include/acpi/acpi_bus.h
===================================================================
--- test.orig/include/acpi/acpi_bus.h
+++ test/include/acpi/acpi_bus.h
@@ -88,11 +88,18 @@ struct acpi_device;
  * -----------------
  */
 
+struct acpi_hotplug_profile {
+	bool enabled:1;
+	bool uevents:1;
+	bool autoeject:1;
+};
+
 struct acpi_scan_handler {
 	const struct acpi_device_id *ids;
 	struct list_head list_node;
 	int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id);
 	void (*detach)(struct acpi_device *dev);
+	struct acpi_hotplug_profile hotplug;
 };
 
 /*
Index: test/drivers/acpi/scan.c
===================================================================
--- test.orig/drivers/acpi/scan.c
+++ test/drivers/acpi/scan.c
@@ -107,32 +107,19 @@ acpi_device_modalias_show(struct device
 }
 static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
 
-/**
- * acpi_bus_hot_remove_device: hot-remove a device and its children
- * @context: struct acpi_eject_event pointer (freed in this func)
- *
- * Hot-remove a device and its children. This function frees up the
- * memory space passed by arg context, so that the caller may call
- * this function asynchronously through acpi_os_hotplug_execute().
- */
-void acpi_bus_hot_remove_device(void *context)
+static int acpi_scan_hot_remove(struct acpi_device *device)
 {
-	struct acpi_eject_event *ej_event = context;
-	struct acpi_device *device = ej_event->device;
 	acpi_handle handle = device->handle;
-	acpi_handle temp;
+	acpi_handle not_used;
 	struct acpi_object_list arg_list;
 	union acpi_object arg;
-	acpi_status status = AE_OK;
-	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
-
-	mutex_lock(&acpi_scan_lock);
+	acpi_status status;
 
 	/* If there is no handle, the device node has been unregistered. */
-	if (!device->handle) {
+	if (!handle) {
 		dev_dbg(&device->dev, "ACPI handle missing\n");
 		put_device(&device->dev);
-		goto out;
+		return -EINVAL;
 	}
 
 	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@@ -143,7 +130,7 @@ void acpi_bus_hot_remove_device(void *co
 	put_device(&device->dev);
 	device = NULL;
 
-	if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) {
+	if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &not_used))) {
 		arg_list.count = 1;
 		arg_list.pointer = &arg;
 		arg.type = ACPI_TYPE_INTEGER;
@@ -161,18 +148,162 @@ void acpi_bus_hot_remove_device(void *co
 	 */
 	status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
 	if (ACPI_FAILURE(status)) {
-		if (status != AE_NOT_FOUND)
+		if (status == AE_NOT_FOUND) {
+			return -ENODEV;
+		} else {
 			acpi_handle_warn(handle, "Eject failed\n");
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+static void acpi_bus_device_eject(void *context)
+{
+	acpi_handle handle = context;
+	struct acpi_device *device = NULL;
+	struct acpi_scan_handler *handler;
+	u32 ost_source = ACPI_NOTIFY_EJECT_REQUEST;
+	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+
+	mutex_lock(&acpi_scan_lock);
 
-		/* Tell the firmware the hot-remove operation has failed. */
-		acpi_evaluate_hotplug_ost(handle, ej_event->event,
-					  ost_code, NULL);
+	acpi_bus_get_device(handle, &device);
+	if (!device)
+		goto out;
+
+	handler = device->handler;
+	if (!handler || !handler->hotplug.enabled) {
+		ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
+		goto out;
 	}
+	acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
+				  ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
+	if (handler->hotplug.uevents)
+		kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
+
+	if (handler->hotplug.autoeject) {
+		int error;
+
+		ost_source = ACPI_OST_EC_OSPM_EJECT;
+		get_device(&device->dev);
+		error = acpi_scan_hot_remove(device);
+		if (!error)
+			ost_code = ACPI_OST_SC_SUCCESS;
+	} else {
+		device->flags.eject_pending = true;
+		goto out_unlock;
+	}
+
+ out:
+	acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL);
+
+ out_unlock:
+	mutex_unlock(&acpi_scan_lock);
+}
+
+static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
+{
+	struct acpi_device *device = NULL;
+	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+	int error;
+
+	mutex_lock(&acpi_scan_lock);
+
+	acpi_bus_get_device(handle, &device);
+	if (device) {
+		dev_warn(&device->dev, "Attempt to re-insert\n");
+		goto out;
+	}
+	acpi_evaluate_hotplug_ost(handle, ost_source,
+				  ACPI_OST_SC_INSERT_IN_PROGRESS, NULL);
+	ost_source = ACPI_OST_EC_OSPM_INSERTION;
+	error = acpi_bus_scan(handle);
+	if (error) {
+		acpi_handle_warn(handle, "Namespace scan failure\n");
+		goto out;
+	}
+	error = acpi_bus_get_device(handle, &device);
+	if (error) {
+		acpi_handle_warn(handle, "Missing device node object\n");
+		goto out;
+	}
+	ost_code = ACPI_OST_SC_SUCCESS;
+	if (device->handler && device->handler->hotplug.uevents)
+		kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
 
  out:
+	acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL);
+	mutex_unlock(&acpi_scan_lock);
+}
+
+static void acpi_scan_bus_check(void *context)
+{
+	acpi_scan_bus_device_check((acpi_handle)context,
+				   ACPI_NOTIFY_BUS_CHECK);
+}
+
+static void acpi_scan_device_check(void *context)
+{
+	acpi_scan_bus_device_check((acpi_handle)context,
+				   ACPI_NOTIFY_DEVICE_CHECK);
+}
+
+static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *not_used)
+{
+	acpi_osd_exec_callback callback;
+	acpi_status status;
+
+	switch (type) {
+	case ACPI_NOTIFY_BUS_CHECK:
+		acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
+		callback = acpi_scan_bus_check;
+		break;
+	case ACPI_NOTIFY_DEVICE_CHECK:
+		acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
+		callback = acpi_scan_device_check;
+		break;
+	case ACPI_NOTIFY_EJECT_REQUEST:
+		acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
+		callback = acpi_bus_device_eject;
+		break;
+	default:
+		/* non-hotplug event; possibly handled by other handler */
+		return;
+	}
+	status = acpi_os_hotplug_execute(callback, handle);
+	if (ACPI_FAILURE(status))
+		acpi_evaluate_hotplug_ost(handle, type,
+					  ACPI_OST_SC_NON_SPECIFIC_FAILURE,
+					  NULL);
+}
+
+/**
+ * acpi_bus_hot_remove_device: hot-remove a device and its children
+ * @context: struct acpi_eject_event pointer (freed in this func)
+ *
+ * Hot-remove a device and its children. This function frees up the
+ * memory space passed by arg context, so that the caller may call
+ * this function asynchronously through acpi_os_hotplug_execute().
+ */
+void acpi_bus_hot_remove_device(void *context)
+{
+	struct acpi_eject_event *ej_event = context;
+	struct acpi_device *device = ej_event->device;
+	acpi_handle handle = device->handle;
+	u32 ost_code = ACPI_OST_SC_SUCCESS;
+	int error;
+
+	mutex_lock(&acpi_scan_lock);
+
+	error = acpi_scan_hot_remove(device);
+	if (error)
+		ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+
+	acpi_evaluate_hotplug_ost(handle, ej_event->event, ost_code, NULL);
+
 	mutex_unlock(&acpi_scan_lock);
 	kfree(context);
-	return;
 }
 EXPORT_SYMBOL(acpi_bus_hot_remove_device);
 
@@ -206,50 +337,48 @@ static ssize_t
 acpi_eject_store(struct device *d, struct device_attribute *attr,
 		const char *buf, size_t count)
 {
-	int ret = count;
-	acpi_status status;
-	acpi_object_type type = 0;
 	struct acpi_device *acpi_device = to_acpi_device(d);
-	struct acpi_eject_event *ej_event;
+	acpi_object_type not_used;
+	acpi_status status;
+	u32 ost_source;
+	u32 ost_code;
+	int ret;
 
-	if ((!count) || (buf[0] != '1')) {
+	if (!count || buf[0] != '1')
 		return -EINVAL;
-	}
-	if (!acpi_device->driver && !acpi_device->handler) {
-		ret = -ENODEV;
-		goto err;
-	}
-	status = acpi_get_type(acpi_device->handle, &type);
-	if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) {
-		ret = -ENODEV;
-		goto err;
-	}
 
-	ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
-	if (!ej_event) {
-		ret = -ENOMEM;
-		goto err;
-	}
+	if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)
+	    && !acpi_device->driver)
+		return -ENODEV;
+
+	status = acpi_get_type(acpi_device->handle, &not_used);
+	if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
+		return -ENODEV;
+
+	mutex_lock(&acpi_scan_lock);
 
-	get_device(&acpi_device->dev);
-	ej_event->device = acpi_device;
 	if (acpi_device->flags.eject_pending) {
-		/* event originated from ACPI eject notification */
-		ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;
+		/* ACPI eject notification event. */
+		ost_source = ACPI_NOTIFY_EJECT_REQUEST;
 		acpi_device->flags.eject_pending = 0;
 	} else {
-		/* event originated from user */
-		ej_event->event = ACPI_OST_EC_OSPM_EJECT;
-		(void) acpi_evaluate_hotplug_ost(acpi_device->handle,
-			ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
+		/* Eject initiated by user space. */
+		ost_source = ACPI_OST_EC_OSPM_EJECT;
 	}
-
-	status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event);
-	if (ACPI_FAILURE(status)) {
-		put_device(&acpi_device->dev);
-		kfree(ej_event);
+	acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source,
+				  ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
+	get_device(&acpi_device->dev);
+	ret = acpi_scan_hot_remove(acpi_device);
+	if (ret) {
+		ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+	} else {
+		ost_code = ACPI_OST_SC_SUCCESS;
+		ret = count;
 	}
-err:
+	acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
+				  ost_code, NULL);
+
+	mutex_unlock(&acpi_scan_lock);
 	return ret;
 }
 
@@ -1548,6 +1677,30 @@ static struct acpi_scan_handler *acpi_sc
 	return NULL;
 }
 
+static void acpi_scan_init_hotplug(acpi_handle handle)
+{
+	struct acpi_device_info *info;
+	struct acpi_scan_handler *handler;
+
+	if (ACPI_FAILURE(acpi_get_object_info(handle, &info)))
+		return;
+
+	if (!(info->valid & ACPI_VALID_HID)) {
+		kfree(info);
+		return;
+	}
+
+	/*
+	 * This relies on the fact that acpi_install_notify_handler() will not
+	 * install the same notify handler routine twice for the same handle.
+	 */
+	handler = acpi_scan_match_handler(info->hardware_id.string, NULL);
+	kfree(info);
+	if (handler && handler->hotplug.enabled)
+		acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+					    acpi_hotplug_notify_cb, NULL);
+}
+
 static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
 				      void *not_used, void **return_value)
 {
@@ -1570,6 +1723,8 @@ static acpi_status acpi_bus_check_add(ac
 		return AE_OK;
 	}
 
+	acpi_scan_init_hotplug(handle);
+
 	if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
 	    !(sta & ACPI_STA_DEVICE_FUNCTIONING)) {
 		struct acpi_device_wakeup wakeup;

  parent reply	other threads:[~2013-02-17 15:21 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-02-17 15:18 [PATCH 0/7] ACPI / hotplug: Common code for ACPI-based hotplug Rafael J. Wysocki
2013-02-17 15:20 ` [PATCH 1/7] ACPI / scan: Introduce acpi_scan_match_handler() Rafael J. Wysocki
2013-02-19  6:48   ` Yasuaki Ishimatsu
2013-02-17 15:21 ` Rafael J. Wysocki [this message]
2013-02-19  6:43   ` [PATCH 2/7] ACPI / scan: Introduce common code for ACPI-based device hotplug Yasuaki Ishimatsu
2013-02-19  7:10     ` Yasuaki Ishimatsu
2013-02-20 13:27       ` Rafael J. Wysocki
2013-02-20 13:23     ` Rafael J. Wysocki
2013-02-20 20:23       ` Toshi Kani
2013-02-20 21:17         ` Rafael J. Wysocki
2013-02-20 22:49   ` [Update][PATCH " Rafael J. Wysocki
2013-02-21  1:17     ` Toshi Kani
2013-02-21 15:44       ` Rafael J. Wysocki
2013-02-21 15:52     ` [Update 2][PATCH " Rafael J. Wysocki
2013-02-21 17:39       ` Toshi Kani
2013-02-21 22:57         ` Rafael J. Wysocki
2013-02-21 23:06       ` [Update 3][PATCH " Rafael J. Wysocki
2013-02-22  1:12         ` Toshi Kani
2013-02-22  1:50           ` Rafael J. Wysocki
2013-02-22  8:51             ` Yasuaki Ishimatsu
2013-02-22 12:37               ` Rafael J. Wysocki
2013-02-22 15:54                 ` Toshi Kani
2013-02-22 20:59                   ` Rafael J. Wysocki
2013-02-23 22:38         ` [Update 4][PATCH " Rafael J. Wysocki
2013-02-25 18:07           ` Toshi Kani
2013-02-25 23:39             ` Rafael J. Wysocki
2013-02-25 23:32               ` Toshi Kani
2013-02-26  0:40               ` Yasuaki Ishimatsu
2013-02-26  1:09                 ` Toshi Kani
2013-02-26  2:02                   ` Yasuaki Ishimatsu
2013-02-26  3:11                     ` Rafael J. Wysocki
2013-02-26  3:40                       ` Yasuaki Ishimatsu
2013-02-26  3:50                         ` Rafael J. Wysocki
2013-03-04 13:10               ` Vasilis Liaskovitis
2013-03-14 17:16                 ` Rafael J. Wysocki
2013-03-15 10:47                   ` Vasilis Liaskovitis
2013-03-25 20:45                     ` Toshi Kani
2013-03-25 22:29                       ` Rafael J. Wysocki
2013-03-25 22:57                         ` Toshi Kani
2013-03-26 12:22                           ` Rafael J. Wysocki
2013-03-26 20:10                             ` Toshi Kani
2013-02-17 15:22 ` [PATCH 3/7] ACPI / container: Use common hotplug code Rafael J. Wysocki
2013-02-17 15:23 ` [PATCH 4/7] ACPI / scan: Introduce acpi_scan_handler_matching() Rafael J. Wysocki
2013-02-19  8:05   ` Yasuaki Ishimatsu
2013-02-17 15:24 ` [PATCH 5/7] ACPI / hotplug: Introduce user space interface for hotplug profiles Rafael J. Wysocki
2013-02-25 18:13   ` Toshi Kani
2013-02-25 23:25     ` Rafael J. Wysocki
2013-02-17 15:25 ` [PATCH 6/7] ACPI / container: Use hotplug profile user space interface Rafael J. Wysocki
2013-02-17 15:27 ` [PATCH 7/7] ACPI / scan: Make memory hotplug driver use struct acpi_scan_handler Rafael J. Wysocki
2013-02-19 18:11   ` Vasilis Liaskovitis
2013-02-20  3:35     ` Yasuaki Ishimatsu
2013-02-20 10:42       ` Vasilis Liaskovitis
2013-02-20 21:50         ` Toshi Kani
2013-02-20 22:29           ` Rafael J. Wysocki
2013-02-20 22:39             ` Toshi Kani
2013-02-21  6:58         ` Yasuaki Ishimatsu
2013-02-26 22:41 ` [PATCH v2, 0/7] ACPI / hotplug: Common code for ACPI-based hotplug Rafael J. Wysocki
2013-02-26 22:44   ` [PATCH v2, 1/7] ACPI / scan: Introduce acpi_scan_match_handler() Rafael J. Wysocki
2013-02-26 22:46   ` [PATCH v2, 2/7] ACPI / scan: Introduce common code for ACPI-based device hotplug Rafael J. Wysocki
2013-02-26 22:46   ` [PATCH v2, 3/7] ACPI / container: Use common hotplug code Rafael J. Wysocki
2013-02-26 23:13     ` Toshi Kani
2013-02-26 23:13       ` Toshi Kani
2013-02-27  0:06       ` Rafael J. Wysocki
2013-02-27  0:09     ` [Update][PATCH " Rafael J. Wysocki
2013-02-26 22:47   ` [PATCH v2, 4/7] ACPI / scan: Introduce acpi_scan_handler_matching() Rafael J. Wysocki
2013-02-26 22:48   ` [PATCH v2, 5/7] ACPI / hotplug: Introduce user space interface for hotplug profiles Rafael J. Wysocki
2013-02-26 22:49   ` [PATCH v2, 6/7] ACPI / container: Use hotplug profile user space interface Rafael J. Wysocki
2013-02-26 22:50   ` [PATCH v2, 7/7] ACPI / scan: Make memory hotplug driver use struct acpi_scan_handler Rafael J. Wysocki
2013-02-27  0:51   ` [PATCH v2, 0/7] ACPI / hotplug: Common code for ACPI-based hotplug Toshi Kani

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2540040.4NPGdn6Df0@vostro.rjw.lan \
    --to=rjw@sisk.pl \
    --cc=bhelgaas@google.com \
    --cc=isimatu.yasuaki@jp.fujitsu.com \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liuj97@gmail.com \
    --cc=toshi.kani@hp.com \
    --cc=yinghai@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.