Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63ff4d07 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-hotplug'

* acpi-hotplug:
  ACPI / hotplug: Consolidate deferred execution of ACPI hotplug routines
  ACPI / hotplug: Do not execute "insert in progress" _OST
  ACPI / hotplug: Carry out PCI root eject directly
  ACPI / hotplug: Merge device hot-removal routines
  ACPI / hotplug: Make acpi_bus_hot_remove_device() internal
  ACPI / hotplug: Simplify device ejection routines
  ACPI / hotplug: Fix handle_root_bridge_removal()
  ACPI / hotplug: Refuse to hot-remove all objects with disabled hotplug
  ACPI / scan: Start matching drivers after trying scan handlers
  ACPI: Remove acpi_pci_slot_init() headers from internal.h

Conflicts:
	include/acpi/acpiosxf.h (with the 'acpica' branch)
parents 679d9980 7b98118a
Loading
Loading
Loading
Loading
+3 −22
Original line number Diff line number Diff line
@@ -669,39 +669,20 @@ static void dock_notify(struct dock_station *ds, u32 event)
	}
}

struct dock_data {
	struct dock_station *ds;
	u32 event;
};

static void acpi_dock_deferred_cb(void *context)
static void acpi_dock_deferred_cb(void *data, u32 event)
{
	struct dock_data *data = context;

	acpi_scan_lock_acquire();
	dock_notify(data->ds, data->event);
	dock_notify(data, event);
	acpi_scan_lock_release();
	kfree(data);
}

static void dock_notify_handler(acpi_handle handle, u32 event, void *data)
{
	struct dock_data *dd;

	if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
	   && event != ACPI_NOTIFY_EJECT_REQUEST)
		return;

	dd = kmalloc(sizeof(*dd), GFP_KERNEL);
	if (dd) {
		acpi_status status;

		dd->ds = data;
		dd->event = event;
		status = acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd);
		if (ACPI_FAILURE(status))
			kfree(dd);
	}
	acpi_hotplug_execute(acpi_dock_deferred_cb, data, event);
}

/**
+1 −5
Original line number Diff line number Diff line
@@ -26,11 +26,6 @@
acpi_status acpi_os_initialize1(void);
int init_acpi_device_notify(void);
int acpi_scan_init(void);
#ifdef	CONFIG_ACPI_PCI_SLOT
void acpi_pci_slot_init(void);
#else
static inline void acpi_pci_slot_init(void) { }
#endif
void acpi_pci_root_init(void);
void acpi_pci_link_init(void);
void acpi_pci_root_hp_init(void);
@@ -92,6 +87,7 @@ void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
int acpi_bind_one(struct device *dev, acpi_handle handle);
int acpi_unbind_one(struct device *dev);
void acpi_bus_device_eject(void *data, u32 ost_src);

/* --------------------------------------------------------------------------
                                  Power Resource
+46 −50
Original line number Diff line number Diff line
@@ -61,7 +61,6 @@ struct acpi_os_dpc {
	acpi_osd_exec_callback function;
	void *context;
	struct work_struct work;
	int wait;
};

#ifdef CONFIG_ACPI_CUSTOM_DSDT
@@ -1087,9 +1086,6 @@ static void acpi_os_execute_deferred(struct work_struct *work)
{
	struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);

	if (dpc->wait)
		acpi_os_wait_events_complete();

	dpc->function(dpc->context);
	kfree(dpc);
}
@@ -1109,8 +1105,8 @@ static void acpi_os_execute_deferred(struct work_struct *work)
 *
 ******************************************************************************/

static acpi_status __acpi_os_execute(acpi_execute_type type,
	acpi_osd_exec_callback function, void *context, int hp)
acpi_status acpi_os_execute(acpi_execute_type type,
			    acpi_osd_exec_callback function, void *context)
{
	acpi_status status = AE_OK;
	struct acpi_os_dpc *dpc;
@@ -1137,20 +1133,11 @@ static acpi_status __acpi_os_execute(acpi_execute_type type,
	dpc->context = context;

	/*
	 * We can't run hotplug code in keventd_wq/kacpid_wq/kacpid_notify_wq
	 * because the hotplug code may call driver .remove() functions,
	 * which invoke flush_scheduled_work/acpi_os_wait_events_complete
	 * to flush these workqueues.
	 *
	 * To prevent lockdep from complaining unnecessarily, make sure that
	 * there is a different static lockdep key for each workqueue by using
	 * INIT_WORK() for each of them separately.
	 */
	if (hp) {
		queue = kacpi_hotplug_wq;
		dpc->wait = 1;
		INIT_WORK(&dpc->work, acpi_os_execute_deferred);
	} else if (type == OSL_NOTIFY_HANDLER) {
	if (type == OSL_NOTIFY_HANDLER) {
		queue = kacpi_notify_wq;
		INIT_WORK(&dpc->work, acpi_os_execute_deferred);
	} else {
@@ -1175,28 +1162,59 @@ static acpi_status __acpi_os_execute(acpi_execute_type type,
	}
	return status;
}
EXPORT_SYMBOL(acpi_os_execute);

acpi_status acpi_os_execute(acpi_execute_type type,
			    acpi_osd_exec_callback function, void *context)
void acpi_os_wait_events_complete(void)
{
	return __acpi_os_execute(type, function, context, 0);
	flush_workqueue(kacpid_wq);
	flush_workqueue(kacpi_notify_wq);
}
EXPORT_SYMBOL(acpi_os_execute);

acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function,
	void *context)
struct acpi_hp_work {
	struct work_struct work;
	acpi_hp_callback func;
	void *data;
	u32 src;
};

static void acpi_hotplug_work_fn(struct work_struct *work)
{
	return __acpi_os_execute(0, function, context, 1);
	struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work);

	acpi_os_wait_events_complete();
	hpw->func(hpw->data, hpw->src);
	kfree(hpw);
}
EXPORT_SYMBOL(acpi_os_hotplug_execute);

void acpi_os_wait_events_complete(void)
acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src)
{
	flush_workqueue(kacpid_wq);
	flush_workqueue(kacpi_notify_wq);
	struct acpi_hp_work *hpw;

	ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
		  "Scheduling function [%p(%p, %u)] for deferred execution.\n",
		  func, data, src));

	hpw = kmalloc(sizeof(*hpw), GFP_KERNEL);
	if (!hpw)
		return AE_NO_MEMORY;

	INIT_WORK(&hpw->work, acpi_hotplug_work_fn);
	hpw->func = func;
	hpw->data = data;
	hpw->src = src;
	/*
	 * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because
	 * the hotplug code may call driver .remove() functions, which may
	 * invoke flush_scheduled_work()/acpi_os_wait_events_complete() to flush
	 * these workqueues.
	 */
	if (!queue_work(kacpi_hotplug_wq, &hpw->work)) {
		kfree(hpw);
		return AE_ERROR;
	}
	return AE_OK;
}

EXPORT_SYMBOL(acpi_os_wait_events_complete);

acpi_status
acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
@@ -1845,25 +1863,3 @@ void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state,
{
	__acpi_os_prepare_extended_sleep = func;
}


void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context,
			void (*func)(struct work_struct *work))
{
	struct acpi_hp_work *hp_work;
	int ret;

	hp_work = kmalloc(sizeof(*hp_work), GFP_KERNEL);
	if (!hp_work)
		return;

	hp_work->handle = handle;
	hp_work->type = type;
	hp_work->context = context;

	INIT_WORK(&hp_work->work, func);
	ret = queue_work(kacpi_hotplug_wq, &hp_work->work);
	if (!ret)
		kfree(hp_work);
}
EXPORT_SYMBOL_GPL(alloc_acpi_hp_work);
+14 −37
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@
#include <acpi/acpi_drivers.h>
#include <acpi/apei.h>

#include "internal.h"

#define PREFIX "ACPI: "

#define _COMPONENT		ACPI_PCI_COMPONENT
@@ -590,39 +592,10 @@ static void handle_root_bridge_insertion(acpi_handle handle)
		acpi_handle_err(handle, "cannot add bridge to acpi list\n");
}

static void handle_root_bridge_removal(struct acpi_device *device)
{
	acpi_status status;
	struct acpi_eject_event *ej_event;

	ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
	if (!ej_event) {
		/* Inform firmware the hot-remove operation has error */
		(void) acpi_evaluate_hotplug_ost(device->handle,
					ACPI_NOTIFY_EJECT_REQUEST,
					ACPI_OST_SC_NON_SPECIFIC_FAILURE,
					NULL);
		return;
	}

	ej_event->device = device;
	ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;

	status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event);
	if (ACPI_FAILURE(status))
		kfree(ej_event);
}

static void _handle_hotplug_event_root(struct work_struct *work)
static void hotplug_event_root(void *data, u32 type)
{
	acpi_handle handle = data;
	struct acpi_pci_root *root;
	struct acpi_hp_work *hp_work;
	acpi_handle handle;
	u32 type;

	hp_work = container_of(work, struct acpi_hp_work, work);
	handle = hp_work->handle;
	type = hp_work->type;

	acpi_scan_lock_acquire();

@@ -652,9 +625,15 @@ static void _handle_hotplug_event_root(struct work_struct *work)
		/* request device eject */
		acpi_handle_printk(KERN_DEBUG, handle,
				   "Device eject notify on %s\n", __func__);
		if (root)
			handle_root_bridge_removal(root->device);
		if (!root)
			break;

		get_device(&root->device->dev);

		acpi_scan_lock_release();

		acpi_bus_device_eject(root->device, ACPI_NOTIFY_EJECT_REQUEST);
		return;
	default:
		acpi_handle_warn(handle,
				 "notify_handler: unknown event type 0x%x\n",
@@ -663,14 +642,12 @@ static void _handle_hotplug_event_root(struct work_struct *work)
	}

	acpi_scan_lock_release();
	kfree(hp_work); /* allocated in handle_hotplug_event_bridge */
}

static void handle_hotplug_event_root(acpi_handle handle, u32 type,
					void *context)
{
	alloc_acpi_hp_work(handle, type, context,
				_handle_hotplug_event_root);
	acpi_hotplug_execute(hotplug_event_root, handle, type);
}

static acpi_status __init
+73 −103
Original line number Diff line number Diff line
@@ -125,8 +125,8 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
}
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);

static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,
					       void *data, void **ret_p)
static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data,
				    void **ret_p)
{
	struct acpi_device *device = NULL;
	struct acpi_device_physical_node *pn;
@@ -136,6 +136,11 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,
	if (acpi_bus_get_device(handle, &device))
		return AE_OK;

	if (device->handler && !device->handler->hotplug.enabled) {
		*ret_p = &device->dev;
		return AE_SUPPORT;
	}

	mutex_lock(&device->physical_node_lock);

	list_for_each_entry(pn, &device->physical_node_list, node) {
@@ -168,8 +173,8 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,
	return status;
}

static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl,
					      void *data, void **ret_p)
static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data,
				   void **ret_p)
{
	struct acpi_device *device = NULL;
	struct acpi_device_physical_node *pn;
@@ -214,26 +219,32 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
	 * If the first pass is successful, the second one isn't needed, though.
	 */
	errdev = NULL;
	status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
				     NULL, acpi_bus_offline, (void *)false,
				     (void **)&errdev);
	if (status == AE_SUPPORT) {
		dev_warn(errdev, "Offline disabled.\n");
		acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
			    NULL, acpi_bus_offline_companions,
			    (void *)false, (void **)&errdev);
	acpi_bus_offline_companions(handle, 0, (void *)false, (void **)&errdev);
				    acpi_bus_online, NULL, NULL, NULL);
		put_device(&device->dev);
		return -EPERM;
	}
	acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev);
	if (errdev) {
		errdev = NULL;
		acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
				    NULL, acpi_bus_offline_companions,
				    (void *)true , (void **)&errdev);
				    NULL, acpi_bus_offline, (void *)true,
				    (void **)&errdev);
		if (!errdev || acpi_force_hot_remove)
			acpi_bus_offline_companions(handle, 0, (void *)true,
			acpi_bus_offline(handle, 0, (void *)true,
					 (void **)&errdev);

		if (errdev && !acpi_force_hot_remove) {
			dev_warn(errdev, "Offline failed.\n");
			acpi_bus_online_companions(handle, 0, NULL, NULL);
			acpi_bus_online(handle, 0, NULL, NULL);
			acpi_walk_namespace(ACPI_TYPE_ANY, handle,
					    ACPI_UINT32_MAX,
					    acpi_bus_online_companions, NULL,
					    NULL, NULL);
					    ACPI_UINT32_MAX, acpi_bus_online,
					    NULL, NULL, NULL);
			put_device(&device->dev);
			return -EBUSY;
		}
@@ -274,10 +285,10 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
	return 0;
}

static void acpi_bus_device_eject(void *context)
void acpi_bus_device_eject(void *data, u32 ost_src)
{
	acpi_handle handle = context;
	struct acpi_device *device = NULL;
	struct acpi_device *device = data;
	acpi_handle handle = device->handle;
	struct acpi_scan_handler *handler;
	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
	int error;
@@ -285,38 +296,41 @@ static void acpi_bus_device_eject(void *context)
	lock_device_hotplug();
	mutex_lock(&acpi_scan_lock);

	acpi_bus_get_device(handle, &device);
	if (!device)
		goto err_out;

	handler = device->handler;
	if (!handler || !handler->hotplug.enabled) {
		ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
		goto err_out;
		put_device(&device->dev);
		goto err_support;
	}

	if (ost_src == ACPI_NOTIFY_EJECT_REQUEST)
		acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
					  ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);

	if (handler->hotplug.mode == AHM_CONTAINER)
		kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);

	get_device(&device->dev);
	error = acpi_scan_hot_remove(device);
	if (error)
	if (error == -EPERM) {
		goto err_support;
	} else if (error) {
		goto err_out;
	}

 out:
	mutex_unlock(&acpi_scan_lock);
	unlock_device_hotplug();
	return;

 err_support:
	ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
 err_out:
	acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code,
				  NULL);
	acpi_evaluate_hotplug_ost(handle, ost_src, ost_code, NULL);
	goto out;
}

static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
static void acpi_scan_bus_device_check(void *data, u32 ost_source)
{
	acpi_handle handle = data;
	struct acpi_device *device = NULL;
	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
	int error;
@@ -331,8 +345,6 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
			goto out;
		}
	}
	acpi_evaluate_hotplug_ost(handle, ost_source,
				  ACPI_OST_SC_INSERT_IN_PROGRESS, NULL);
	error = acpi_bus_scan(handle);
	if (error) {
		acpi_handle_warn(handle, "Namespace scan failure\n");
@@ -353,18 +365,6 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
	unlock_device_hotplug();
}

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_unsupported(acpi_handle handle, u32 type)
{
	u32 ost_status;
@@ -395,8 +395,8 @@ static void acpi_hotplug_unsupported(acpi_handle handle, u32 type)

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
	acpi_osd_exec_callback callback;
	struct acpi_scan_handler *handler = data;
	struct acpi_device *adev;
	acpi_status status;

	if (!handler->hotplug.enabled)
@@ -405,56 +405,35 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
	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;
		status = acpi_bus_get_device(handle, &adev);
		if (ACPI_FAILURE(status))
			goto err_out;

		get_device(&adev->dev);
		status = acpi_hotplug_execute(acpi_bus_device_eject, adev, type);
		if (ACPI_SUCCESS(status))
			return;

		put_device(&adev->dev);
		goto err_out;
	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;
	int error;

	lock_device_hotplug();
	mutex_lock(&acpi_scan_lock);

	error = acpi_scan_hot_remove(device);
	if (error && handle)
		acpi_evaluate_hotplug_ost(handle, ej_event->event,
					  ACPI_OST_SC_NON_SPECIFIC_FAILURE,
					  NULL);
	status = acpi_hotplug_execute(acpi_scan_bus_device_check, handle, type);
	if (ACPI_SUCCESS(status))
		return;

	mutex_unlock(&acpi_scan_lock);
	unlock_device_hotplug();
	kfree(context);
 err_out:
	acpi_evaluate_hotplug_ost(handle, type,
				  ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
}
EXPORT_SYMBOL(acpi_bus_hot_remove_device);

static ssize_t real_power_state_show(struct device *dev,
				     struct device_attribute *attr, char *buf)
@@ -487,10 +466,8 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
		const char *buf, size_t count)
{
	struct acpi_device *acpi_device = to_acpi_device(d);
	struct acpi_eject_event *ej_event;
	acpi_object_type not_used;
	acpi_status status;
	int ret;

	if (!count || buf[0] != '1')
		return -EINVAL;
@@ -503,28 +480,18 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
	if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
		return -ENODEV;

	ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
	if (!ej_event) {
		ret = -ENOMEM;
		goto err_out;
	}
	acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
				  ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
	ej_event->device = acpi_device;
	ej_event->event = ACPI_OST_EC_OSPM_EJECT;
	get_device(&acpi_device->dev);
	status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event);
	status = acpi_hotplug_execute(acpi_bus_device_eject, acpi_device,
				      ACPI_OST_EC_OSPM_EJECT);
	if (ACPI_SUCCESS(status))
		return count;

	put_device(&acpi_device->dev);
	kfree(ej_event);
	ret = status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;

 err_out:
	acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
				  ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
	return ret;
	return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;
}

static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store);
@@ -1676,7 +1643,6 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,

void acpi_device_add_finalize(struct acpi_device *device)
{
	device->flags.match_driver = true;
	dev_set_uevent_suppress(&device->dev, false);
	kobject_uevent(&device->dev.kobj, KOBJ_ADD);
}
@@ -1915,8 +1881,12 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
		return AE_OK;

	ret = acpi_scan_attach_handler(device);
	if (ret)
		return ret > 0 ? AE_OK : AE_CTRL_DEPTH;
	if (ret < 0)
		return AE_CTRL_DEPTH;

	device->flags.match_driver = true;
	if (ret > 0)
		return AE_OK;

	ret = device_attach(&device->dev);
	return ret >= 0 ? AE_OK : AE_CTRL_DEPTH;
Loading