2014-01-27 00:32:56

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 0/11] ACPI / hotplug / PCI: Updates on top of changes merged recently

Hi All,

ACPIPHP can be simplified a bit on top of some PCI and ACPI changes merged
recently and the following series of patches implements those simplifications:

[1/11] Fix up two kerneldoc comments in acpiphp_glue.c.
[2/11] Get rid of an unnecessary label in register_slot().
[3/11] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
[4/11] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
[5/11] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
[6/11] Drop acpiphp_bus_add() (which has only one user).
[7/11] Drop crit_sect mutexes (that are redundant).
[8/11] Clean up the usage of the slot variable in hotplug_event().
[9/11] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
[10/11] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
[11/11] Drop handle argument from the member functions of struct acpi_dock_ops.

All of that is relateively straightforward, but I have some more intrusive changes
on top of it in the works. They will be posted separately later this week.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.


2014-01-27 00:32:27

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 5/11] ACPI / hotplug / PCI: Store acpi_device pointer in acpiphp_context

From: Rafael J. Wysocki <[email protected]>

After recent modifications of the ACPI core making it create a struct
acpi_device object for every namespace node representing a device
regardless of the current status of that device the ACPIPHP code
can store a struct acpi_device pointer instead of an ACPI handle
in struct acpiphp_context. This immediately makes it possible to
avoid making potentially costly calls to acpi_bus_get_device() in
two places and allows some more simplifications to be made going
forward.

The reason why that is correct is because ACPIPHP only installs
hotify handlers for namespace nodes that exist when
acpiphp_enumerate_slots() is called for their parent bridge.
That only happens if the parent bridge has an ACPI companion
associated with it, which means that the ACPI namespace scope
in question has been scanned already at that point. That, in
turn, means that struct acpi_device objects have been created
for all namespace nodes in that scope and pointers to those
objects can be stored directly instead of their ACPI handles.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp.h | 9 +++++--
drivers/pci/hotplug/acpiphp_glue.c | 44 +++++++++++++++++--------------------
2 files changed, 28 insertions(+), 25 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -117,8 +117,8 @@ struct acpiphp_func {
};

struct acpiphp_context {
- acpi_handle handle;
struct acpiphp_func func;
+ struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};
@@ -128,9 +128,14 @@ static inline struct acpiphp_context *fu
return container_of(func, struct acpiphp_context, func);
}

+static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
+{
+ return func_to_context(func)->adev;
+}
+
static inline acpi_handle func_to_handle(struct acpiphp_func *func)
{
- return func_to_context(func)->handle;
+ return func_to_acpi_device(func)->handle;
}

/*
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -73,11 +73,11 @@ static void acpiphp_context_handler(acpi

/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
- * @handle: ACPI object handle to create the context for.
+ * @adev: ACPI device object to create the context for.
*
* Call under acpiphp_context_lock.
*/
-static struct acpiphp_context *acpiphp_init_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
acpi_status status;
@@ -86,9 +86,9 @@ static struct acpiphp_context *acpiphp_i
if (!context)
return NULL;

- context->handle = handle;
+ context->adev = adev;
context->refcount = 1;
- status = acpi_attach_data(handle, acpiphp_context_handler, context);
+ status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
if (ACPI_FAILURE(status)) {
kfree(context);
return NULL;
@@ -118,7 +118,7 @@ static struct acpiphp_context *acpiphp_g

/**
* acpiphp_put_context - Drop a reference to ACPI hotplug context.
- * @handle: ACPI object handle to put the context for.
+ * @context: ACPI hotplug context to drop a reference to.
*
* The context object is removed if there are no more references to it.
*
@@ -130,7 +130,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->handle, acpiphp_context_handler);
+ acpi_detach_data(context->adev->handle, acpiphp_context_handler);
kfree(context);
}

@@ -265,6 +265,7 @@ static acpi_status register_slot(acpi_ha
{
struct acpiphp_bridge *bridge = data;
struct acpiphp_context *context;
+ struct acpi_device *adev;
struct acpiphp_slot *slot;
struct acpiphp_func *newfunc;
acpi_status status = AE_OK;
@@ -284,12 +285,14 @@ static acpi_status register_slot(acpi_ha
"can't evaluate _ADR (%#x)\n", status);
return AE_OK;
}
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;

device = (adr >> 16) & 0xffff;
function = adr & 0xffff;

mutex_lock(&acpiphp_context_lock);
- context = acpiphp_init_context(handle);
+ context = acpiphp_init_context(adev);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
acpi_handle_err(handle, "No hotplug context\n");
@@ -625,12 +628,8 @@ static void disable_slot(struct acpiphp_
pci_dev_put(pdev);
}

- list_for_each_entry(func, &slot->funcs, sibling) {
- struct acpi_device *adev;
-
- if (!acpi_bus_get_device(func_to_handle(func), &adev))
- acpi_bus_trim(adev);
- }
+ list_for_each_entry(func, &slot->funcs, sibling)
+ acpi_bus_trim(func_to_acpi_device(func));

slot->flags &= (~SLOT_ENABLED);
}
@@ -644,13 +643,10 @@ static bool slot_no_hotplug(struct acpip
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling) {
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(func_to_handle(func), &adev);
- if (acpiphp_no_hotplug(adev))
+ list_for_each_entry(func, &slot->funcs, sibling)
+ if (acpiphp_no_hotplug(func_to_acpi_device(func)))
return true;
- }
+
return false;
}

@@ -899,7 +895,7 @@ static void hotplug_event(acpi_handle ha
static void hotplug_event_work(void *data, u32 type)
{
struct acpiphp_context *context = data;
- acpi_handle handle = context->handle;
+ acpi_handle handle = context->adev->handle;

acpi_scan_lock_acquire();
pci_lock_rescan_remove();
@@ -959,7 +955,7 @@ static void handle_hotplug_event(acpi_ha

mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
- if (context && !WARN_ON(context->handle != handle)) {
+ if (context && !WARN_ON(context->adev->handle != handle)) {
get_bridge(context->func.parent);
acpiphp_put_context(context);
acpi_hotplug_execute(hotplug_event_work, context, type);
@@ -983,16 +979,18 @@ static void handle_hotplug_event(acpi_ha
void acpiphp_enumerate_slots(struct pci_bus *bus)
{
struct acpiphp_bridge *bridge;
+ struct acpi_device *adev;
acpi_handle handle;
acpi_status status;

if (acpiphp_disabled)
return;

- handle = ACPI_HANDLE(bus->bridge);
- if (!handle)
+ adev = ACPI_COMPANION(bus->bridge);
+ if (!adev)
return;

+ handle = adev->handle;
bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
if (!bridge) {
acpi_handle_err(handle, "No memory for bridge object\n");

2014-01-27 00:32:26

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 6/11] ACPI / hotplug / PCI: Drop acpiphp_bus_add()

From: Rafael J. Wysocki <[email protected]>

acpiphp_bus_add() is only called from one place, so move the code out
of it into that place and drop it. Also make that code use
func_to_acpi_device() to get the struct acpi_device pointer it needs
instead of calling acpi_bus_get_device() which may be costly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 22 ++++++----------------
1 file changed, 6 insertions(+), 16 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -470,20 +470,6 @@ static unsigned char acpiphp_max_busnr(s
return max;
}

-/**
- * acpiphp_bus_add - Scan ACPI namespace subtree.
- * @handle: ACPI object handle to start the scan from.
- */
-static void acpiphp_bus_add(acpi_handle handle)
-{
- struct acpi_device *adev = NULL;
-
- acpi_bus_scan(handle);
- acpi_bus_get_device(handle, &adev);
- if (acpi_device_enumerated(adev))
- acpi_device_set_power(adev, ACPI_STATE_D0);
-}
-
static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
{
struct acpiphp_func *func;
@@ -523,9 +509,13 @@ static int acpiphp_rescan_slot(struct ac
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling)
- acpiphp_bus_add(func_to_handle(func));
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev = func_to_acpi_device(func);

+ acpi_bus_scan(adev->handle);
+ if (acpi_device_enumerated(adev))
+ acpi_device_set_power(adev, ACPI_STATE_D0);
+ }
return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0));
}

2014-01-27 00:33:19

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 1/11] ACPI / hotplug / PCI: Proper kerneldoc comments for enumeration/removal

From: Rafael J. Wysocki <[email protected]>

Add proper kerneldoc comments describing acpiphp_enumerate_slots()
and acpiphp_remove_slots().

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -986,9 +986,12 @@ static void handle_hotplug_event(acpi_ha
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-/*
- * Create hotplug slots for the PCI bus.
- * It should always return 0 to avoid skipping following notifiers.
+/**
+ * acpiphp_enumerate_slots - Enumerate PCI slots for a given bus.
+ * @bus: PCI bus to enumerate the slots for.
+ *
+ * A "slot" is an object associated with a PCI device number. All functions
+ * (PCI devices) with the same bus and device number belong to the same slot.
*/
void acpiphp_enumerate_slots(struct pci_bus *bus)
{
@@ -1061,7 +1064,10 @@ void acpiphp_enumerate_slots(struct pci_
}
}

-/* Destroy hotplug slots associated with the PCI bus */
+/**
+ * acpiphp_remove_slots - Remove slot objects associated with a given bus.
+ * @bus: PCI bus to remove the slot objects for.
+ */
void acpiphp_remove_slots(struct pci_bus *bus)
{
struct acpiphp_bridge *bridge;

2014-01-27 00:33:37

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 2/11] ACPI / hotplug / PCI: Simplify register_slot()

From: Rafael J. Wysocki <[email protected]>

The err label in register_slot() is only jumped to from one place,
so move the code under the label to that place and drop the label.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -316,8 +316,10 @@ static acpi_status register_slot(acpi_ha

slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
- status = AE_NO_MEMORY;
- goto err;
+ mutex_lock(&acpiphp_context_lock);
+ acpiphp_put_context(context);
+ mutex_unlock(&acpiphp_context_lock);
+ return AE_NO_MEMORY;
}

slot->bus = bridge->pci_bus;
@@ -385,12 +387,6 @@ static acpi_status register_slot(acpi_ha
}

return AE_OK;
-
- err:
- mutex_lock(&acpiphp_context_lock);
- acpiphp_put_context(context);
- mutex_unlock(&acpiphp_context_lock);
- return status;
}

static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)

2014-01-27 00:32:24

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 10/11] ACPI / hotplug / PCI: Use acpi_handle_debug() in hotplug_event()

From: Rafael J. Wysocki <[email protected]>

Make hotplug_event() use acpi_handle_debug() instead of an open-coded
debug message printing and clean up the messages printed by it.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -794,9 +794,6 @@ static void hotplug_event(acpi_handle ha
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
- char objname[64];
- struct acpi_buffer buffer = { .length = sizeof(objname),
- .pointer = objname };

mutex_lock(&acpiphp_context_lock);
bridge = context->bridge;
@@ -805,14 +802,10 @@ static void hotplug_event(acpi_handle ha

mutex_unlock(&acpiphp_context_lock);

- acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
-
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/* bus re-enumerate */
- pr_debug("%s: Bus check notify on %s\n", __func__, objname);
- pr_debug("%s: re-enumerating slots under %s\n",
- __func__, objname);
+ acpi_handle_debug(handle, "Bus check in %s()\n", __func__);
if (bridge)
acpiphp_check_bridge(bridge);
else if (!(slot->flags & SLOT_IS_GOING_AWAY))
@@ -822,7 +815,7 @@ static void hotplug_event(acpi_handle ha

case ACPI_NOTIFY_DEVICE_CHECK:
/* device check */
- pr_debug("%s: Device check notify on %s\n", __func__, objname);
+ acpi_handle_debug(handle, "Device check in %s()\n", __func__);
if (bridge) {
acpiphp_check_bridge(bridge);
} else if (!(slot->flags & SLOT_IS_GOING_AWAY)) {
@@ -837,7 +830,7 @@ static void hotplug_event(acpi_handle ha

case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
- pr_debug("%s: Device eject notify on %s\n", __func__, objname);
+ acpi_handle_debug(handle, "Eject request in %s()\n", __func__);
acpiphp_disable_and_eject_slot(slot);
break;
}

2014-01-27 00:34:32

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 3/11] ACPI / hotplug / PCI: Drop acpiphp_bus_trim()

From: Rafael J. Wysocki <[email protected]>

If trim_stale_devices() calls acpi_bus_trim() directly, we can
save a potentially costly acpi_bus_get_device() invocation. After
making that change acpiphp_bus_trim() would only be called from one
place, so move the code from it to that place and drop it.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 30 +++++++++++-------------------
1 file changed, 11 insertions(+), 19 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -468,19 +468,6 @@ static unsigned char acpiphp_max_busnr(s
}

/**
- * acpiphp_bus_trim - Trim device objects in an ACPI namespace subtree.
- * @handle: ACPI device object handle to start from.
- */
-static void acpiphp_bus_trim(acpi_handle handle)
-{
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(handle, &adev);
- if (adev)
- acpi_bus_trim(adev);
-}
-
-/**
* acpiphp_bus_add - Scan ACPI namespace subtree.
* @handle: ACPI object handle to start the scan from.
*/
@@ -638,8 +625,12 @@ static void disable_slot(struct acpiphp_
pci_dev_put(pdev);
}

- list_for_each_entry(func, &slot->funcs, sibling)
- acpiphp_bus_trim(func_to_handle(func));
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev;
+
+ if (!acpi_bus_get_device(func_to_handle(func), &adev))
+ acpi_bus_trim(adev);
+ }

slot->flags &= (~SLOT_ENABLED);
}
@@ -711,11 +702,12 @@ static unsigned int get_slot_status(stru
*/
static void trim_stale_devices(struct pci_dev *dev)
{
- acpi_handle handle = ACPI_HANDLE(&dev->dev);
+ struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
struct pci_bus *bus = dev->subordinate;
bool alive = false;

- if (handle) {
+ if (adev) {
+ acpi_handle handle = adev->handle;
acpi_status status;
unsigned long long sta;

@@ -731,8 +723,8 @@ static void trim_stale_devices(struct pc
}
if (!alive) {
pci_stop_and_remove_bus_device(dev);
- if (handle)
- acpiphp_bus_trim(handle);
+ if (adev)
+ acpi_bus_trim(adev);
} else if (bus) {
struct pci_dev *child, *tmp;

2014-01-27 00:34:49

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 4/11] ACPI / hotplug / PCI: Rework acpiphp_no_hotplug()

From: Rafael J. Wysocki <[email protected]>

If a struct acpi_device pointer is passed to acpiphp_no_hotplug()
instead of an ACPI handle, the function won't need to call
acpi_bus_get_device(), which may be costly, any more. Then,
trim_stale_devices() can call acpiphp_no_hotplug() passing
the struct acpi_device object it already has directly to that
function.

Make those changes and update slot_no_hotplug() accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -635,11 +635,8 @@ static void disable_slot(struct acpiphp_
slot->flags &= (~SLOT_ENABLED);
}

-static bool acpiphp_no_hotplug(acpi_handle handle)
+static bool acpiphp_no_hotplug(struct acpi_device *adev)
{
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(handle, &adev);
return adev && adev->flags.no_hotplug;
}

@@ -647,10 +644,13 @@ static bool slot_no_hotplug(struct acpip
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling)
- if (acpiphp_no_hotplug(func_to_handle(func)))
- return true;
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev = NULL;

+ acpi_bus_get_device(func_to_handle(func), &adev);
+ if (acpiphp_no_hotplug(adev))
+ return true;
+ }
return false;
}

@@ -707,13 +707,12 @@ static void trim_stale_devices(struct pc
bool alive = false;

if (adev) {
- acpi_handle handle = adev->handle;
acpi_status status;
unsigned long long sta;

- status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
alive = (ACPI_SUCCESS(status) && sta == ACPI_STA_ALL)
- || acpiphp_no_hotplug(handle);
+ || acpiphp_no_hotplug(adev);
}
if (!alive) {
u32 v;

2014-01-27 00:35:13

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 7/11] ACPI / hotplug / PCI: Drop crit_sect locking

From: Rafael J. Wysocki <[email protected]>

After recent PCI core changes related to the rescan/remove locking,
the code sections under crit_sect mutexes from ACPIPHP slot objects
are always executed under the general PCI rescan/remove lock.
For this reason, the crit_sect mutexes are simply redundant, so drop
them.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp.h | 1 -
drivers/pci/hotplug/acpiphp_glue.c | 23 +++--------------------
2 files changed, 3 insertions(+), 21 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -328,7 +328,6 @@ static acpi_status register_slot(acpi_ha
slot->bus = bridge->pci_bus;
slot->device = device;
INIT_LIST_HEAD(&slot->funcs);
- mutex_init(&slot->crit_sect);

list_add_tail(&slot->node, &bridge->slots);

@@ -741,7 +740,6 @@ static void acpiphp_check_bridge(struct
struct pci_bus *bus = slot->bus;
struct pci_dev *dev, *tmp;

- mutex_lock(&slot->crit_sect);
if (slot_no_hotplug(slot)) {
; /* do nothing */
} else if (get_slot_status(slot) == ACPI_STA_ALL) {
@@ -756,7 +754,6 @@ static void acpiphp_check_bridge(struct
} else {
disable_slot(slot);
}
- mutex_unlock(&slot->crit_sect);
}
}

@@ -838,12 +835,8 @@ static void hotplug_event(acpi_handle ha
} else {
struct acpiphp_slot *slot = func->slot;

- if (slot->flags & SLOT_IS_GOING_AWAY)
- break;
-
- mutex_lock(&slot->crit_sect);
- enable_slot(slot);
- mutex_unlock(&slot->crit_sect);
+ if (!(slot->flags & SLOT_IS_GOING_AWAY))
+ enable_slot(slot);
}
break;

@@ -854,7 +847,6 @@ static void hotplug_event(acpi_handle ha
acpiphp_check_bridge(bridge);
} else {
struct acpiphp_slot *slot = func->slot;
- int ret;

if (slot->flags & SLOT_IS_GOING_AWAY)
break;
@@ -863,10 +855,7 @@ static void hotplug_event(acpi_handle ha
* Check if anything has changed in the slot and rescan
* from the parent if that's the case.
*/
- mutex_lock(&slot->crit_sect);
- ret = acpiphp_rescan_slot(slot);
- mutex_unlock(&slot->crit_sect);
- if (ret)
+ if (acpiphp_rescan_slot(slot))
acpiphp_check_bridge(func->parent);
}
break;
@@ -1073,13 +1062,10 @@ int acpiphp_enable_slot(struct acpiphp_s
if (slot->flags & SLOT_IS_GOING_AWAY)
return -ENODEV;

- mutex_lock(&slot->crit_sect);
/* configure all functions */
if (!(slot->flags & SLOT_ENABLED))
enable_slot(slot);

- mutex_unlock(&slot->crit_sect);
-
pci_unlock_rescan_remove();
return 0;
}
@@ -1095,8 +1081,6 @@ static int acpiphp_disable_and_eject_slo
if (slot->flags & SLOT_IS_GOING_AWAY)
return -ENODEV;

- mutex_lock(&slot->crit_sect);
-
/* unconfigure all functions */
disable_slot(slot);

@@ -1110,7 +1094,6 @@ static int acpiphp_disable_and_eject_slo
break;
}

- mutex_unlock(&slot->crit_sect);
return 0;
}

Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -93,7 +93,6 @@ struct acpiphp_slot {
struct list_head funcs; /* one slot may have different
objects (i.e. for each function) */
struct slot *slot;
- struct mutex crit_sect;

u8 device; /* pci device# */
u32 flags; /* see below */

2014-01-27 00:32:21

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 11/11] ACPI / hotplug: Do not pass ACPI handles to ACPI dock operations

From: Rafael J. Wysocki <[email protected]>

None of the existing users of struct acpi_dock_ops actually needs the
first argument of its member functions, so redefine those functions
to take only two arguments, the event type and data pointer, and
update the users of struct acpi_dock_ops accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/dock.c | 4 ++--
drivers/ata/libata-acpi.c | 8 ++++----
drivers/pci/hotplug/acpiphp_glue.c | 13 +++++++------
include/acpi/acpi_drivers.h | 6 +++---
4 files changed, 16 insertions(+), 15 deletions(-)

Index: linux-pm/include/acpi/acpi_drivers.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_drivers.h
+++ linux-pm/include/acpi/acpi_drivers.h
@@ -110,9 +110,9 @@ void pci_acpi_crs_quirks(void);
Dock Station
-------------------------------------------------------------------------- */
struct acpi_dock_ops {
- acpi_notify_handler fixup;
- acpi_notify_handler handler;
- acpi_notify_handler uevent;
+ void (*handler)(u32 event_type, void *data);
+ void (*fixup)(u32 event_type, void *data);
+ void (*uevent)(u32 event_type, void *data);
};

#ifdef CONFIG_ACPI_DOCK
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -63,7 +63,7 @@ static DEFINE_MUTEX(acpiphp_context_lock
static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
-static void hotplug_event(acpi_handle handle, u32 type, void *data);
+static void hotplug_event(u32 type, void *data);
static void free_bridge(struct kref *kref);

static void acpiphp_context_handler(acpi_handle handle, void *context)
@@ -185,7 +185,7 @@ static void free_bridge(struct kref *kre
* TBD - figure out a way to only call fixups for
* systems that require them.
*/
-static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
+static void post_dock_fixups(u32 event, void *data)
{
struct acpiphp_context *context = data;
struct pci_bus *bus = context->func.slot->bus;
@@ -788,11 +788,12 @@ void acpiphp_check_host_bridge(acpi_hand

static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);

-static void hotplug_event(acpi_handle handle, u32 type, void *data)
+static void hotplug_event(u32 type, void *data)
{
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
+ acpi_handle handle = context->adev->handle;
struct acpiphp_bridge *bridge;

mutex_lock(&acpiphp_context_lock);
@@ -842,16 +843,16 @@ static void hotplug_event(acpi_handle ha
static void hotplug_event_work(void *data, u32 type)
{
struct acpiphp_context *context = data;
- acpi_handle handle = context->adev->handle;

acpi_scan_lock_acquire();
pci_lock_rescan_remove();

- hotplug_event(handle, type, context);
+ hotplug_event(type, context);

pci_unlock_rescan_remove();
acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);
+ acpi_evaluate_hotplug_ost(context->adev->handle, type,
+ ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);
}

Index: linux-pm/drivers/acpi/dock.c
===================================================================
--- linux-pm.orig/drivers/acpi/dock.c
+++ linux-pm/drivers/acpi/dock.c
@@ -185,7 +185,7 @@ static void dock_release_hotplug(struct
static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
enum dock_callback_type cb_type)
{
- acpi_notify_handler cb = NULL;
+ void (*cb)(u32, void *) = NULL;
bool run = false;

mutex_lock(&hotplug_lock);
@@ -213,7 +213,7 @@ static void dock_hotplug_event(struct do
return;

if (cb)
- cb(dd->handle, event, dd->hp_context);
+ cb(event, dd->hp_context);

dock_release_hotplug(dd);
}
Index: linux-pm/drivers/ata/libata-acpi.c
===================================================================
--- linux-pm.orig/drivers/ata/libata-acpi.c
+++ linux-pm/drivers/ata/libata-acpi.c
@@ -121,14 +121,14 @@ static void ata_acpi_handle_hotplug(stru
ata_port_wait_eh(ap);
}

-static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data)
+static void ata_acpi_dev_notify_dock(u32 event, void *data)
{
struct ata_device *dev = data;

ata_acpi_handle_hotplug(dev->link->ap, dev, event);
}

-static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data)
+static void ata_acpi_ap_notify_dock(u32 event, void *data)
{
struct ata_port *ap = data;

@@ -154,12 +154,12 @@ static void ata_acpi_uevent(struct ata_p
}
}

-static void ata_acpi_ap_uevent(acpi_handle handle, u32 event, void *data)
+static void ata_acpi_ap_uevent(u32 event, void *data)
{
ata_acpi_uevent(data, NULL, event);
}

-static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data)
+static void ata_acpi_dev_uevent(u32 event, void *data)
{
struct ata_device *dev = data;
ata_acpi_uevent(dev->link->ap, dev, event);

2014-01-27 00:35:52

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 8/11] ACPI / hotplug / PCI: Simplify hotplug_event()

From: Rafael J. Wysocki <[email protected]>

A few lines of code can be cut from hotplug_event() by defining
and initializing the slot variable at the top of the function,
so do that.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -810,6 +810,7 @@ static void hotplug_event(acpi_handle ha
{
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
+ struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
@@ -830,14 +831,11 @@ static void hotplug_event(acpi_handle ha
pr_debug("%s: Bus check notify on %s\n", __func__, objname);
pr_debug("%s: re-enumerating slots under %s\n",
__func__, objname);
- if (bridge) {
+ if (bridge)
acpiphp_check_bridge(bridge);
- } else {
- struct acpiphp_slot *slot = func->slot;
+ else if (!(slot->flags & SLOT_IS_GOING_AWAY))
+ enable_slot(slot);

- if (!(slot->flags & SLOT_IS_GOING_AWAY))
- enable_slot(slot);
- }
break;

case ACPI_NOTIFY_DEVICE_CHECK:
@@ -845,12 +843,7 @@ static void hotplug_event(acpi_handle ha
pr_debug("%s: Device check notify on %s\n", __func__, objname);
if (bridge) {
acpiphp_check_bridge(bridge);
- } else {
- struct acpiphp_slot *slot = func->slot;
-
- if (slot->flags & SLOT_IS_GOING_AWAY)
- break;
-
+ } else if (!(slot->flags & SLOT_IS_GOING_AWAY)) {
/*
* Check if anything has changed in the slot and rescan
* from the parent if that's the case.
@@ -863,7 +856,7 @@ static void hotplug_event(acpi_handle ha
case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
pr_debug("%s: Device eject notify on %s\n", __func__, objname);
- acpiphp_disable_and_eject_slot(func->slot);
+ acpiphp_disable_and_eject_slot(slot);
break;
}

2014-01-27 00:36:09

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 9/11] ACPI / hotplug / PCI: Simplify disable_slot()

From: Rafael J. Wysocki <[email protected]>

After recent PCI core changes related to the rescan/remove locking,
the ACPIPHP's disable_slot() function is only called under the
general PCI rescan/remove lock, so it doesn't have to use
dev_in_slot() any more to avoid race conditions. Make it simply
walk the devices on the bus and remove the ones in the slot being
disabled. Then, drop dev_in_slot() which has no more users.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 28 +++++-----------------------
1 file changed, 5 insertions(+), 23 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -579,32 +579,15 @@ static void __ref enable_slot(struct acp
}
}

-/* return first device in slot, acquiring a reference on it */
-static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
-{
- struct pci_bus *bus = slot->bus;
- struct pci_dev *dev;
- struct pci_dev *ret = NULL;
-
- down_read(&pci_bus_sem);
- list_for_each_entry(dev, &bus->devices, bus_list)
- if (PCI_SLOT(dev->devfn) == slot->device) {
- ret = pci_dev_get(dev);
- break;
- }
- up_read(&pci_bus_sem);
-
- return ret;
-}
-
/**
* disable_slot - disable a slot
* @slot: ACPI PHP slot
*/
static void disable_slot(struct acpiphp_slot *slot)
{
+ struct pci_bus *bus = slot->bus;
+ struct pci_dev *dev, *next;
struct acpiphp_func *func;
- struct pci_dev *pdev;

/*
* enable_slot() enumerates all functions in this device via
@@ -612,10 +595,9 @@ static void disable_slot(struct acpiphp_
* methods (_EJ0, etc.) or not. Therefore, we remove all functions
* here.
*/
- while ((pdev = dev_in_slot(slot))) {
- pci_stop_and_remove_bus_device(pdev);
- pci_dev_put(pdev);
- }
+ list_for_each_entry_safe(dev, next, &bus->devices, bus_list)
+ if (PCI_SLOT(dev->devfn) == slot->device)
+ pci_stop_and_remove_bus_device(dev);

list_for_each_entry(func, &slot->funcs, sibling)
acpi_bus_trim(func_to_acpi_device(func));

2014-01-28 22:05:40

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 5/5][RFT] ACPI / hotplug: Dispach hotplug notifications from acpi_bus_notify()

From: Rafael J. Wysocki <[email protected]>

Since acpi_bus_notify() is executed on all notifications for all
devices anyway, rename acpi_hotplug_notify_cb() to acpi_system_notify()
and call it directly from acpi_bus_notify() instead of installing
notify handlers pointing to it for all hotplug devices.

This change reduces both the size and complexity of ACPI-base device
hotplug code. Moreover, since acpi_system_notify() only does
significant things for devices that either have an ACPI scan handler,
or have a hotplug context with .eject() defined, and those devices
had notify handlers pointing to acpi_hotplug_notify_cb() installed
before anyway, this change shouldn't modify functionality, except for
executing _OST with the ACPI_OST_SC_NON_SPECIFIC_FAILURE status code
for devices that were not handled at all previously.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/bus.c | 42 -----------------------------------
drivers/acpi/internal.h | 1
drivers/acpi/scan.c | 44 -------------------------------------
drivers/pci/hotplug/acpiphp.h | 1
drivers/pci/hotplug/acpiphp_glue.c | 16 +++++--------
include/acpi/acpi_bus.h | 2 -
6 files changed, 10 insertions(+), 96 deletions(-)

Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -345,47 +345,7 @@ static void acpi_bus_notify(acpi_handle
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
type, handle));

- switch (type) {
-
- case ACPI_NOTIFY_BUS_CHECK:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_EJECT_REQUEST:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
- /* TBD: Exactly what does 'light' mean? */
- break;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_POWER_FAULT:
- /* TBD */
- break;
-
- default:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Received unknown/unsupported notification [%08x]\n",
- type));
- break;
- }
-
+ acpi_system_notify(handle, type);
acpi_bus_get_device(handle, &device);
if (device) {
driver = device->driver;
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -74,6 +74,7 @@ static inline void acpi_lpss_init(void)

bool acpi_queue_hotplug_work(struct work_struct *work);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
+void acpi_system_notify(acpi_handle handle, u32 type);

/* --------------------------------------------------------------------------
Device Node Initialization / Removal
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -485,7 +485,7 @@ static void acpi_device_hotplug(void *da
unlock_device_hotplug();
}

-static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
+void acpi_system_notify(acpi_handle handle, u32 type)
{
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
@@ -551,18 +551,6 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, NULL);
-}
-
-void acpi_remove_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb);
-}
-
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1982,34 +1970,6 @@ void acpi_scan_hotplug_enabled(struct ac
mutex_unlock(&acpi_scan_lock);
}

-static void acpi_scan_init_hotplug(acpi_handle handle, int type)
-{
- struct acpi_device_pnp pnp = {};
- struct acpi_hardware_id *hwid;
- struct acpi_scan_handler *handler;
-
- INIT_LIST_HEAD(&pnp.ids);
- acpi_set_pnp_ids(handle, &pnp, type);
-
- if (!pnp.type.hardware_id)
- goto out;
-
- /*
- * This relies on the fact that acpi_install_notify_handler() will not
- * install the same notify handler routine twice for the same handle.
- */
- list_for_each_entry(hwid, &pnp.ids, list) {
- handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- acpi_install_hotplug_notify_handler(handle);
- break;
- }
- }
-
-out:
- acpi_free_pnp_ids(&pnp);
-}
-
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **return_value)
{
@@ -2031,8 +1991,6 @@ static acpi_status acpi_bus_check_add(ac
return AE_OK;
}

- acpi_scan_init_hotplug(handle, type);
-
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -292,7 +292,6 @@ static acpi_status register_slot(acpi_ha
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- mutex_unlock(&acpiphp_context_lock);

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -300,8 +299,14 @@ static acpi_status register_slot(acpi_ha
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;

+ /*
+ * Dock stations' notify handler should be used for dock devices instead
+ * of the common one, so clear hp.event in their contexts.
+ */
if (acpi_has_method(handle, "_DCK"))
- newfunc->flags |= FUNC_HAS_DCK;
+ context->hp.event = NULL;
+
+ mutex_unlock(&acpiphp_context_lock);

/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
@@ -369,10 +374,6 @@ static acpi_status register_slot(acpi_ha
pr_debug("failed to register dock device\n");
}

- /* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle);
-
return AE_OK;
}

@@ -409,9 +410,6 @@ static void cleanup_bridge(struct acpiph

if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);
-
- if (!(func->flags & FUNC_HAS_DCK))
- acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -435,8 +435,6 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle);
-void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -167,7 +167,6 @@ struct acpiphp_attention_info

#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
-#define FUNC_HAS_DCK (0x00000004)

/* function prototypes */

2014-01-28 22:05:43

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 2/5][RFT] ACPI / hotplug: Introduce acpi_install_hotplug_notify_handler()

From: Rafael J. Wysocki <[email protected]>

Introduce a helper routine for installing acpi_hotplug_notify_cb()
as an ACPI notify handler for the given ACPI namespace node and make
acpi_scan_init_hotplug() use it.

This is to make subsequent changes easier to follow.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 15 +++++++++++++--
include/acpi/acpi_bus.h | 2 ++
2 files changed, 15 insertions(+), 2 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -523,6 +523,18 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+{
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb, data);
+}
+
+void acpi_remove_hotplug_notify_handler(acpi_handle handle)
+{
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb);
+}
+
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1961,8 +1973,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, handler);
+ acpi_install_hotplug_notify_handler(handle, handler);
break;
}
}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -434,6 +434,8 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver

2014-01-28 22:05:41

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 3/5][RFT] ACPI / hotplug / PCI: Consolidate ACPIPHP with ACPI core hotplug

From: Rafael J. Wysocki <[email protected]>

Use the same ACPI notify handler, acpi_hotplug_notify_cb() for both
ACPI-based PCI hotplug (ACPIPHP) and the generic ACPI-based hotplug
of devices. For PCI devices use the .hp.event() callback from
their ACPI companions that points to acpiphp_hotplug_event().
For other devices (CPU, memory, containers, PCI host bridges) the
generic ACPI-based device hotplug code is used.

This allows code duplication between ACPIPHP and the ACPI core to be
reduced significantly and makes further ACPI-based device hotplug
consolidation possible.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 85 ++++++++++++++++++----------
drivers/pci/hotplug/acpiphp_glue.c | 110 +++----------------------------------
include/acpi/acpi_bus.h | 1
3 files changed, 66 insertions(+), 130 deletions(-)

Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -145,6 +145,7 @@ struct acpi_scan_handler {

struct acpi_hotplug_context {
struct acpi_device *self;
+ int (*event)(struct acpi_device *, u32);
void (*release)(struct acpi_hotplug_context *);
};

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -439,40 +439,44 @@ static int acpi_scan_bus_check(struct ac
return 0;
}

+static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
+{
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ return acpi_scan_bus_check(adev);
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ return acpi_scan_device_check(adev);
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ case ACPI_OST_EC_OSPM_EJECT:
+ return acpi_scan_hot_remove(adev);
+ }
+ return -EINVAL;
+}
+
static void acpi_device_hotplug(void *data, u32 src)
{
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+ u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev = data;
- int error;
+ int error = -ENODEV;

lock_device_hotplug();
mutex_lock(&acpi_scan_lock);

/*
* The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it may have become invalid before
+ * are holding acpi_scan_lock, but it might have become invalid before
* that lock was acquired.
*/
if (adev->handle == INVALID_ACPI_HANDLE)
goto out;

- switch (src) {
- case ACPI_NOTIFY_BUS_CHECK:
- error = acpi_scan_bus_check(adev);
- break;
- case ACPI_NOTIFY_DEVICE_CHECK:
- error = acpi_scan_device_check(adev);
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- case ACPI_OST_EC_OSPM_EJECT:
- error = acpi_scan_hot_remove(adev);
- break;
- default:
- error = -EINVAL;
- break;
- }
- if (!error)
- ost_code = ACPI_OST_SC_SUCCESS;
+ if (adev->handler)
+ error = acpi_generic_hotplug_event(adev, src);
+ else if (adev->hp && adev->hp->event)
+ error = adev->hp->event(adev, src);
+
+ if (error)
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
@@ -483,35 +487,58 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_scan_handler *handler = data;
+ u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;

- if (acpi_bus_get_device(handle, &adev))
- goto err_out;
-
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;
+
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;
+
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (!handler->hotplug.enabled) {
+ if (handler && !handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
- goto err_out;
+ goto out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
- default:
- /* non-hotplug event; possibly handled by other handler */
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
return;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
+ goto out;
+
+ default:
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto out;
}
+
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+ if (acpi_bus_get_device(handle, &adev))
+ goto out;
+
get_device(&adev->dev);
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
@@ -519,7 +546,7 @@ static void acpi_hotplug_notify_cb(acpi_

put_device(&adev->dev);

- err_out:
+ out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,18 +53,14 @@
#include <linux/slab.h>
#include <linux/acpi.h>

-#include <asm/pgtable.h>
-
#include "../pci.h"
#include "acpiphp.h"

-#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
-
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
static DEFINE_MUTEX(acpiphp_context_lock);

-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, void *data);
@@ -91,6 +87,7 @@ static struct acpiphp_context *acpiphp_i

context->hp.self = adev;
context->hp.release = acpiphp_free_context;
+ context->hp.event = acpiphp_hotplug_event;
context->refcount = 1;
adev->hp = &context->hp;
return context;
@@ -373,14 +370,8 @@ static acpi_status register_slot(acpi_ha
}

/* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK)) {
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event,
- context);
- if (ACPI_FAILURE(status))
- acpi_handle_err(handle,
- "failed to install notify handler\n");
- }
+ if (!(newfunc->flags & FUNC_HAS_DCK))
+ acpi_install_hotplug_notify_handler(handle, NULL);

return AE_OK;
}
@@ -411,7 +402,6 @@ static void cleanup_bridge(struct acpiph
{
struct acpiphp_slot *slot;
struct acpiphp_func *func;
- acpi_status status;

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
@@ -420,13 +410,8 @@ static void cleanup_bridge(struct acpiph
if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);

- if (!(func->flags & FUNC_HAS_DCK)) {
- status = acpi_remove_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event);
- if (ACPI_FAILURE(status))
- pr_err("failed to remove notify handler\n");
- }
+ if (!(func->flags & FUNC_HAS_DCK))
+ acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
@@ -838,26 +823,15 @@ static void hotplug_event(u32 type, void
put_bridge(bridge);
}

-static void hotplug_event_work(void *data, u32 type)
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{
- struct acpi_device *adev = data;
struct acpiphp_context *context;
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
-
- acpi_scan_lock_acquire();
- /*
- * The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it might have become invalid before
- * that lock was acquired.
- */
- if (adev->handle == INVALID_ACPI_HANDLE)
- goto out;

mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(adev);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
- goto out;
+ return -ENODATA;
}
get_bridge(context->func.parent);
acpiphp_put_context(context);
@@ -867,73 +841,7 @@ static void hotplug_event_work(void *dat
hotplug_event(type, context);
pci_unlock_rescan_remove();
put_bridge(context->func.parent);
- ost_code = ACPI_OST_SC_SUCCESS;
-
- out:
- acpi_evaluate_hotplug_ost(adev->handle, type, ost_code, NULL);
- put_device(&adev->dev);
- acpi_scan_lock_release();
-}
-
-/**
- * handle_hotplug_event - handle ACPI hotplug event
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @data: pointer to acpiphp_context structure
- *
- * Handles ACPI event notification on slots.
- */
-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
-{
- struct acpi_device *adev;
- acpi_status status;
- u32 ost_code = ACPI_OST_SC_SUCCESS;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- case ACPI_NOTIFY_DEVICE_CHECK:
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS;
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }
-
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
- if (acpi_bus_get_device(handle, &adev))
- goto out;
-
- get_device(&adev->dev);
- status = acpi_hotplug_execute(hotplug_event_work, adev, type);
- if (ACPI_SUCCESS(status))
- return;
-
- put_device(&adev->dev);
-
- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
+ return 0;
}

/**

2014-01-28 22:05:37

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

From: Rafael J. Wysocki <[email protected]>

Use the observation that the ACPI scan handler of the device object
in acpi_hotplug_notify_cb() can be obtained from that device object's
handler pointer and do not pass it as data to
acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().

That allows the second argument of acpi_install_hotplug_notify_handler()
to be dropped, so do it and update its callers accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 23 ++++++++++++-----------
drivers/pci/hotplug/acpiphp_glue.c | 2 +-
include/acpi/acpi_bus.h | 2 +-
3 files changed, 14 insertions(+), 13 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- struct acpi_scan_handler *handler = data;
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;
@@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_

case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (handler && !handler->hotplug.enabled) {
- acpi_handle_err(handle, "Eject disabled\n");
- 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);
break;

case ACPI_NOTIFY_DEVICE_WAKE:
@@ -539,6 +531,15 @@ static void acpi_hotplug_notify_cb(acpi_
if (acpi_bus_get_device(handle, &adev))
goto out;

+ if (type == ACPI_NOTIFY_EJECT_REQUEST) {
+ if (adev->handler && !adev->handler->hotplug.enabled) {
+ acpi_handle_err(handle, "Eject disabled\n");
+ 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);
+ }
get_device(&adev->dev);
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
@@ -550,10 +551,10 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+void acpi_install_hotplug_notify_handler(acpi_handle handle)
{
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, data);
+ acpi_hotplug_notify_cb, NULL);
}

void acpi_remove_hotplug_notify_handler(acpi_handle handle)
@@ -2000,7 +2001,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_hotplug_notify_handler(handle, handler);
+ acpi_install_hotplug_notify_handler(handle);
break;
}
}
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -371,7 +371,7 @@ static acpi_status register_slot(acpi_ha

/* install notify handler */
if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle, NULL);
+ acpi_install_hotplug_notify_handler(handle);

return AE_OK;
}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -435,7 +435,7 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_install_hotplug_notify_handler(acpi_handle handle);
void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**

2014-01-28 22:06:57

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 0/5][RFT] ACPI / hotplug / PCI: Consolidation of ACPIPHP with ACPI core device hotplug

Hi All,

It looks like there's time for more adventurous stuff. :-)

The following series is on top of the one I sent on Sunday:

https://lkml.org/lkml/2014/1/26/191

The final outcome of the patches below is that all ACPI hotplug notifications
for PCI devices and for core system things like CPU, memory, PCI roots etc.,
will be dispatched from acpi_bus_notify() and it is not necessary to install a
separate hotplug notify handler for each device any more.

[1/5] Attach ACPIPHP hotplug contexts to struct acpi_device objects.
[2/5] Introduce wrappers for installing and removing hotplug notify handlers
(those wrappers go away later on, but they are useful for separating
changes).
[3/5] Consolidate ACPI hotplug signaling for PCI and ACPI core.
[4/5] Simplify notify handle registration wrapper.
[5/5] Dispatch ACPI hotplug notifications for "core" devices and PCI from acpi_bus_notify().

The ACPIPHP part of this has been tested on Acer Aspire S5 with Thunderbolt
hotplug, but if you have access to systems with hot-removable CPUs, PCI host
bridges and such things, please check if the above changes don't break them.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-01-28 22:07:24

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 1/5][RFT] ACPI / hotplug / PCI: Attach hotplug contexts to struct acpi_device

From: Rafael J. Wysocki <[email protected]>

The ACPI-based PCI hotplug (ACPIPHP) code currently attaches its
hotplug context objects directly to ACPI namespace nodes representing
hotplug devices. However, after recent changes causing struct
acpi_device to be created for every namespace node representing a
device (regardless of its status), that is not necessary any more.
Moreover, it's vulnerable to a theoretical issue that the ACPI
handle passed in the context between handle_hotplug_event() and
hotplug_event_work() may become invalid in the meantime (as a
result of a concurrent table unload).

For this reason, modify the code to attach the ACPIPHP's device
hotplug contexts to struct device objects representing hotplug
devices.

This also allows further consolidation of the ACPI hotplug code
to be carried out in subsequent changesets.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 3 +
drivers/pci/hotplug/acpiphp.h | 9 ++-
drivers/pci/hotplug/acpiphp_glue.c | 99 ++++++++++++++++++++++---------------
include/acpi/acpi_bus.h | 11 ++++
4 files changed, 80 insertions(+), 42 deletions(-)

Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -139,6 +139,16 @@ struct acpi_scan_handler {
};

/*
+ * ACPI Hotplug Context
+ * --------------------
+ */
+
+struct acpi_hotplug_context {
+ struct acpi_device *self;
+ void (*release)(struct acpi_hotplug_context *);
+};
+
+/*
* ACPI Driver
* -----------
*/
@@ -331,6 +341,7 @@ struct acpi_device {
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_scan_handler *handler;
+ struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
void *driver_data;
struct device dev;
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -1063,6 +1063,9 @@ static void acpi_device_del_work_fn(stru
mutex_unlock(&acpi_device_del_lock);

acpi_device_del(adev);
+ if (adev->hp && adev->hp->release)
+ adev->hp->release(adev->hp);
+
/*
* Drop references to all power resources that might have been
* used by the device.
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -116,12 +116,17 @@ struct acpiphp_func {
};

struct acpiphp_context {
+ struct acpi_hotplug_context hp;
struct acpiphp_func func;
- struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};

+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *fu

static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
{
- return func_to_context(func)->adev;
+ return func_to_context(func)->hp.self;
}

static inline acpi_handle func_to_handle(struct acpiphp_func *func)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,9 +53,13 @@
#include <linux/slab.h>
#include <linux/acpi.h>

+#include <asm/pgtable.h>
+
#include "../pci.h"
#include "acpiphp.h"

+#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
+
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
static DEFINE_MUTEX(acpiphp_context_lock);
@@ -66,9 +70,9 @@ static void acpiphp_set_hpp_values(struc
static void hotplug_event(u32 type, void *data);
static void free_bridge(struct kref *kref);

-static void acpiphp_context_handler(acpi_handle handle, void *context)
+static void acpiphp_free_context(struct acpi_hotplug_context *hp)
{
- /* Intentionally empty. */
+ kfree(to_acpiphp_context(hp));
}

/**
@@ -80,39 +84,29 @@ static void acpiphp_context_handler(acpi
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
- acpi_status status;

context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;

- context->adev = adev;
+ context->hp.self = adev;
+ context->hp.release = acpiphp_free_context;
context->refcount = 1;
- status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
- if (ACPI_FAILURE(status)) {
- kfree(context);
- return NULL;
- }
+ adev->hp = &context->hp;
return context;
}

/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
- * @handle: ACPI object handle to get the context for.
+ * @adev: ACPI device object to get the context for.
*
* Call under acpiphp_context_lock.
*/
-static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- struct acpiphp_context *context = NULL;
- acpi_status status;
- void *data;
+ struct acpiphp_context *context = to_acpiphp_context(adev->hp);

- status = acpi_get_data(handle, acpiphp_context_handler, &data);
- if (ACPI_SUCCESS(status)) {
- context = data;
- context->refcount++;
- }
+ context->refcount++;
return context;
}

@@ -130,7 +124,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->adev->handle, acpiphp_context_handler);
+ context->hp.self->hp = NULL;
kfree(context);
}

@@ -395,9 +389,13 @@ static struct acpiphp_bridge *acpiphp_ha
{
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;
+ struct acpi_device *adev;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return NULL;

mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (context) {
bridge = context->bridge;
if (bridge)
@@ -793,7 +791,7 @@ static void hotplug_event(u32 type, void
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
- acpi_handle handle = context->adev->handle;
+ acpi_handle handle = context->hp.self->handle;
struct acpiphp_bridge *bridge;

mutex_lock(&acpiphp_context_lock);
@@ -842,18 +840,39 @@ static void hotplug_event(u32 type, void

static void hotplug_event_work(void *data, u32 type)
{
- struct acpiphp_context *context = data;
+ struct acpi_device *adev = data;
+ struct acpiphp_context *context;
+ u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

acpi_scan_lock_acquire();
- pci_lock_rescan_remove();
+ /*
+ * The device object's ACPI handle cannot become invalid as long as we
+ * are holding acpi_scan_lock, but it might have become invalid before
+ * that lock was acquired.
+ */
+ if (adev->handle == INVALID_ACPI_HANDLE)
+ goto out;

- hotplug_event(type, context);
+ mutex_lock(&acpiphp_context_lock);
+ context = acpiphp_get_context(adev);
+ if (!context) {
+ mutex_unlock(&acpiphp_context_lock);
+ goto out;
+ }
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
+ mutex_unlock(&acpiphp_context_lock);

+ pci_lock_rescan_remove();
+ hotplug_event(type, context);
pci_unlock_rescan_remove();
- acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(context->adev->handle, type,
- ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);
+ ost_code = ACPI_OST_SC_SUCCESS;
+
+ out:
+ acpi_evaluate_hotplug_ost(adev->handle, type, ost_code, NULL);
+ put_device(&adev->dev);
+ acpi_scan_lock_release();
}

/**
@@ -866,7 +885,8 @@ static void hotplug_event_work(void *dat
*/
static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
{
- struct acpiphp_context *context;
+ struct acpi_device *adev;
+ acpi_status status;
u32 ost_code = ACPI_OST_SC_SUCCESS;

switch (type) {
@@ -901,17 +921,16 @@ static void handle_hotplug_event(acpi_ha
goto out;
}

- mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
- if (context && !WARN_ON(context->adev->handle != handle)) {
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_hotplug_execute(hotplug_event_work, context, type);
- mutex_unlock(&acpiphp_context_lock);
- return;
- }
- mutex_unlock(&acpiphp_context_lock);
ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+ if (acpi_bus_get_device(handle, &adev))
+ goto out;
+
+ get_device(&adev->dev);
+ status = acpi_hotplug_execute(hotplug_event_work, adev, type);
+ if (ACPI_SUCCESS(status))
+ return;
+
+ put_device(&adev->dev);

out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
@@ -967,7 +986,7 @@ void acpiphp_enumerate_slots(struct pci_
* bridge is not interesting to us either.
*/
mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
put_device(&bus->dev);

2014-01-28 23:50:34

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 0/5][RFT] ACPI / hotplug / PCI: Consolidation of ACPIPHP with ACPI core device hotplug

On Tuesday, January 28, 2014 11:10:30 PM Rafael J. Wysocki wrote:
> Hi All,
>
> It looks like there's time for more adventurous stuff. :-)
>
> The following series is on top of the one I sent on Sunday:
>
> https://lkml.org/lkml/2014/1/26/191
>
> The final outcome of the patches below is that all ACPI hotplug notifications
> for PCI devices and for core system things like CPU, memory, PCI roots etc.,
> will be dispatched from acpi_bus_notify() and it is not necessary to install a
> separate hotplug notify handler for each device any more.
>
> [1/5] Attach ACPIPHP hotplug contexts to struct acpi_device objects.
> [2/5] Introduce wrappers for installing and removing hotplug notify handlers
> (those wrappers go away later on, but they are useful for separating
> changes).
> [3/5] Consolidate ACPI hotplug signaling for PCI and ACPI core.
> [4/5] Simplify notify handle registration wrapper.
> [5/5] Dispatch ACPI hotplug notifications for "core" devices and PCI from acpi_bus_notify().

Unfortunately, I realized that patches [3-5/5] were buggy. The bugs were
kind of subtle and might not be easy to reproduce, but they were bugs anyway. :-)

A respin of the whole series follows.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-01-28 23:50:32

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Resend][PATCH 1/5][RFT] ACPI / hotplug / PCI: Attach hotplug contexts to struct acpi_device

From: Rafael J. Wysocki <[email protected]>

The ACPI-based PCI hotplug (ACPIPHP) code currently attaches its
hotplug context objects directly to ACPI namespace nodes representing
hotplug devices. However, after recent changes causing struct
acpi_device to be created for every namespace node representing a
device (regardless of its status), that is not necessary any more.
Moreover, it's vulnerable to a theoretical issue that the ACPI
handle passed in the context between handle_hotplug_event() and
hotplug_event_work() may become invalid in the meantime (as a
result of a concurrent table unload).

For this reason, modify the code to attach the ACPIPHP's device
hotplug contexts to struct device objects representing hotplug
devices.

This also allows further consolidation of the ACPI hotplug code
to be carried out in subsequent changesets.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 3 +
drivers/pci/hotplug/acpiphp.h | 9 ++-
drivers/pci/hotplug/acpiphp_glue.c | 99 ++++++++++++++++++++++---------------
include/acpi/acpi_bus.h | 11 ++++
4 files changed, 80 insertions(+), 42 deletions(-)

Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -139,6 +139,16 @@ struct acpi_scan_handler {
};

/*
+ * ACPI Hotplug Context
+ * --------------------
+ */
+
+struct acpi_hotplug_context {
+ struct acpi_device *self;
+ void (*release)(struct acpi_hotplug_context *);
+};
+
+/*
* ACPI Driver
* -----------
*/
@@ -331,6 +341,7 @@ struct acpi_device {
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_scan_handler *handler;
+ struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
void *driver_data;
struct device dev;
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -1063,6 +1063,9 @@ static void acpi_device_del_work_fn(stru
mutex_unlock(&acpi_device_del_lock);

acpi_device_del(adev);
+ if (adev->hp && adev->hp->release)
+ adev->hp->release(adev->hp);
+
/*
* Drop references to all power resources that might have been
* used by the device.
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -116,12 +116,17 @@ struct acpiphp_func {
};

struct acpiphp_context {
+ struct acpi_hotplug_context hp;
struct acpiphp_func func;
- struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};

+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *fu

static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
{
- return func_to_context(func)->adev;
+ return func_to_context(func)->hp.self;
}

static inline acpi_handle func_to_handle(struct acpiphp_func *func)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,9 +53,13 @@
#include <linux/slab.h>
#include <linux/acpi.h>

+#include <asm/pgtable.h>
+
#include "../pci.h"
#include "acpiphp.h"

+#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
+
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
static DEFINE_MUTEX(acpiphp_context_lock);
@@ -66,9 +70,9 @@ static void acpiphp_set_hpp_values(struc
static void hotplug_event(u32 type, void *data);
static void free_bridge(struct kref *kref);

-static void acpiphp_context_handler(acpi_handle handle, void *context)
+static void acpiphp_free_context(struct acpi_hotplug_context *hp)
{
- /* Intentionally empty. */
+ kfree(to_acpiphp_context(hp));
}

/**
@@ -80,39 +84,29 @@ static void acpiphp_context_handler(acpi
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
- acpi_status status;

context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;

- context->adev = adev;
+ context->hp.self = adev;
+ context->hp.release = acpiphp_free_context;
context->refcount = 1;
- status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
- if (ACPI_FAILURE(status)) {
- kfree(context);
- return NULL;
- }
+ adev->hp = &context->hp;
return context;
}

/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
- * @handle: ACPI object handle to get the context for.
+ * @adev: ACPI device object to get the context for.
*
* Call under acpiphp_context_lock.
*/
-static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- struct acpiphp_context *context = NULL;
- acpi_status status;
- void *data;
+ struct acpiphp_context *context = to_acpiphp_context(adev->hp);

- status = acpi_get_data(handle, acpiphp_context_handler, &data);
- if (ACPI_SUCCESS(status)) {
- context = data;
- context->refcount++;
- }
+ context->refcount++;
return context;
}

@@ -130,7 +124,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->adev->handle, acpiphp_context_handler);
+ context->hp.self->hp = NULL;
kfree(context);
}

@@ -395,9 +389,13 @@ static struct acpiphp_bridge *acpiphp_ha
{
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;
+ struct acpi_device *adev;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return NULL;

mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (context) {
bridge = context->bridge;
if (bridge)
@@ -793,7 +791,7 @@ static void hotplug_event(u32 type, void
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
- acpi_handle handle = context->adev->handle;
+ acpi_handle handle = context->hp.self->handle;
struct acpiphp_bridge *bridge;

mutex_lock(&acpiphp_context_lock);
@@ -842,18 +840,39 @@ static void hotplug_event(u32 type, void

static void hotplug_event_work(void *data, u32 type)
{
- struct acpiphp_context *context = data;
+ struct acpi_device *adev = data;
+ struct acpiphp_context *context;
+ u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

acpi_scan_lock_acquire();
- pci_lock_rescan_remove();
+ /*
+ * The device object's ACPI handle cannot become invalid as long as we
+ * are holding acpi_scan_lock, but it might have become invalid before
+ * that lock was acquired.
+ */
+ if (adev->handle == INVALID_ACPI_HANDLE)
+ goto out;

- hotplug_event(type, context);
+ mutex_lock(&acpiphp_context_lock);
+ context = acpiphp_get_context(adev);
+ if (!context) {
+ mutex_unlock(&acpiphp_context_lock);
+ goto out;
+ }
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
+ mutex_unlock(&acpiphp_context_lock);

+ pci_lock_rescan_remove();
+ hotplug_event(type, context);
pci_unlock_rescan_remove();
- acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(context->adev->handle, type,
- ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);
+ ost_code = ACPI_OST_SC_SUCCESS;
+
+ out:
+ acpi_evaluate_hotplug_ost(adev->handle, type, ost_code, NULL);
+ put_device(&adev->dev);
+ acpi_scan_lock_release();
}

/**
@@ -866,7 +885,8 @@ static void hotplug_event_work(void *dat
*/
static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
{
- struct acpiphp_context *context;
+ struct acpi_device *adev;
+ acpi_status status;
u32 ost_code = ACPI_OST_SC_SUCCESS;

switch (type) {
@@ -901,17 +921,16 @@ static void handle_hotplug_event(acpi_ha
goto out;
}

- mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
- if (context && !WARN_ON(context->adev->handle != handle)) {
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_hotplug_execute(hotplug_event_work, context, type);
- mutex_unlock(&acpiphp_context_lock);
- return;
- }
- mutex_unlock(&acpiphp_context_lock);
ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+ if (acpi_bus_get_device(handle, &adev))
+ goto out;
+
+ get_device(&adev->dev);
+ status = acpi_hotplug_execute(hotplug_event_work, adev, type);
+ if (ACPI_SUCCESS(status))
+ return;
+
+ put_device(&adev->dev);

out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
@@ -967,7 +986,7 @@ void acpiphp_enumerate_slots(struct pci_
* bridge is not interesting to us either.
*/
mutex_lock(&acpiphp_context_lock);
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
put_device(&bus->dev);

2014-01-28 23:51:07

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update][PATCH 3/5][RFT] ACPI / hotplug / PCI: Consolidate ACPIPHP with ACPI core hotplug

From: Rafael J. Wysocki <[email protected]>

Use the same ACPI notify handler, acpi_hotplug_notify_cb() for both
ACPI-based PCI hotplug (ACPIPHP) and the generic ACPI-based hotplug
of devices. For PCI devices use the .hp.event() callback from
their ACPI companions that points to acpiphp_hotplug_event().
For other devices (CPU, memory, containers, PCI host bridges) the
generic ACPI-based device hotplug code is used.

This allows code duplication between ACPIPHP and the ACPI core to be
reduced significantly and makes further ACPI-based device hotplug
consolidation possible.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 79 +++++++++++++++++---------
drivers/pci/hotplug/acpiphp_glue.c | 110 +++----------------------------------
include/acpi/acpi_bus.h | 1
3 files changed, 63 insertions(+), 127 deletions(-)

Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -145,6 +145,7 @@ struct acpi_scan_handler {

struct acpi_hotplug_context {
struct acpi_device *self;
+ int (*event)(struct acpi_device *, u32);
void (*release)(struct acpi_hotplug_context *);
};

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -439,38 +439,42 @@ static int acpi_scan_bus_check(struct ac
return 0;
}

+static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
+{
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ return acpi_scan_bus_check(adev);
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ return acpi_scan_device_check(adev);
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ case ACPI_OST_EC_OSPM_EJECT:
+ return acpi_scan_hot_remove(adev);
+ }
+ return -EINVAL;
+}
+
static void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
- int error;
+ int error = -ENODEV;

lock_device_hotplug();
mutex_lock(&acpi_scan_lock);

/*
* The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it may have become invalid before
+ * are holding acpi_scan_lock, but it might have become invalid before
* that lock was acquired.
*/
if (adev->handle == INVALID_ACPI_HANDLE)
goto out;

- switch (src) {
- case ACPI_NOTIFY_BUS_CHECK:
- error = acpi_scan_bus_check(adev);
- break;
- case ACPI_NOTIFY_DEVICE_CHECK:
- error = acpi_scan_device_check(adev);
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- case ACPI_OST_EC_OSPM_EJECT:
- error = acpi_scan_hot_remove(adev);
- break;
- default:
- error = -EINVAL;
- break;
- }
+ if (adev->handler)
+ error = acpi_generic_hotplug_event(adev, src);
+ else if (adev->hp && adev->hp->event)
+ error = adev->hp->event(adev, src);
+
if (!error)
ost_code = ACPI_OST_SC_SUCCESS;

@@ -483,35 +487,58 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_scan_handler *handler = data;
+ u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;

- if (acpi_bus_get_device(handle, &adev))
- goto err_out;
-
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;
+
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;
+
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (!handler->hotplug.enabled) {
+ if (handler && !handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
- goto err_out;
+ goto out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
- default:
- /* non-hotplug event; possibly handled by other handler */
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
return;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
+ goto out;
+
+ default:
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto out;
}
+
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+ if (acpi_bus_get_device(handle, &adev))
+ goto out;
+
get_device(&adev->dev);
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
@@ -519,7 +546,7 @@ static void acpi_hotplug_notify_cb(acpi_

put_device(&adev->dev);

- err_out:
+ out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,18 +53,14 @@
#include <linux/slab.h>
#include <linux/acpi.h>

-#include <asm/pgtable.h>
-
#include "../pci.h"
#include "acpiphp.h"

-#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
-
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
static DEFINE_MUTEX(acpiphp_context_lock);

-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, void *data);
@@ -91,6 +87,7 @@ static struct acpiphp_context *acpiphp_i

context->hp.self = adev;
context->hp.release = acpiphp_free_context;
+ context->hp.event = acpiphp_hotplug_event;
context->refcount = 1;
adev->hp = &context->hp;
return context;
@@ -373,14 +370,8 @@ static acpi_status register_slot(acpi_ha
}

/* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK)) {
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event,
- context);
- if (ACPI_FAILURE(status))
- acpi_handle_err(handle,
- "failed to install notify handler\n");
- }
+ if (!(newfunc->flags & FUNC_HAS_DCK))
+ acpi_install_hotplug_notify_handler(handle, NULL);

return AE_OK;
}
@@ -411,7 +402,6 @@ static void cleanup_bridge(struct acpiph
{
struct acpiphp_slot *slot;
struct acpiphp_func *func;
- acpi_status status;

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
@@ -420,13 +410,8 @@ static void cleanup_bridge(struct acpiph
if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);

- if (!(func->flags & FUNC_HAS_DCK)) {
- status = acpi_remove_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event);
- if (ACPI_FAILURE(status))
- pr_err("failed to remove notify handler\n");
- }
+ if (!(func->flags & FUNC_HAS_DCK))
+ acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
@@ -838,26 +823,15 @@ static void hotplug_event(u32 type, void
put_bridge(bridge);
}

-static void hotplug_event_work(void *data, u32 type)
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{
- struct acpi_device *adev = data;
struct acpiphp_context *context;
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
-
- acpi_scan_lock_acquire();
- /*
- * The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it might have become invalid before
- * that lock was acquired.
- */
- if (adev->handle == INVALID_ACPI_HANDLE)
- goto out;

mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(adev);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
- goto out;
+ return -ENODATA;
}
get_bridge(context->func.parent);
acpiphp_put_context(context);
@@ -867,73 +841,7 @@ static void hotplug_event_work(void *dat
hotplug_event(type, context);
pci_unlock_rescan_remove();
put_bridge(context->func.parent);
- ost_code = ACPI_OST_SC_SUCCESS;
-
- out:
- acpi_evaluate_hotplug_ost(adev->handle, type, ost_code, NULL);
- put_device(&adev->dev);
- acpi_scan_lock_release();
-}
-
-/**
- * handle_hotplug_event - handle ACPI hotplug event
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @data: pointer to acpiphp_context structure
- *
- * Handles ACPI event notification on slots.
- */
-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
-{
- struct acpi_device *adev;
- acpi_status status;
- u32 ost_code = ACPI_OST_SC_SUCCESS;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- case ACPI_NOTIFY_DEVICE_CHECK:
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS;
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }
-
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
- if (acpi_bus_get_device(handle, &adev))
- goto out;
-
- get_device(&adev->dev);
- status = acpi_hotplug_execute(hotplug_event_work, adev, type);
- if (ACPI_SUCCESS(status))
- return;
-
- put_device(&adev->dev);
-
- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
+ return 0;
}

/**

2014-01-28 23:51:05

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Resend][PATCH 2/5][RFT] ACPI / hotplug: Introduce acpi_install_hotplug_notify_handler()

From: Rafael J. Wysocki <[email protected]>

Introduce a helper routine for installing acpi_hotplug_notify_cb()
as an ACPI notify handler for the given ACPI namespace node and make
acpi_scan_init_hotplug() use it.

This is to make subsequent changes easier to follow.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 15 +++++++++++++--
include/acpi/acpi_bus.h | 2 ++
2 files changed, 15 insertions(+), 2 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -523,6 +523,18 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+{
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb, data);
+}
+
+void acpi_remove_hotplug_notify_handler(acpi_handle handle)
+{
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb);
+}
+
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1961,8 +1973,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, handler);
+ acpi_install_hotplug_notify_handler(handle, handler);
break;
}
}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -434,6 +434,8 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver

2014-01-28 23:51:47

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

From: Rafael J. Wysocki <[email protected]>

Use the observation that the ACPI scan handler of the device object
in acpi_hotplug_notify_cb() can be obtained from that device object's
handler pointer and do not pass it as data to
acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().

That allows the second argument of acpi_install_hotplug_notify_handler()
to be dropped, so do it and update its callers accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 31 ++++++++++++++++++-------------
drivers/pci/hotplug/acpiphp_glue.c | 2 +-
include/acpi/acpi_bus.h | 2 +-
3 files changed, 20 insertions(+), 15 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- struct acpi_scan_handler *handler = data;
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;
@@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_

case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (handler && !handler->hotplug.enabled) {
- acpi_handle_err(handle, "Eject disabled\n");
- 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);
break;

case ACPI_NOTIFY_DEVICE_WAKE:
@@ -535,11 +527,24 @@ static void acpi_hotplug_notify_cb(acpi_
goto out;
}

+ mutex_lock(&acpi_scan_lock);
ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
- if (acpi_bus_get_device(handle, &adev))
+ if (acpi_bus_get_device(handle, &adev)) {
+ mutex_unlock(&acpi_scan_lock);
goto out;
-
+ }
+ if (type == ACPI_NOTIFY_EJECT_REQUEST) {
+ if (adev->handler && !adev->handler->hotplug.enabled) {
+ acpi_handle_err(handle, "Eject disabled\n");
+ ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
+ mutex_unlock(&acpi_scan_lock);
+ goto out;
+ }
+ acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
+ ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
+ }
get_device(&adev->dev);
+ mutex_unlock(&acpi_scan_lock);
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
return;
@@ -550,10 +555,10 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+void acpi_install_hotplug_notify_handler(acpi_handle handle)
{
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, data);
+ acpi_hotplug_notify_cb, NULL);
}

void acpi_remove_hotplug_notify_handler(acpi_handle handle)
@@ -2000,7 +2005,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_hotplug_notify_handler(handle, handler);
+ acpi_install_hotplug_notify_handler(handle);
break;
}
}
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -371,7 +371,7 @@ static acpi_status register_slot(acpi_ha

/* install notify handler */
if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle, NULL);
+ acpi_install_hotplug_notify_handler(handle);

return AE_OK;
}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -435,7 +435,7 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_install_hotplug_notify_handler(acpi_handle handle);
void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**

2014-01-28 23:52:02

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update][PATCH 5/5][RFT] ACPI / hotplug / PCI: Hotplug notifications from acpi_bus_notify()

From: Rafael J. Wysocki <[email protected]>

Since acpi_bus_notify() is executed on all notifications for all
devices anyway, rename acpi_hotplug_notify_cb() to acpi_system_notify()
and call it directly from acpi_bus_notify() instead of installing
notify handlers pointing to it for all hotplug devices.

This change reduces both the size and complexity of ACPI-base device
hotplug code. Moreover, since acpi_system_notify() only does
significant things for devices that either have an ACPI scan handler,
or have a hotplug context with .eject() defined, and those devices
had notify handlers pointing to acpi_hotplug_notify_cb() installed
before anyway, this change shouldn't modify functionality.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/bus.c | 42 ----------------------
drivers/acpi/internal.h | 1
drivers/acpi/scan.c | 68 +++++++++----------------------------
drivers/pci/hotplug/acpiphp.h | 1
drivers/pci/hotplug/acpiphp_glue.c | 16 +++-----
include/acpi/acpi_bus.h | 2 -
6 files changed, 26 insertions(+), 104 deletions(-)

Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -345,47 +345,7 @@ static void acpi_bus_notify(acpi_handle
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
type, handle));

- switch (type) {
-
- case ACPI_NOTIFY_BUS_CHECK:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_EJECT_REQUEST:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
- /* TBD: Exactly what does 'light' mean? */
- break;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_POWER_FAULT:
- /* TBD */
- break;
-
- default:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Received unknown/unsupported notification [%08x]\n",
- type));
- break;
- }
-
+ acpi_system_notify(handle, type);
acpi_bus_get_device(handle, &device);
if (device) {
driver = device->driver;
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -74,6 +74,7 @@ static inline void acpi_lpss_init(void)

bool acpi_queue_hotplug_work(struct work_struct *work);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
+void acpi_system_notify(acpi_handle handle, u32 type);

/* --------------------------------------------------------------------------
Device Node Initialization / Removal
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -485,11 +485,11 @@ static void acpi_device_hotplug(void *da
unlock_device_hotplug();
}

-static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
+void acpi_system_notify(acpi_handle handle, u32 type)
{
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
- acpi_status status;
+ bool execute_ost = true;

switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
@@ -543,28 +543,24 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
}
- get_device(&adev->dev);
- mutex_unlock(&acpi_scan_lock);
- status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
- if (ACPI_SUCCESS(status))
- return;
-
- put_device(&adev->dev);
+ if (adev->handler || (adev->hp && adev->hp->event)) {
+ acpi_status status;

- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
-}
+ get_device(&adev->dev);
+ mutex_unlock(&acpi_scan_lock);
+ status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
+ if (ACPI_SUCCESS(status))
+ return;

-void acpi_install_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, NULL);
-}
+ put_device(&adev->dev);
+ } else {
+ mutex_unlock(&acpi_scan_lock);
+ execute_ost = false;
+ }

-void acpi_remove_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb);
+ out:
+ if (execute_ost)
+ acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

static ssize_t real_power_state_show(struct device *dev,
@@ -1986,34 +1982,6 @@ void acpi_scan_hotplug_enabled(struct ac
mutex_unlock(&acpi_scan_lock);
}

-static void acpi_scan_init_hotplug(acpi_handle handle, int type)
-{
- struct acpi_device_pnp pnp = {};
- struct acpi_hardware_id *hwid;
- struct acpi_scan_handler *handler;
-
- INIT_LIST_HEAD(&pnp.ids);
- acpi_set_pnp_ids(handle, &pnp, type);
-
- if (!pnp.type.hardware_id)
- goto out;
-
- /*
- * This relies on the fact that acpi_install_notify_handler() will not
- * install the same notify handler routine twice for the same handle.
- */
- list_for_each_entry(hwid, &pnp.ids, list) {
- handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- acpi_install_hotplug_notify_handler(handle);
- break;
- }
- }
-
-out:
- acpi_free_pnp_ids(&pnp);
-}
-
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **return_value)
{
@@ -2035,8 +2003,6 @@ static acpi_status acpi_bus_check_add(ac
return AE_OK;
}

- acpi_scan_init_hotplug(handle, type);
-
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -292,7 +292,6 @@ static acpi_status register_slot(acpi_ha
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- mutex_unlock(&acpiphp_context_lock);

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -300,8 +299,14 @@ static acpi_status register_slot(acpi_ha
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;

+ /*
+ * Dock stations' notify handler should be used for dock devices instead
+ * of the common one, so clear hp.event in their contexts.
+ */
if (acpi_has_method(handle, "_DCK"))
- newfunc->flags |= FUNC_HAS_DCK;
+ context->hp.event = NULL;
+
+ mutex_unlock(&acpiphp_context_lock);

/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
@@ -369,10 +374,6 @@ static acpi_status register_slot(acpi_ha
pr_debug("failed to register dock device\n");
}

- /* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle);
-
return AE_OK;
}

@@ -409,9 +410,6 @@ static void cleanup_bridge(struct acpiph

if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);
-
- if (!(func->flags & FUNC_HAS_DCK))
- acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -435,8 +435,6 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle);
-void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -167,7 +167,6 @@ struct acpiphp_attention_info

#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
-#define FUNC_HAS_DCK (0x00000004)

/* function prototypes */

2014-01-30 15:13:50

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update 2x][PATCH 5/5][RFT] ACPI / hotplug / PCI: Hotplug notifications from acpi_bus_notify()

From: Rafael J. Wysocki <[email protected]>

Since acpi_bus_notify() is executed on all notifications for all
devices anyway, rename acpi_hotplug_notify_cb() to acpi_system_notify()
and call it directly from acpi_bus_notify() instead of installing
notify handlers pointing to it for all hotplug devices.

This change reduces both the size and complexity of ACPI-base device
hotplug code. Moreover, since acpi_system_notify() only does
significant things for devices that either have an ACPI scan handler,
or have a hotplug context with .eject() defined, and those devices
had notify handlers pointing to acpi_hotplug_notify_cb() installed
before anyway, this change shouldn't modify functionality.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---

Well, I'm not sure why exactly I left the execute_ost variable in
acpi_system_notify() in the previous version.

Guess what, it's a good idea to review your own patches a couple of days
after sending them out. :-)

Rafael

---
drivers/acpi/bus.c | 42 ------------------------
drivers/acpi/internal.h | 1
drivers/acpi/scan.c | 64 ++++++++-----------------------------
drivers/pci/hotplug/acpiphp.h | 1
drivers/pci/hotplug/acpiphp_glue.c | 16 ++++-----
include/acpi/acpi_bus.h | 2 -
6 files changed, 23 insertions(+), 103 deletions(-)

Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -345,47 +345,7 @@ static void acpi_bus_notify(acpi_handle
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
type, handle));

- switch (type) {
-
- case ACPI_NOTIFY_BUS_CHECK:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_EJECT_REQUEST:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
- /* TBD: Exactly what does 'light' mean? */
- break;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_POWER_FAULT:
- /* TBD */
- break;
-
- default:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Received unknown/unsupported notification [%08x]\n",
- type));
- break;
- }
-
+ acpi_system_notify(handle, type);
acpi_bus_get_device(handle, &device);
if (device) {
driver = device->driver;
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -74,6 +74,7 @@ static inline void acpi_lpss_init(void)

bool acpi_queue_hotplug_work(struct work_struct *work);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
+void acpi_system_notify(acpi_handle handle, u32 type);

/* --------------------------------------------------------------------------
Device Node Initialization / Removal
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -485,11 +485,10 @@ static void acpi_device_hotplug(void *da
unlock_device_hotplug();
}

-static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
+void acpi_system_notify(acpi_handle handle, u32 type)
{
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
- acpi_status status;

switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
@@ -543,30 +542,25 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
}
- get_device(&adev->dev);
- mutex_unlock(&acpi_scan_lock);
- status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
- if (ACPI_SUCCESS(status))
- return;
+ if (adev->handler || (adev->hp && adev->hp->event)) {
+ acpi_status status;

- put_device(&adev->dev);
+ get_device(&adev->dev);
+ mutex_unlock(&acpi_scan_lock);
+ status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
+ if (ACPI_SUCCESS(status))
+ return;
+
+ put_device(&adev->dev);
+ } else {
+ mutex_unlock(&acpi_scan_lock);
+ return;
+ }

out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, NULL);
-}
-
-void acpi_remove_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb);
-}
-
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1986,34 +1980,6 @@ void acpi_scan_hotplug_enabled(struct ac
mutex_unlock(&acpi_scan_lock);
}

-static void acpi_scan_init_hotplug(acpi_handle handle, int type)
-{
- struct acpi_device_pnp pnp = {};
- struct acpi_hardware_id *hwid;
- struct acpi_scan_handler *handler;
-
- INIT_LIST_HEAD(&pnp.ids);
- acpi_set_pnp_ids(handle, &pnp, type);
-
- if (!pnp.type.hardware_id)
- goto out;
-
- /*
- * This relies on the fact that acpi_install_notify_handler() will not
- * install the same notify handler routine twice for the same handle.
- */
- list_for_each_entry(hwid, &pnp.ids, list) {
- handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- acpi_install_hotplug_notify_handler(handle);
- break;
- }
- }
-
-out:
- acpi_free_pnp_ids(&pnp);
-}
-
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **return_value)
{
@@ -2035,8 +2001,6 @@ static acpi_status acpi_bus_check_add(ac
return AE_OK;
}

- acpi_scan_init_hotplug(handle, type);
-
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -292,7 +292,6 @@ static acpi_status register_slot(acpi_ha
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- mutex_unlock(&acpiphp_context_lock);

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -300,8 +299,14 @@ static acpi_status register_slot(acpi_ha
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;

+ /*
+ * Dock stations' notify handler should be used for dock devices instead
+ * of the common one, so clear hp.event in their contexts.
+ */
if (acpi_has_method(handle, "_DCK"))
- newfunc->flags |= FUNC_HAS_DCK;
+ context->hp.event = NULL;
+
+ mutex_unlock(&acpiphp_context_lock);

/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
@@ -369,10 +374,6 @@ static acpi_status register_slot(acpi_ha
pr_debug("failed to register dock device\n");
}

- /* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle);
-
return AE_OK;
}

@@ -409,9 +410,6 @@ static void cleanup_bridge(struct acpiph

if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);
-
- if (!(func->flags & FUNC_HAS_DCK))
- acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -435,8 +435,6 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle);
-void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -167,7 +167,6 @@ struct acpiphp_attention_info

#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
-#define FUNC_HAS_DCK (0x00000004)

/* function prototypes */

2014-01-31 15:21:34

by Mika Westerberg

[permalink] [raw]
Subject: Re: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

On Wed, Jan 29, 2014 at 01:00:57AM +0100, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> Use the observation that the ACPI scan handler of the device object
> in acpi_hotplug_notify_cb() can be obtained from that device object's
> handler pointer and do not pass it as data to
> acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().
>
> That allows the second argument of acpi_install_hotplug_notify_handler()
> to be dropped, so do it and update its callers accordingly.
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>
> ---
> drivers/acpi/scan.c | 31 ++++++++++++++++++-------------
> drivers/pci/hotplug/acpiphp_glue.c | 2 +-
> include/acpi/acpi_bus.h | 2 +-
> 3 files changed, 20 insertions(+), 15 deletions(-)
>
> Index: linux-pm/drivers/acpi/scan.c
> ===================================================================
> --- linux-pm.orig/drivers/acpi/scan.c
> +++ linux-pm/drivers/acpi/scan.c
> @@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da
>
> static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
> {
> - struct acpi_scan_handler *handler = data;
> u32 ost_code = ACPI_OST_SC_SUCCESS;
> struct acpi_device *adev;
> acpi_status status;
> @@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_
>
> case ACPI_NOTIFY_EJECT_REQUEST:
> acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
> - if (handler && !handler->hotplug.enabled) {
> - acpi_handle_err(handle, "Eject disabled\n");
> - 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);
> break;
>
> case ACPI_NOTIFY_DEVICE_WAKE:
> @@ -535,11 +527,24 @@ static void acpi_hotplug_notify_cb(acpi_
> goto out;
> }
>
> + mutex_lock(&acpi_scan_lock);

This is weird. I'm testing against your test-next branch which has this
patch included. If I remove locking here, TBT hotplug works fine. Otherwise
I can see the first hotplug/unplug works but after that I cannot get any
events anymore.

> ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
> - if (acpi_bus_get_device(handle, &adev))
> + if (acpi_bus_get_device(handle, &adev)) {
> + mutex_unlock(&acpi_scan_lock);
> goto out;
> -
> + }
> + if (type == ACPI_NOTIFY_EJECT_REQUEST) {
> + if (adev->handler && !adev->handler->hotplug.enabled) {
> + acpi_handle_err(handle, "Eject disabled\n");
> + ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
> + mutex_unlock(&acpi_scan_lock);
> + goto out;
> + }
> + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
> + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
> + }
> get_device(&adev->dev);
> + mutex_unlock(&acpi_scan_lock);
> status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
> if (ACPI_SUCCESS(status))
> return;

2014-01-31 15:28:01

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

On Friday, January 31, 2014 05:28:36 PM Mika Westerberg wrote:
> On Wed, Jan 29, 2014 at 01:00:57AM +0100, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <[email protected]>
> >
> > Use the observation that the ACPI scan handler of the device object
> > in acpi_hotplug_notify_cb() can be obtained from that device object's
> > handler pointer and do not pass it as data to
> > acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().
> >
> > That allows the second argument of acpi_install_hotplug_notify_handler()
> > to be dropped, so do it and update its callers accordingly.
> >
> > Signed-off-by: Rafael J. Wysocki <[email protected]>
> > ---
> > drivers/acpi/scan.c | 31 ++++++++++++++++++-------------
> > drivers/pci/hotplug/acpiphp_glue.c | 2 +-
> > include/acpi/acpi_bus.h | 2 +-
> > 3 files changed, 20 insertions(+), 15 deletions(-)
> >
> > Index: linux-pm/drivers/acpi/scan.c
> > ===================================================================
> > --- linux-pm.orig/drivers/acpi/scan.c
> > +++ linux-pm/drivers/acpi/scan.c
> > @@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da
> >
> > static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
> > {
> > - struct acpi_scan_handler *handler = data;
> > u32 ost_code = ACPI_OST_SC_SUCCESS;
> > struct acpi_device *adev;
> > acpi_status status;
> > @@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_
> >
> > case ACPI_NOTIFY_EJECT_REQUEST:
> > acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
> > - if (handler && !handler->hotplug.enabled) {
> > - acpi_handle_err(handle, "Eject disabled\n");
> > - 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);
> > break;
> >
> > case ACPI_NOTIFY_DEVICE_WAKE:
> > @@ -535,11 +527,24 @@ static void acpi_hotplug_notify_cb(acpi_
> > goto out;
> > }
> >
> > + mutex_lock(&acpi_scan_lock);
>
> This is weird. I'm testing against your test-next branch which has this
> patch included. If I remove locking here, TBT hotplug works fine. Otherwise
> I can see the first hotplug/unplug works but after that I cannot get any
> events anymore.

Weird indeed. I don't seem to be able to reproduce that on my Aspire S5.

What system are you testing on?

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-01-31 15:33:10

by Mika Westerberg

[permalink] [raw]
Subject: Re: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

On Fri, Jan 31, 2014 at 04:42:21PM +0100, Rafael J. Wysocki wrote:
> On Friday, January 31, 2014 05:28:36 PM Mika Westerberg wrote:
> > On Wed, Jan 29, 2014 at 01:00:57AM +0100, Rafael J. Wysocki wrote:
> > > From: Rafael J. Wysocki <[email protected]>
> > >
> > > Use the observation that the ACPI scan handler of the device object
> > > in acpi_hotplug_notify_cb() can be obtained from that device object's
> > > handler pointer and do not pass it as data to
> > > acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().
> > >
> > > That allows the second argument of acpi_install_hotplug_notify_handler()
> > > to be dropped, so do it and update its callers accordingly.
> > >
> > > Signed-off-by: Rafael J. Wysocki <[email protected]>
> > > ---
> > > drivers/acpi/scan.c | 31 ++++++++++++++++++-------------
> > > drivers/pci/hotplug/acpiphp_glue.c | 2 +-
> > > include/acpi/acpi_bus.h | 2 +-
> > > 3 files changed, 20 insertions(+), 15 deletions(-)
> > >
> > > Index: linux-pm/drivers/acpi/scan.c
> > > ===================================================================
> > > --- linux-pm.orig/drivers/acpi/scan.c
> > > +++ linux-pm/drivers/acpi/scan.c
> > > @@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da
> > >
> > > static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
> > > {
> > > - struct acpi_scan_handler *handler = data;
> > > u32 ost_code = ACPI_OST_SC_SUCCESS;
> > > struct acpi_device *adev;
> > > acpi_status status;
> > > @@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_
> > >
> > > case ACPI_NOTIFY_EJECT_REQUEST:
> > > acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
> > > - if (handler && !handler->hotplug.enabled) {
> > > - acpi_handle_err(handle, "Eject disabled\n");
> > > - 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);
> > > break;
> > >
> > > case ACPI_NOTIFY_DEVICE_WAKE:
> > > @@ -535,11 +527,24 @@ static void acpi_hotplug_notify_cb(acpi_
> > > goto out;
> > > }
> > >
> > > + mutex_lock(&acpi_scan_lock);
> >
> > This is weird. I'm testing against your test-next branch which has this
> > patch included. If I remove locking here, TBT hotplug works fine. Otherwise
> > I can see the first hotplug/unplug works but after that I cannot get any
> > events anymore.
>
> Weird indeed. I don't seem to be able to reproduce that on my Aspire S5.
>
> What system are you testing on?

This is Intel NUC. Let me see if I can reproduce this on S5 with longer
chain of devices.

2014-01-31 15:55:05

by Mika Westerberg

[permalink] [raw]
Subject: Re: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

On Fri, Jan 31, 2014 at 05:40:11PM +0200, Mika Westerberg wrote:
> On Fri, Jan 31, 2014 at 04:42:21PM +0100, Rafael J. Wysocki wrote:
> > On Friday, January 31, 2014 05:28:36 PM Mika Westerberg wrote:
> > > On Wed, Jan 29, 2014 at 01:00:57AM +0100, Rafael J. Wysocki wrote:
> > > > From: Rafael J. Wysocki <[email protected]>
> > > >
> > > > Use the observation that the ACPI scan handler of the device object
> > > > in acpi_hotplug_notify_cb() can be obtained from that device object's
> > > > handler pointer and do not pass it as data to
> > > > acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().
> > > >
> > > > That allows the second argument of acpi_install_hotplug_notify_handler()
> > > > to be dropped, so do it and update its callers accordingly.
> > > >
> > > > Signed-off-by: Rafael J. Wysocki <[email protected]>
> > > > ---
> > > > drivers/acpi/scan.c | 31 ++++++++++++++++++-------------
> > > > drivers/pci/hotplug/acpiphp_glue.c | 2 +-
> > > > include/acpi/acpi_bus.h | 2 +-
> > > > 3 files changed, 20 insertions(+), 15 deletions(-)
> > > >
> > > > Index: linux-pm/drivers/acpi/scan.c
> > > > ===================================================================
> > > > --- linux-pm.orig/drivers/acpi/scan.c
> > > > +++ linux-pm/drivers/acpi/scan.c
> > > > @@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da
> > > >
> > > > static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
> > > > {
> > > > - struct acpi_scan_handler *handler = data;
> > > > u32 ost_code = ACPI_OST_SC_SUCCESS;
> > > > struct acpi_device *adev;
> > > > acpi_status status;
> > > > @@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_
> > > >
> > > > case ACPI_NOTIFY_EJECT_REQUEST:
> > > > acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
> > > > - if (handler && !handler->hotplug.enabled) {
> > > > - acpi_handle_err(handle, "Eject disabled\n");
> > > > - 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);
> > > > break;
> > > >
> > > > case ACPI_NOTIFY_DEVICE_WAKE:
> > > > @@ -535,11 +527,24 @@ static void acpi_hotplug_notify_cb(acpi_
> > > > goto out;
> > > > }
> > > >
> > > > + mutex_lock(&acpi_scan_lock);
> > >
> > > This is weird. I'm testing against your test-next branch which has this
> > > patch included. If I remove locking here, TBT hotplug works fine. Otherwise
> > > I can see the first hotplug/unplug works but after that I cannot get any
> > > events anymore.
> >
> > Weird indeed. I don't seem to be able to reproduce that on my Aspire S5.
> >
> > What system are you testing on?
>
> This is Intel NUC. Let me see if I can reproduce this on S5 with longer
> chain of devices.

OK, so on S5 this works even with longer chain. However, exactly the same
kernel image on NUC fails after first hotplug/unplug cycle.

2014-01-31 16:02:00

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

On Friday, January 31, 2014 06:01:57 PM Mika Westerberg wrote:
> On Fri, Jan 31, 2014 at 05:40:11PM +0200, Mika Westerberg wrote:
> > On Fri, Jan 31, 2014 at 04:42:21PM +0100, Rafael J. Wysocki wrote:
> > > On Friday, January 31, 2014 05:28:36 PM Mika Westerberg wrote:
> > > > On Wed, Jan 29, 2014 at 01:00:57AM +0100, Rafael J. Wysocki wrote:
> > > > > From: Rafael J. Wysocki <[email protected]>
> > > > >
> > > > > Use the observation that the ACPI scan handler of the device object
> > > > > in acpi_hotplug_notify_cb() can be obtained from that device object's
> > > > > handler pointer and do not pass it as data to
> > > > > acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().
> > > > >
> > > > > That allows the second argument of acpi_install_hotplug_notify_handler()
> > > > > to be dropped, so do it and update its callers accordingly.
> > > > >
> > > > > Signed-off-by: Rafael J. Wysocki <[email protected]>
> > > > > ---
> > > > > drivers/acpi/scan.c | 31 ++++++++++++++++++-------------
> > > > > drivers/pci/hotplug/acpiphp_glue.c | 2 +-
> > > > > include/acpi/acpi_bus.h | 2 +-
> > > > > 3 files changed, 20 insertions(+), 15 deletions(-)
> > > > >
> > > > > Index: linux-pm/drivers/acpi/scan.c
> > > > > ===================================================================
> > > > > --- linux-pm.orig/drivers/acpi/scan.c
> > > > > +++ linux-pm/drivers/acpi/scan.c
> > > > > @@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da
> > > > >
> > > > > static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
> > > > > {
> > > > > - struct acpi_scan_handler *handler = data;
> > > > > u32 ost_code = ACPI_OST_SC_SUCCESS;
> > > > > struct acpi_device *adev;
> > > > > acpi_status status;
> > > > > @@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_
> > > > >
> > > > > case ACPI_NOTIFY_EJECT_REQUEST:
> > > > > acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
> > > > > - if (handler && !handler->hotplug.enabled) {
> > > > > - acpi_handle_err(handle, "Eject disabled\n");
> > > > > - 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);
> > > > > break;
> > > > >
> > > > > case ACPI_NOTIFY_DEVICE_WAKE:
> > > > > @@ -535,11 +527,24 @@ static void acpi_hotplug_notify_cb(acpi_
> > > > > goto out;
> > > > > }
> > > > >
> > > > > + mutex_lock(&acpi_scan_lock);
> > > >
> > > > This is weird. I'm testing against your test-next branch which has this
> > > > patch included. If I remove locking here, TBT hotplug works fine. Otherwise
> > > > I can see the first hotplug/unplug works but after that I cannot get any
> > > > events anymore.
> > >
> > > Weird indeed. I don't seem to be able to reproduce that on my Aspire S5.
> > >
> > > What system are you testing on?
> >
> > This is Intel NUC. Let me see if I can reproduce this on S5 with longer
> > chain of devices.
>
> OK, so on S5 this works even with longer chain. However, exactly the same
> kernel image on NUC fails after first hotplug/unplug cycle.

Well, we need to figure out what happens there.

Please add printks (1) before mutex_lock(), (2) before the if (type == ...)
instruction, (3) before the acpi_evaluate_hotplug_ost() under the if () and (4)
before the get_device() in acpi_notify_hotplug_cb() and check if they are all
printed (on NUC).

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-01-31 17:03:29

by Mika Westerberg

[permalink] [raw]
Subject: Re: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

On Fri, Jan 31, 2014 at 05:16:03PM +0100, Rafael J. Wysocki wrote:
> On Friday, January 31, 2014 06:01:57 PM Mika Westerberg wrote:
> > On Fri, Jan 31, 2014 at 05:40:11PM +0200, Mika Westerberg wrote:
> > > On Fri, Jan 31, 2014 at 04:42:21PM +0100, Rafael J. Wysocki wrote:
> > > > On Friday, January 31, 2014 05:28:36 PM Mika Westerberg wrote:
> > > > > On Wed, Jan 29, 2014 at 01:00:57AM +0100, Rafael J. Wysocki wrote:
> > > > > > From: Rafael J. Wysocki <[email protected]>
> > > > > >
> > > > > > Use the observation that the ACPI scan handler of the device object
> > > > > > in acpi_hotplug_notify_cb() can be obtained from that device object's
> > > > > > handler pointer and do not pass it as data to
> > > > > > acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().
> > > > > >
> > > > > > That allows the second argument of acpi_install_hotplug_notify_handler()
> > > > > > to be dropped, so do it and update its callers accordingly.
> > > > > >
> > > > > > Signed-off-by: Rafael J. Wysocki <[email protected]>
> > > > > > ---
> > > > > > drivers/acpi/scan.c | 31 ++++++++++++++++++-------------
> > > > > > drivers/pci/hotplug/acpiphp_glue.c | 2 +-
> > > > > > include/acpi/acpi_bus.h | 2 +-
> > > > > > 3 files changed, 20 insertions(+), 15 deletions(-)
> > > > > >
> > > > > > Index: linux-pm/drivers/acpi/scan.c
> > > > > > ===================================================================
> > > > > > --- linux-pm.orig/drivers/acpi/scan.c
> > > > > > +++ linux-pm/drivers/acpi/scan.c
> > > > > > @@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da
> > > > > >
> > > > > > static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
> > > > > > {
> > > > > > - struct acpi_scan_handler *handler = data;
> > > > > > u32 ost_code = ACPI_OST_SC_SUCCESS;
> > > > > > struct acpi_device *adev;
> > > > > > acpi_status status;
> > > > > > @@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_
> > > > > >
> > > > > > case ACPI_NOTIFY_EJECT_REQUEST:
> > > > > > acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
> > > > > > - if (handler && !handler->hotplug.enabled) {
> > > > > > - acpi_handle_err(handle, "Eject disabled\n");
> > > > > > - 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);
> > > > > > break;
> > > > > >
> > > > > > case ACPI_NOTIFY_DEVICE_WAKE:
> > > > > > @@ -535,11 +527,24 @@ static void acpi_hotplug_notify_cb(acpi_
> > > > > > goto out;
> > > > > > }
> > > > > >
> > > > > > + mutex_lock(&acpi_scan_lock);
> > > > >
> > > > > This is weird. I'm testing against your test-next branch which has this
> > > > > patch included. If I remove locking here, TBT hotplug works fine. Otherwise
> > > > > I can see the first hotplug/unplug works but after that I cannot get any
> > > > > events anymore.
> > > >
> > > > Weird indeed. I don't seem to be able to reproduce that on my Aspire S5.
> > > >
> > > > What system are you testing on?
> > >
> > > This is Intel NUC. Let me see if I can reproduce this on S5 with longer
> > > chain of devices.
> >
> > OK, so on S5 this works even with longer chain. However, exactly the same
> > kernel image on NUC fails after first hotplug/unplug cycle.
>
> Well, we need to figure out what happens there.
>
> Please add printks (1) before mutex_lock(), (2) before the if (type == ...)
> instruction, (3) before the acpi_evaluate_hotplug_ost() under the if () and (4)
> before the get_device() in acpi_notify_hotplug_cb() and check if they are all
> printed (on NUC).

Looks like something doesn't release that lock. I added printks what you
suggested and after first hotplug/unplug last messages I get are:

[ 64.914639] ACPI: \_SB_.PCI0.RP05: ACPI_NOTIFY_BUS_CHECK event
[ 64.914640] ACPI: \_SB_.PCI0.RP05: BEFORE mutex_lock()

Then running sysrq-w I get following task as blocked:

[ 346.885950] SysRq : Show Blocked State
[ 346.887733] task PC stack pid father
[ 346.889535] kworker/0:0 D ffff880100241ae0 5280 4 2 0x00000000
[ 346.891355] Workqueue: kacpi_notify acpi_os_execute_deferred
[ 346.893171] ffff8801003c7d30 0000000000000046 ffff880100241710 ffffffff81e10460
[ 346.895011] ffff8801003c7fd8 00000000000129c0 00000000000129c0 ffff880100241710
[ 346.896849] ffffffff81e5db70 ffffffff81e5db74 ffff880100241710 00000000ffffffff
[ 346.898702] Call Trace:
[ 346.900518] [<ffffffff81815bc4>] schedule_preempt_disabled+0x24/0x70
[ 346.902338] [<ffffffff818175f2>] __mutex_lock_slowpath+0x132/0x1b0
[ 346.904164] [<ffffffff8181768a>] mutex_lock+0x1a/0x2a
[ 346.905993] [<ffffffff81324845>] acpi_hotplug_notify_cb+0x70/0x227
[ 346.907820] [<ffffffff813384f4>] acpi_ev_notify_dispatch+0x44/0x5c
[ 346.909637] [<ffffffff8131fa8d>] acpi_os_execute_deferred+0xf/0x1b
[ 346.911455] [<ffffffff8105e12a>] process_one_work+0x17a/0x440
[ 346.913285] [<ffffffff8105ed29>] worker_thread+0x119/0x390
[ 346.915121] [<ffffffff8105ec10>] ? manage_workers.isra.25+0x2a0/0x2a0
[ 346.916970] [<ffffffff81064dad>] kthread+0xcd/0xf0
[ 346.918809] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
[ 346.920653] [<ffffffff8181fefc>] ret_from_fork+0x7c/0xb0
[ 346.922501] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
[ 346.924351] kworker/u8:5 D ffff88006ea55860 4696 945 2 0x00000000
[ 346.926212] Workqueue: kacpi_hotplug acpi_hotplug_work_fn
[ 346.928074] ffff88006de49a40 0000000000000046 ffff88006ea55490 ffff880100245490
[ 346.929978] ffff88006de49fd8 00000000000129c0 00000000000129c0 ffff88006ea55490
[ 346.931872] ffff88006de49ba0 7fffffffffffffff ffff88006de49b98 ffff88006ea55490
[ 346.933753] Call Trace:
[ 346.935626] [<ffffffff81815794>] schedule+0x24/0x70
[ 346.937516] [<ffffffff81814a89>] schedule_timeout+0x1a9/0x2a0
[ 346.939415] [<ffffffff8107dda8>] ? __wake_up_common+0x58/0x90
[ 346.941312] [<ffffffff81816968>] wait_for_completion+0x98/0x100
[ 346.943222] [<ffffffff81071b70>] ? wake_up_state+0x10/0x10
[ 346.945133] [<ffffffff8105ba25>] flush_workqueue+0x115/0x5a0
[ 346.947054] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
[ 346.948986] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
[ 346.950904] [<ffffffff81320340>] acpi_os_wait_events_complete+0x1c/0x1e
[ 346.952829] [<ffffffff8133a387>] acpi_remove_notify_handler+0x78/0x1ef
[ 346.954748] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
[ 346.956669] [<ffffffff813226df>] acpi_remove_pm_notifier+0x39/0x5c
[ 346.958592] [<ffffffff81309ab5>] pci_acpi_cleanup+0x25/0x50
[ 346.960508] [<ffffffff8132368c>] acpi_platform_notify_remove+0x44/0x53
[ 346.962427] [<ffffffff814426f2>] device_del+0x142/0x1b0
[ 346.964333] [<ffffffff812edcfa>] pci_remove_bus_device+0x7a/0x100
[ 346.966238] [<ffffffff812edd95>] pci_stop_and_remove_bus_device+0x15/0x20
[ 346.968140] [<ffffffff8130639e>] disable_slot+0x4e/0xa0
[ 346.970030] [<ffffffff813067b8>] acpiphp_check_bridge.part.10+0xe8/0xf0
[ 346.971911] [<ffffffff81306f92>] hotplug_event+0xf2/0x1a0
[ 346.973776] [<ffffffff813070a1>] acpiphp_hotplug_event+0x61/0xe0
[ 346.975630] [<ffffffff8132634e>] acpi_device_hotplug+0x37c/0x3c2
[ 346.977471] [<ffffffff81320359>] acpi_hotplug_work_fn+0x17/0x22
[ 346.979299] [<ffffffff8105e12a>] process_one_work+0x17a/0x440
[ 346.981122] [<ffffffff8105ed29>] worker_thread+0x119/0x390
[ 346.982956] [<ffffffff8105ec10>] ? manage_workers.isra.25+0x2a0/0x2a0
[ 346.984762] [<ffffffff81064dad>] kthread+0xcd/0xf0
[ 346.986581] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
[ 346.988419] [<ffffffff8181fefc>] ret_from_fork+0x7c/0xb0
[ 346.990221] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180

I have to leave now but I can continue debugging tomorrow if needed.

2014-01-31 17:20:02

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

On Friday, January 31, 2014 07:09:51 PM Mika Westerberg wrote:
> On Fri, Jan 31, 2014 at 05:16:03PM +0100, Rafael J. Wysocki wrote:
> > On Friday, January 31, 2014 06:01:57 PM Mika Westerberg wrote:
> > > On Fri, Jan 31, 2014 at 05:40:11PM +0200, Mika Westerberg wrote:
> > > > On Fri, Jan 31, 2014 at 04:42:21PM +0100, Rafael J. Wysocki wrote:
> > > > > On Friday, January 31, 2014 05:28:36 PM Mika Westerberg wrote:
> > > > > > On Wed, Jan 29, 2014 at 01:00:57AM +0100, Rafael J. Wysocki wrote:
> > > > > > > From: Rafael J. Wysocki <[email protected]>
> > > > > > >
> > > > > > > Use the observation that the ACPI scan handler of the device object
> > > > > > > in acpi_hotplug_notify_cb() can be obtained from that device object's
> > > > > > > handler pointer and do not pass it as data to
> > > > > > > acpi_install_hotplug_notify_handler() in acpi_scan_init_hotplug().
> > > > > > >
> > > > > > > That allows the second argument of acpi_install_hotplug_notify_handler()
> > > > > > > to be dropped, so do it and update its callers accordingly.
> > > > > > >
> > > > > > > Signed-off-by: Rafael J. Wysocki <[email protected]>
> > > > > > > ---
> > > > > > > drivers/acpi/scan.c | 31 ++++++++++++++++++-------------
> > > > > > > drivers/pci/hotplug/acpiphp_glue.c | 2 +-
> > > > > > > include/acpi/acpi_bus.h | 2 +-
> > > > > > > 3 files changed, 20 insertions(+), 15 deletions(-)
> > > > > > >
> > > > > > > Index: linux-pm/drivers/acpi/scan.c
> > > > > > > ===================================================================
> > > > > > > --- linux-pm.orig/drivers/acpi/scan.c
> > > > > > > +++ linux-pm/drivers/acpi/scan.c
> > > > > > > @@ -487,7 +487,6 @@ static void acpi_device_hotplug(void *da
> > > > > > >
> > > > > > > static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
> > > > > > > {
> > > > > > > - struct acpi_scan_handler *handler = data;
> > > > > > > u32 ost_code = ACPI_OST_SC_SUCCESS;
> > > > > > > struct acpi_device *adev;
> > > > > > > acpi_status status;
> > > > > > > @@ -503,13 +502,6 @@ static void acpi_hotplug_notify_cb(acpi_
> > > > > > >
> > > > > > > case ACPI_NOTIFY_EJECT_REQUEST:
> > > > > > > acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
> > > > > > > - if (handler && !handler->hotplug.enabled) {
> > > > > > > - acpi_handle_err(handle, "Eject disabled\n");
> > > > > > > - 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);
> > > > > > > break;
> > > > > > >
> > > > > > > case ACPI_NOTIFY_DEVICE_WAKE:
> > > > > > > @@ -535,11 +527,24 @@ static void acpi_hotplug_notify_cb(acpi_
> > > > > > > goto out;
> > > > > > > }
> > > > > > >
> > > > > > > + mutex_lock(&acpi_scan_lock);
> > > > > >
> > > > > > This is weird. I'm testing against your test-next branch which has this
> > > > > > patch included. If I remove locking here, TBT hotplug works fine. Otherwise
> > > > > > I can see the first hotplug/unplug works but after that I cannot get any
> > > > > > events anymore.
> > > > >
> > > > > Weird indeed. I don't seem to be able to reproduce that on my Aspire S5.
> > > > >
> > > > > What system are you testing on?
> > > >
> > > > This is Intel NUC. Let me see if I can reproduce this on S5 with longer
> > > > chain of devices.
> > >
> > > OK, so on S5 this works even with longer chain. However, exactly the same
> > > kernel image on NUC fails after first hotplug/unplug cycle.
> >
> > Well, we need to figure out what happens there.
> >
> > Please add printks (1) before mutex_lock(), (2) before the if (type == ...)
> > instruction, (3) before the acpi_evaluate_hotplug_ost() under the if () and (4)
> > before the get_device() in acpi_notify_hotplug_cb() and check if they are all
> > printed (on NUC).
>
> Looks like something doesn't release that lock. I added printks what you
> suggested and after first hotplug/unplug last messages I get are:
>
> [ 64.914639] ACPI: \_SB_.PCI0.RP05: ACPI_NOTIFY_BUS_CHECK event
> [ 64.914640] ACPI: \_SB_.PCI0.RP05: BEFORE mutex_lock()
>
> Then running sysrq-w I get following task as blocked:
>
> [ 346.885950] SysRq : Show Blocked State
> [ 346.887733] task PC stack pid father
> [ 346.889535] kworker/0:0 D ffff880100241ae0 5280 4 2 0x00000000
> [ 346.891355] Workqueue: kacpi_notify acpi_os_execute_deferred
> [ 346.893171] ffff8801003c7d30 0000000000000046 ffff880100241710 ffffffff81e10460
> [ 346.895011] ffff8801003c7fd8 00000000000129c0 00000000000129c0 ffff880100241710
> [ 346.896849] ffffffff81e5db70 ffffffff81e5db74 ffff880100241710 00000000ffffffff
> [ 346.898702] Call Trace:
> [ 346.900518] [<ffffffff81815bc4>] schedule_preempt_disabled+0x24/0x70
> [ 346.902338] [<ffffffff818175f2>] __mutex_lock_slowpath+0x132/0x1b0
> [ 346.904164] [<ffffffff8181768a>] mutex_lock+0x1a/0x2a
> [ 346.905993] [<ffffffff81324845>] acpi_hotplug_notify_cb+0x70/0x227
> [ 346.907820] [<ffffffff813384f4>] acpi_ev_notify_dispatch+0x44/0x5c
> [ 346.909637] [<ffffffff8131fa8d>] acpi_os_execute_deferred+0xf/0x1b
> [ 346.911455] [<ffffffff8105e12a>] process_one_work+0x17a/0x440
> [ 346.913285] [<ffffffff8105ed29>] worker_thread+0x119/0x390
> [ 346.915121] [<ffffffff8105ec10>] ? manage_workers.isra.25+0x2a0/0x2a0
> [ 346.916970] [<ffffffff81064dad>] kthread+0xcd/0xf0
> [ 346.918809] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
> [ 346.920653] [<ffffffff8181fefc>] ret_from_fork+0x7c/0xb0
> [ 346.922501] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
> [ 346.924351] kworker/u8:5 D ffff88006ea55860 4696 945 2 0x00000000
> [ 346.926212] Workqueue: kacpi_hotplug acpi_hotplug_work_fn
> [ 346.928074] ffff88006de49a40 0000000000000046 ffff88006ea55490 ffff880100245490
> [ 346.929978] ffff88006de49fd8 00000000000129c0 00000000000129c0 ffff88006ea55490
> [ 346.931872] ffff88006de49ba0 7fffffffffffffff ffff88006de49b98 ffff88006ea55490
> [ 346.933753] Call Trace:
> [ 346.935626] [<ffffffff81815794>] schedule+0x24/0x70
> [ 346.937516] [<ffffffff81814a89>] schedule_timeout+0x1a9/0x2a0
> [ 346.939415] [<ffffffff8107dda8>] ? __wake_up_common+0x58/0x90
> [ 346.941312] [<ffffffff81816968>] wait_for_completion+0x98/0x100
> [ 346.943222] [<ffffffff81071b70>] ? wake_up_state+0x10/0x10
> [ 346.945133] [<ffffffff8105ba25>] flush_workqueue+0x115/0x5a0
> [ 346.947054] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
> [ 346.948986] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
> [ 346.950904] [<ffffffff81320340>] acpi_os_wait_events_complete+0x1c/0x1e
> [ 346.952829] [<ffffffff8133a387>] acpi_remove_notify_handler+0x78/0x1ef
> [ 346.954748] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
> [ 346.956669] [<ffffffff813226df>] acpi_remove_pm_notifier+0x39/0x5c
> [ 346.958592] [<ffffffff81309ab5>] pci_acpi_cleanup+0x25/0x50
> [ 346.960508] [<ffffffff8132368c>] acpi_platform_notify_remove+0x44/0x53
> [ 346.962427] [<ffffffff814426f2>] device_del+0x142/0x1b0
> [ 346.964333] [<ffffffff812edcfa>] pci_remove_bus_device+0x7a/0x100
> [ 346.966238] [<ffffffff812edd95>] pci_stop_and_remove_bus_device+0x15/0x20
> [ 346.968140] [<ffffffff8130639e>] disable_slot+0x4e/0xa0
> [ 346.970030] [<ffffffff813067b8>] acpiphp_check_bridge.part.10+0xe8/0xf0
> [ 346.971911] [<ffffffff81306f92>] hotplug_event+0xf2/0x1a0
> [ 346.973776] [<ffffffff813070a1>] acpiphp_hotplug_event+0x61/0xe0
> [ 346.975630] [<ffffffff8132634e>] acpi_device_hotplug+0x37c/0x3c2
> [ 346.977471] [<ffffffff81320359>] acpi_hotplug_work_fn+0x17/0x22
> [ 346.979299] [<ffffffff8105e12a>] process_one_work+0x17a/0x440
> [ 346.981122] [<ffffffff8105ed29>] worker_thread+0x119/0x390
> [ 346.982956] [<ffffffff8105ec10>] ? manage_workers.isra.25+0x2a0/0x2a0
> [ 346.984762] [<ffffffff81064dad>] kthread+0xcd/0xf0
> [ 346.986581] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
> [ 346.988419] [<ffffffff8181fefc>] ret_from_fork+0x7c/0xb0
> [ 346.990221] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
>
> I have to leave now but I can continue debugging tomorrow if needed.

No need for now, I think I know what's happening. I'll follow up later.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-01 23:57:38

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 4/5][RFT] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

On Friday, January 31, 2014 06:34:22 PM Rafael J. Wysocki wrote:
> On Friday, January 31, 2014 07:09:51 PM Mika Westerberg wrote:
> > On Fri, Jan 31, 2014 at 05:16:03PM +0100, Rafael J. Wysocki wrote:

[...]

> >
> > [ 64.914639] ACPI: \_SB_.PCI0.RP05: ACPI_NOTIFY_BUS_CHECK event
> > [ 64.914640] ACPI: \_SB_.PCI0.RP05: BEFORE mutex_lock()
> >
> > Then running sysrq-w I get following task as blocked:
> >
> > [ 346.885950] SysRq : Show Blocked State
> > [ 346.887733] task PC stack pid father
> > [ 346.889535] kworker/0:0 D ffff880100241ae0 5280 4 2 0x00000000
> > [ 346.891355] Workqueue: kacpi_notify acpi_os_execute_deferred
> > [ 346.893171] ffff8801003c7d30 0000000000000046 ffff880100241710 ffffffff81e10460
> > [ 346.895011] ffff8801003c7fd8 00000000000129c0 00000000000129c0 ffff880100241710
> > [ 346.896849] ffffffff81e5db70 ffffffff81e5db74 ffff880100241710 00000000ffffffff
> > [ 346.898702] Call Trace:
> > [ 346.900518] [<ffffffff81815bc4>] schedule_preempt_disabled+0x24/0x70
> > [ 346.902338] [<ffffffff818175f2>] __mutex_lock_slowpath+0x132/0x1b0
> > [ 346.904164] [<ffffffff8181768a>] mutex_lock+0x1a/0x2a
> > [ 346.905993] [<ffffffff81324845>] acpi_hotplug_notify_cb+0x70/0x227
> > [ 346.907820] [<ffffffff813384f4>] acpi_ev_notify_dispatch+0x44/0x5c
> > [ 346.909637] [<ffffffff8131fa8d>] acpi_os_execute_deferred+0xf/0x1b
> > [ 346.911455] [<ffffffff8105e12a>] process_one_work+0x17a/0x440
> > [ 346.913285] [<ffffffff8105ed29>] worker_thread+0x119/0x390
> > [ 346.915121] [<ffffffff8105ec10>] ? manage_workers.isra.25+0x2a0/0x2a0
> > [ 346.916970] [<ffffffff81064dad>] kthread+0xcd/0xf0
> > [ 346.918809] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
> > [ 346.920653] [<ffffffff8181fefc>] ret_from_fork+0x7c/0xb0
> > [ 346.922501] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
> > [ 346.924351] kworker/u8:5 D ffff88006ea55860 4696 945 2 0x00000000
> > [ 346.926212] Workqueue: kacpi_hotplug acpi_hotplug_work_fn
> > [ 346.928074] ffff88006de49a40 0000000000000046 ffff88006ea55490 ffff880100245490
> > [ 346.929978] ffff88006de49fd8 00000000000129c0 00000000000129c0 ffff88006ea55490
> > [ 346.931872] ffff88006de49ba0 7fffffffffffffff ffff88006de49b98 ffff88006ea55490
> > [ 346.933753] Call Trace:
> > [ 346.935626] [<ffffffff81815794>] schedule+0x24/0x70
> > [ 346.937516] [<ffffffff81814a89>] schedule_timeout+0x1a9/0x2a0
> > [ 346.939415] [<ffffffff8107dda8>] ? __wake_up_common+0x58/0x90
> > [ 346.941312] [<ffffffff81816968>] wait_for_completion+0x98/0x100
> > [ 346.943222] [<ffffffff81071b70>] ? wake_up_state+0x10/0x10
> > [ 346.945133] [<ffffffff8105ba25>] flush_workqueue+0x115/0x5a0
> > [ 346.947054] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
> > [ 346.948986] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
> > [ 346.950904] [<ffffffff81320340>] acpi_os_wait_events_complete+0x1c/0x1e
> > [ 346.952829] [<ffffffff8133a387>] acpi_remove_notify_handler+0x78/0x1ef
> > [ 346.954748] [<ffffffff81309b20>] ? acpi_pci_find_companion+0x40/0x40
> > [ 346.956669] [<ffffffff813226df>] acpi_remove_pm_notifier+0x39/0x5c
> > [ 346.958592] [<ffffffff81309ab5>] pci_acpi_cleanup+0x25/0x50
> > [ 346.960508] [<ffffffff8132368c>] acpi_platform_notify_remove+0x44/0x53
> > [ 346.962427] [<ffffffff814426f2>] device_del+0x142/0x1b0
> > [ 346.964333] [<ffffffff812edcfa>] pci_remove_bus_device+0x7a/0x100
> > [ 346.966238] [<ffffffff812edd95>] pci_stop_and_remove_bus_device+0x15/0x20
> > [ 346.968140] [<ffffffff8130639e>] disable_slot+0x4e/0xa0
> > [ 346.970030] [<ffffffff813067b8>] acpiphp_check_bridge.part.10+0xe8/0xf0
> > [ 346.971911] [<ffffffff81306f92>] hotplug_event+0xf2/0x1a0
> > [ 346.973776] [<ffffffff813070a1>] acpiphp_hotplug_event+0x61/0xe0
> > [ 346.975630] [<ffffffff8132634e>] acpi_device_hotplug+0x37c/0x3c2
> > [ 346.977471] [<ffffffff81320359>] acpi_hotplug_work_fn+0x17/0x22
> > [ 346.979299] [<ffffffff8105e12a>] process_one_work+0x17a/0x440
> > [ 346.981122] [<ffffffff8105ed29>] worker_thread+0x119/0x390
> > [ 346.982956] [<ffffffff8105ec10>] ? manage_workers.isra.25+0x2a0/0x2a0
> > [ 346.984762] [<ffffffff81064dad>] kthread+0xcd/0xf0
> > [ 346.986581] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
> > [ 346.988419] [<ffffffff8181fefc>] ret_from_fork+0x7c/0xb0
> > [ 346.990221] [<ffffffff81064ce0>] ? kthread_create_on_node+0x180/0x180
> >
> > I have to leave now but I can continue debugging tomorrow if needed.
>
> No need for now, I think I know what's happening. I'll follow up later.

acpi_remove_notify_handler() which is called from acpi_remove_pm_notifier()
executes acpi_os_wait_events_complete() which does flush_workqueue(kacpi_notify_wq)
and all that happens under acpi_scan_lock acquired by acpi_device_hotplug().

Now, acpi_hotplug_notify_cb() runs from kacpi_notify_wq, so effectively
acpi_os_wait_events_complete() waits for it to return, so if it attempts
to acquire acpi_scan_lock, it will deadlock. Which happens on the NUC.

The good news is that we can use a different lock to eliminate the race
I wanted to deal with using acpi_scan_lock, but that required me to rework
the whole patchset. I'll send a new version shortly, but I need to update
the ACPIPHP updates it is on top of.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-02 00:18:25

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 3/13] ACPI / hotplug / PCI: Simplify disable_slot()

From: Rafael J. Wysocki <[email protected]>

After recent PCI core changes related to the rescan/remove locking,
the ACPIPHP's disable_slot() function is only called under the
general PCI rescan/remove lock, so it doesn't have to use
dev_in_slot() any more to avoid race conditions. Make it simply
walk the devices on the bus and drop the ones in the slot being
disabled and drop dev_in_slot() which has no more users.

Moreover, to avoid problems described in the changelog of commit
29ed1f29b68a (PCI: pciehp: Fix null pointer deref when hot-removing
SR-IOV device), make disable_slot() carry out the list walk in
reverse order.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 28 +++++-----------------------
1 file changed, 5 insertions(+), 23 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -604,32 +604,15 @@ static void __ref enable_slot(struct acp
}
}

-/* return first device in slot, acquiring a reference on it */
-static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
-{
- struct pci_bus *bus = slot->bus;
- struct pci_dev *dev;
- struct pci_dev *ret = NULL;
-
- down_read(&pci_bus_sem);
- list_for_each_entry(dev, &bus->devices, bus_list)
- if (PCI_SLOT(dev->devfn) == slot->device) {
- ret = pci_dev_get(dev);
- break;
- }
- up_read(&pci_bus_sem);
-
- return ret;
-}
-
/**
* disable_slot - disable a slot
* @slot: ACPI PHP slot
*/
static void disable_slot(struct acpiphp_slot *slot)
{
+ struct pci_bus *bus = slot->bus;
+ struct pci_dev *dev, *prev;
struct acpiphp_func *func;
- struct pci_dev *pdev;

/*
* enable_slot() enumerates all functions in this device via
@@ -637,10 +620,9 @@ static void disable_slot(struct acpiphp_
* methods (_EJ0, etc.) or not. Therefore, we remove all functions
* here.
*/
- while ((pdev = dev_in_slot(slot))) {
- pci_stop_and_remove_bus_device(pdev);
- pci_dev_put(pdev);
- }
+ list_for_each_entry_safe_reverse(dev, prev, &bus->devices, bus_list)
+ if (PCI_SLOT(dev->devfn) == slot->device)
+ pci_stop_and_remove_bus_device(dev);

list_for_each_entry(func, &slot->funcs, sibling)
acpiphp_bus_trim(func_to_handle(func));

2014-02-02 00:18:30

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 2/13] ACPI / hotplug / PCI: Move PCI rescan-remove locking to hotplug_event()

From: Rafael J. Wysocki <[email protected]>

Commit 9217a984671e (ACPI / hotplug / PCI: Use global PCI rescan-remove
locking) modified ACPIPHP to protect its PCI device removal and addition
code paths from races against sysfs-driven rescan and remove operations
with the help of PCI rescan-remove locking. However, it overlooked the
fact that hotplug_event_work() is not the only caller of hotplug_event()
which may also be called by dock_hotplug_event() and that code path
is missing the PCI rescan-remove locking. This means that, although
the PCI rescan-remove lock is held as appropriate during the handling
of events originating from handle_hotplug_event(), the ACPIPHP's
operations resulting from dock events may still suffer the race
conditions that commit 9217a984671e was supposed to eliminate.

To address that problem, move the PCI rescan-remove locking from
hotplug_event_work() to hotplug_event() so that it is used regardless
of the way that function is invoked.

Revamps: 9217a984671e (ACPI / hotplug / PCI: Use global PCI rescan-remove locking)
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -852,6 +852,7 @@ static void hotplug_event(acpi_handle ha

mutex_unlock(&acpiphp_context_lock);

+ pci_lock_rescan_remove();
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);

switch (type) {
@@ -905,6 +906,7 @@ static void hotplug_event(acpi_handle ha
break;
}

+ pci_unlock_rescan_remove();
if (bridge)
put_bridge(bridge);
}
@@ -915,11 +917,9 @@ static void hotplug_event_work(void *dat
acpi_handle handle = context->handle;

acpi_scan_lock_acquire();
- pci_lock_rescan_remove();

hotplug_event(handle, type, context);

- pci_unlock_rescan_remove();
acpi_scan_lock_release();
acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);

2014-02-02 00:18:52

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 0/13] ACPI / hotplug / PCI: Updates on top of changes merged recently

On Monday, January 27, 2014 01:37:17 AM Rafael J. Wysocki wrote:
> Hi All,
>
> ACPIPHP can be simplified a bit on top of some PCI and ACPI changes merged
> recently and the following series of patches implements those simplifications:
>
> [1/11] Fix up two kerneldoc comments in acpiphp_glue.c.
> [2/11] Get rid of an unnecessary label in register_slot().
> [3/11] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
> [4/11] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
> [5/11] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
> [6/11] Drop acpiphp_bus_add() (which has only one user).
> [7/11] Drop crit_sect mutexes (that are redundant).
> [8/11] Clean up the usage of the slot variable in hotplug_event().
> [9/11] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
> [10/11] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
> [11/11] Drop handle argument from the member functions of struct acpi_dock_ops.
>
> All of that is relateively straightforward, but I have some more intrusive changes
> on top of it in the works. They will be posted separately later this week.

I've learned a couple of things since I sent this patchset. First, all
bus->devices list walks that may remove PCI devices should be done in reverse
order or they can crash if virtual functions are involved. Second, hotplug_event()
(in acpiphp_glue.c) has to acquire pci_rescan_remove_lock by itself, because it
may be called from multiple places and all of them need that lock to be held.
That is done by patches [1-2/13] which I'm planning to push as fixes for 3.14-rc2.

The rest is pretty much the same as last time except that the old patch [9/11]
became [3/13] in this series and it has been changed so that the list is walked in
reverse order.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-02 00:18:24

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 4/13] ACPI / hotplug / PCI: Proper kerneldoc comments for enumeration/removal

From: Rafael J. Wysocki <[email protected]>

Add proper kerneldoc comments describing acpiphp_enumerate_slots()
and acpiphp_remove_slots().

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -968,9 +968,12 @@ static void handle_hotplug_event(acpi_ha
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-/*
- * Create hotplug slots for the PCI bus.
- * It should always return 0 to avoid skipping following notifiers.
+/**
+ * acpiphp_enumerate_slots - Enumerate PCI slots for a given bus.
+ * @bus: PCI bus to enumerate the slots for.
+ *
+ * A "slot" is an object associated with a PCI device number. All functions
+ * (PCI devices) with the same bus and device number belong to the same slot.
*/
void acpiphp_enumerate_slots(struct pci_bus *bus)
{
@@ -1043,7 +1046,10 @@ void acpiphp_enumerate_slots(struct pci_
}
}

-/* Destroy hotplug slots associated with the PCI bus */
+/**
+ * acpiphp_remove_slots - Remove slot objects associated with a given bus.
+ * @bus: PCI bus to remove the slot objects for.
+ */
void acpiphp_remove_slots(struct pci_bus *bus)
{
struct acpiphp_bridge *bridge;

2014-02-02 00:19:22

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 1/13] ACPI / hotplug / PCI: Remove entries from bus->devices in reverse order

From: Rafael J. Wysocki <[email protected]>

According to the changelog of commit 29ed1f29b68a (PCI: pciehp: Fix null
pointer deref when hot-removing SR-IOV device) it is unsafe to walk the
bus->devices list of a PCI bus and remove devices from it in direct order,
because that may lead to NULL pointer dereferences related to virtual
functions.

For this reason, change all of the bus->devices list walks in
acpiphp_glue.c during which devices may be removed to be carried out in
reverse order.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -742,7 +742,7 @@ static void trim_stale_devices(struct pc

/* The device is a bridge. so check the bus below it. */
pm_runtime_get_sync(&dev->dev);
- list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
+ list_for_each_entry_safe_reverse(child, tmp, &bus->devices, bus_list)
trim_stale_devices(child);

pm_runtime_put(&dev->dev);
@@ -773,8 +773,8 @@ static void acpiphp_check_bridge(struct
; /* do nothing */
} else if (get_slot_status(slot) == ACPI_STA_ALL) {
/* remove stale devices if any */
- list_for_each_entry_safe(dev, tmp, &bus->devices,
- bus_list)
+ list_for_each_entry_safe_reverse(dev, tmp,
+ &bus->devices, bus_list)
if (PCI_SLOT(dev->devfn) == slot->device)
trim_stale_devices(dev);

@@ -805,7 +805,7 @@ static void acpiphp_sanitize_bus(struct
int i;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM;

- list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
+ list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) {
for (i=0; i<PCI_BRIDGE_RESOURCES; i++) {
struct resource *res = &dev->resource[i];
if ((res->flags & type_mask) && !res->start &&

2014-02-02 00:18:21

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 12/13] ACPI / hotplug / PCI: Use acpi_handle_debug() in hotplug_event()

From: Rafael J. Wysocki <[email protected]>

Make hotplug_event() use acpi_handle_debug() instead of an open-coded
debug message printing and clean up the messages printed by it.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -794,9 +794,6 @@ static void hotplug_event(acpi_handle ha
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
- char objname[64];
- struct acpi_buffer buffer = { .length = sizeof(objname),
- .pointer = objname };

mutex_lock(&acpiphp_context_lock);
bridge = context->bridge;
@@ -806,14 +803,11 @@ static void hotplug_event(acpi_handle ha
mutex_unlock(&acpiphp_context_lock);

pci_lock_rescan_remove();
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);

switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/* bus re-enumerate */
- pr_debug("%s: Bus check notify on %s\n", __func__, objname);
- pr_debug("%s: re-enumerating slots under %s\n",
- __func__, objname);
+ acpi_handle_debug(handle, "Bus check in %s()\n", __func__);
if (bridge)
acpiphp_check_bridge(bridge);
else if (!(slot->flags & SLOT_IS_GOING_AWAY))
@@ -823,7 +817,7 @@ static void hotplug_event(acpi_handle ha

case ACPI_NOTIFY_DEVICE_CHECK:
/* device check */
- pr_debug("%s: Device check notify on %s\n", __func__, objname);
+ acpi_handle_debug(handle, "Device check in %s()\n", __func__);
if (bridge) {
acpiphp_check_bridge(bridge);
} else if (!(slot->flags & SLOT_IS_GOING_AWAY)) {
@@ -838,7 +832,7 @@ static void hotplug_event(acpi_handle ha

case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
- pr_debug("%s: Device eject notify on %s\n", __func__, objname);
+ acpi_handle_debug(handle, "Eject request in %s()\n", __func__);
acpiphp_disable_and_eject_slot(slot);
break;
}

2014-02-02 00:19:50

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 5/13] ACPI / hotplug / PCI: Simplify register_slot()

From: Rafael J. Wysocki <[email protected]>

The err label in register_slot() is only jumped to from one place,
so move the code under the label to that place and drop the label.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -316,8 +316,10 @@ static acpi_status register_slot(acpi_ha

slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
- status = AE_NO_MEMORY;
- goto err;
+ mutex_lock(&acpiphp_context_lock);
+ acpiphp_put_context(context);
+ mutex_unlock(&acpiphp_context_lock);
+ return AE_NO_MEMORY;
}

slot->bus = bridge->pci_bus;
@@ -385,12 +387,6 @@ static acpi_status register_slot(acpi_ha
}

return AE_OK;
-
- err:
- mutex_lock(&acpiphp_context_lock);
- acpiphp_put_context(context);
- mutex_unlock(&acpiphp_context_lock);
- return status;
}

static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)

2014-02-02 00:20:16

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 6/13] ACPI / hotplug / PCI: Drop acpiphp_bus_trim()

From: Rafael J. Wysocki <[email protected]>

If trim_stale_devices() calls acpi_bus_trim() directly, we can
save a potentially costly acpi_bus_get_device() invocation. After
making that change acpiphp_bus_trim() would only be called from one
place, so move the code from it to that place and drop it.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 30 +++++++++++-------------------
1 file changed, 11 insertions(+), 19 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -468,19 +468,6 @@ static unsigned char acpiphp_max_busnr(s
}

/**
- * acpiphp_bus_trim - Trim device objects in an ACPI namespace subtree.
- * @handle: ACPI device object handle to start from.
- */
-static void acpiphp_bus_trim(acpi_handle handle)
-{
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(handle, &adev);
- if (adev)
- acpi_bus_trim(adev);
-}
-
-/**
* acpiphp_bus_add - Scan ACPI namespace subtree.
* @handle: ACPI object handle to start the scan from.
*/
@@ -620,8 +607,12 @@ static void disable_slot(struct acpiphp_
if (PCI_SLOT(dev->devfn) == slot->device)
pci_stop_and_remove_bus_device(dev);

- list_for_each_entry(func, &slot->funcs, sibling)
- acpiphp_bus_trim(func_to_handle(func));
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev;
+
+ if (!acpi_bus_get_device(func_to_handle(func), &adev))
+ acpi_bus_trim(adev);
+ }

slot->flags &= (~SLOT_ENABLED);
}
@@ -693,11 +684,12 @@ static unsigned int get_slot_status(stru
*/
static void trim_stale_devices(struct pci_dev *dev)
{
- acpi_handle handle = ACPI_HANDLE(&dev->dev);
+ struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
struct pci_bus *bus = dev->subordinate;
bool alive = false;

- if (handle) {
+ if (adev) {
+ acpi_handle handle = adev->handle;
acpi_status status;
unsigned long long sta;

@@ -713,8 +705,8 @@ static void trim_stale_devices(struct pc
}
if (!alive) {
pci_stop_and_remove_bus_device(dev);
- if (handle)
- acpiphp_bus_trim(handle);
+ if (adev)
+ acpi_bus_trim(adev);
} else if (bus) {
struct pci_dev *child, *tmp;

2014-02-02 00:20:56

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 8/13] ACPI / hotplug / PCI: Store acpi_device pointer in acpiphp_context

From: Rafael J. Wysocki <[email protected]>

After recent modifications of the ACPI core making it create a struct
acpi_device object for every namespace node representing a device
regardless of the current status of that device the ACPIPHP code
can store a struct acpi_device pointer instead of an ACPI handle
in struct acpiphp_context. This immediately makes it possible to
avoid making potentially costly calls to acpi_bus_get_device() in
two places and allows some more simplifications to be made going
forward.

The reason why that is correct is because ACPIPHP only installs
hotify handlers for namespace nodes that exist when
acpiphp_enumerate_slots() is called for their parent bridge.
That only happens if the parent bridge has an ACPI companion
associated with it, which means that the ACPI namespace scope
in question has been scanned already at that point. That, in
turn, means that struct acpi_device objects have been created
for all namespace nodes in that scope and pointers to those
objects can be stored directly instead of their ACPI handles.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp.h | 9 +++++--
drivers/pci/hotplug/acpiphp_glue.c | 44 +++++++++++++++++--------------------
2 files changed, 28 insertions(+), 25 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -117,8 +117,8 @@ struct acpiphp_func {
};

struct acpiphp_context {
- acpi_handle handle;
struct acpiphp_func func;
+ struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};
@@ -128,9 +128,14 @@ static inline struct acpiphp_context *fu
return container_of(func, struct acpiphp_context, func);
}

+static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
+{
+ return func_to_context(func)->adev;
+}
+
static inline acpi_handle func_to_handle(struct acpiphp_func *func)
{
- return func_to_context(func)->handle;
+ return func_to_acpi_device(func)->handle;
}

/*
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -73,11 +73,11 @@ static void acpiphp_context_handler(acpi

/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
- * @handle: ACPI object handle to create the context for.
+ * @adev: ACPI device object to create the context for.
*
* Call under acpiphp_context_lock.
*/
-static struct acpiphp_context *acpiphp_init_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
acpi_status status;
@@ -86,9 +86,9 @@ static struct acpiphp_context *acpiphp_i
if (!context)
return NULL;

- context->handle = handle;
+ context->adev = adev;
context->refcount = 1;
- status = acpi_attach_data(handle, acpiphp_context_handler, context);
+ status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
if (ACPI_FAILURE(status)) {
kfree(context);
return NULL;
@@ -118,7 +118,7 @@ static struct acpiphp_context *acpiphp_g

/**
* acpiphp_put_context - Drop a reference to ACPI hotplug context.
- * @handle: ACPI object handle to put the context for.
+ * @context: ACPI hotplug context to drop a reference to.
*
* The context object is removed if there are no more references to it.
*
@@ -130,7 +130,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->handle, acpiphp_context_handler);
+ acpi_detach_data(context->adev->handle, acpiphp_context_handler);
kfree(context);
}

@@ -265,6 +265,7 @@ static acpi_status register_slot(acpi_ha
{
struct acpiphp_bridge *bridge = data;
struct acpiphp_context *context;
+ struct acpi_device *adev;
struct acpiphp_slot *slot;
struct acpiphp_func *newfunc;
acpi_status status = AE_OK;
@@ -284,12 +285,14 @@ static acpi_status register_slot(acpi_ha
"can't evaluate _ADR (%#x)\n", status);
return AE_OK;
}
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;

device = (adr >> 16) & 0xffff;
function = adr & 0xffff;

mutex_lock(&acpiphp_context_lock);
- context = acpiphp_init_context(handle);
+ context = acpiphp_init_context(adev);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
acpi_handle_err(handle, "No hotplug context\n");
@@ -607,12 +610,8 @@ static void disable_slot(struct acpiphp_
if (PCI_SLOT(dev->devfn) == slot->device)
pci_stop_and_remove_bus_device(dev);

- list_for_each_entry(func, &slot->funcs, sibling) {
- struct acpi_device *adev;
-
- if (!acpi_bus_get_device(func_to_handle(func), &adev))
- acpi_bus_trim(adev);
- }
+ list_for_each_entry(func, &slot->funcs, sibling)
+ acpi_bus_trim(func_to_acpi_device(func));

slot->flags &= (~SLOT_ENABLED);
}
@@ -626,13 +625,10 @@ static bool slot_no_hotplug(struct acpip
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling) {
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(func_to_handle(func), &adev);
- if (acpiphp_no_hotplug(adev))
+ list_for_each_entry(func, &slot->funcs, sibling)
+ if (acpiphp_no_hotplug(func_to_acpi_device(func)))
return true;
- }
+
return false;
}

@@ -883,7 +879,7 @@ static void hotplug_event(acpi_handle ha
static void hotplug_event_work(void *data, u32 type)
{
struct acpiphp_context *context = data;
- acpi_handle handle = context->handle;
+ acpi_handle handle = context->adev->handle;

acpi_scan_lock_acquire();

@@ -941,7 +937,7 @@ static void handle_hotplug_event(acpi_ha

mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
- if (context && !WARN_ON(context->handle != handle)) {
+ if (context && !WARN_ON(context->adev->handle != handle)) {
get_bridge(context->func.parent);
acpiphp_put_context(context);
acpi_hotplug_execute(hotplug_event_work, context, type);
@@ -965,16 +961,18 @@ static void handle_hotplug_event(acpi_ha
void acpiphp_enumerate_slots(struct pci_bus *bus)
{
struct acpiphp_bridge *bridge;
+ struct acpi_device *adev;
acpi_handle handle;
acpi_status status;

if (acpiphp_disabled)
return;

- handle = ACPI_HANDLE(bus->bridge);
- if (!handle)
+ adev = ACPI_COMPANION(bus->bridge);
+ if (!adev)
return;

+ handle = adev->handle;
bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
if (!bridge) {
acpi_handle_err(handle, "No memory for bridge object\n");

2014-02-02 00:20:55

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 7/13] ACPI / hotplug / PCI: Rework acpiphp_no_hotplug()

From: Rafael J. Wysocki <[email protected]>

If a struct acpi_device pointer is passed to acpiphp_no_hotplug()
instead of an ACPI handle, the function won't need to call
acpi_bus_get_device(), which may be costly, any more. Then,
trim_stale_devices() can call acpiphp_no_hotplug() passing
the struct acpi_device object it already has directly to that
function.

Make those changes and update slot_no_hotplug() accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -617,11 +617,8 @@ static void disable_slot(struct acpiphp_
slot->flags &= (~SLOT_ENABLED);
}

-static bool acpiphp_no_hotplug(acpi_handle handle)
+static bool acpiphp_no_hotplug(struct acpi_device *adev)
{
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(handle, &adev);
return adev && adev->flags.no_hotplug;
}

@@ -629,10 +626,13 @@ static bool slot_no_hotplug(struct acpip
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling)
- if (acpiphp_no_hotplug(func_to_handle(func)))
- return true;
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev = NULL;

+ acpi_bus_get_device(func_to_handle(func), &adev);
+ if (acpiphp_no_hotplug(adev))
+ return true;
+ }
return false;
}

@@ -689,13 +689,12 @@ static void trim_stale_devices(struct pc
bool alive = false;

if (adev) {
- acpi_handle handle = adev->handle;
acpi_status status;
unsigned long long sta;

- status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
alive = (ACPI_SUCCESS(status) && sta == ACPI_STA_ALL)
- || acpiphp_no_hotplug(handle);
+ || acpiphp_no_hotplug(adev);
}
if (!alive) {
u32 v;

2014-02-02 00:18:19

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 13/13] ACPI / hotplug: Do not pass ACPI handles to ACPI dock operations

From: Rafael J. Wysocki <[email protected]>

None of the existing users of struct acpi_dock_ops actually needs the
first argument of its member functions, so redefine those functions
to take only two arguments, the event type and data pointer, and
update the users of struct acpi_dock_ops accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/dock.c | 4 ++--
drivers/ata/libata-acpi.c | 8 ++++----
drivers/pci/hotplug/acpiphp_glue.c | 13 +++++++------
include/acpi/acpi_drivers.h | 6 +++---
4 files changed, 16 insertions(+), 15 deletions(-)

Index: linux-pm/include/acpi/acpi_drivers.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_drivers.h
+++ linux-pm/include/acpi/acpi_drivers.h
@@ -110,9 +110,9 @@ void pci_acpi_crs_quirks(void);
Dock Station
-------------------------------------------------------------------------- */
struct acpi_dock_ops {
- acpi_notify_handler fixup;
- acpi_notify_handler handler;
- acpi_notify_handler uevent;
+ void (*handler)(u32 event_type, void *data);
+ void (*fixup)(u32 event_type, void *data);
+ void (*uevent)(u32 event_type, void *data);
};

#ifdef CONFIG_ACPI_DOCK
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -63,7 +63,7 @@ static DEFINE_MUTEX(acpiphp_context_lock
static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
-static void hotplug_event(acpi_handle handle, u32 type, void *data);
+static void hotplug_event(u32 type, void *data);
static void free_bridge(struct kref *kref);

static void acpiphp_context_handler(acpi_handle handle, void *context)
@@ -185,7 +185,7 @@ static void free_bridge(struct kref *kre
* TBD - figure out a way to only call fixups for
* systems that require them.
*/
-static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
+static void post_dock_fixups(u32 event, void *data)
{
struct acpiphp_context *context = data;
struct pci_bus *bus = context->func.slot->bus;
@@ -788,11 +788,12 @@ void acpiphp_check_host_bridge(acpi_hand

static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);

-static void hotplug_event(acpi_handle handle, u32 type, void *data)
+static void hotplug_event(u32 type, void *data)
{
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
+ acpi_handle handle = context->adev->handle;
struct acpiphp_bridge *bridge;

mutex_lock(&acpiphp_context_lock);
@@ -845,14 +846,14 @@ static void hotplug_event(acpi_handle ha
static void hotplug_event_work(void *data, u32 type)
{
struct acpiphp_context *context = data;
- acpi_handle handle = context->adev->handle;

acpi_scan_lock_acquire();

- hotplug_event(handle, type, context);
+ hotplug_event(type, context);

acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);
+ acpi_evaluate_hotplug_ost(context->adev->handle, type,
+ ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);
}

Index: linux-pm/drivers/acpi/dock.c
===================================================================
--- linux-pm.orig/drivers/acpi/dock.c
+++ linux-pm/drivers/acpi/dock.c
@@ -185,7 +185,7 @@ static void dock_release_hotplug(struct
static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
enum dock_callback_type cb_type)
{
- acpi_notify_handler cb = NULL;
+ void (*cb)(u32, void *) = NULL;
bool run = false;

mutex_lock(&hotplug_lock);
@@ -213,7 +213,7 @@ static void dock_hotplug_event(struct do
return;

if (cb)
- cb(dd->handle, event, dd->hp_context);
+ cb(event, dd->hp_context);

dock_release_hotplug(dd);
}
Index: linux-pm/drivers/ata/libata-acpi.c
===================================================================
--- linux-pm.orig/drivers/ata/libata-acpi.c
+++ linux-pm/drivers/ata/libata-acpi.c
@@ -121,14 +121,14 @@ static void ata_acpi_handle_hotplug(stru
ata_port_wait_eh(ap);
}

-static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data)
+static void ata_acpi_dev_notify_dock(u32 event, void *data)
{
struct ata_device *dev = data;

ata_acpi_handle_hotplug(dev->link->ap, dev, event);
}

-static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data)
+static void ata_acpi_ap_notify_dock(u32 event, void *data)
{
struct ata_port *ap = data;

@@ -154,12 +154,12 @@ static void ata_acpi_uevent(struct ata_p
}
}

-static void ata_acpi_ap_uevent(acpi_handle handle, u32 event, void *data)
+static void ata_acpi_ap_uevent(u32 event, void *data)
{
ata_acpi_uevent(data, NULL, event);
}

-static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data)
+static void ata_acpi_dev_uevent(u32 event, void *data)
{
struct ata_device *dev = data;
ata_acpi_uevent(dev->link->ap, dev, event);

2014-02-02 00:21:38

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 9/13] ACPI / hotplug / PCI: Drop acpiphp_bus_add()

From: Rafael J. Wysocki <[email protected]>

acpiphp_bus_add() is only called from one place, so move the code out
of it into that place and drop it. Also make that code use
func_to_acpi_device() to get the struct acpi_device pointer it needs
instead of calling acpi_bus_get_device() which may be costly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 22 ++++++----------------
1 file changed, 6 insertions(+), 16 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -470,20 +470,6 @@ static unsigned char acpiphp_max_busnr(s
return max;
}

-/**
- * acpiphp_bus_add - Scan ACPI namespace subtree.
- * @handle: ACPI object handle to start the scan from.
- */
-static void acpiphp_bus_add(acpi_handle handle)
-{
- struct acpi_device *adev = NULL;
-
- acpi_bus_scan(handle);
- acpi_bus_get_device(handle, &adev);
- if (acpi_device_enumerated(adev))
- acpi_device_set_power(adev, ACPI_STATE_D0);
-}
-
static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
{
struct acpiphp_func *func;
@@ -523,9 +509,13 @@ static int acpiphp_rescan_slot(struct ac
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling)
- acpiphp_bus_add(func_to_handle(func));
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev = func_to_acpi_device(func);

+ acpi_bus_scan(adev->handle);
+ if (acpi_device_enumerated(adev))
+ acpi_device_set_power(adev, ACPI_STATE_D0);
+ }
return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0));
}

2014-02-02 00:21:57

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 10/13] ACPI / hotplug / PCI: Drop crit_sect locking

From: Rafael J. Wysocki <[email protected]>

After recent PCI core changes related to the rescan/remove locking,
the code sections under crit_sect mutexes from ACPIPHP slot objects
are always executed under the general PCI rescan/remove lock.
For this reason, the crit_sect mutexes are simply redundant, so drop
them.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp.h | 1 -
drivers/pci/hotplug/acpiphp_glue.c | 23 +++--------------------
2 files changed, 3 insertions(+), 21 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -328,7 +328,6 @@ static acpi_status register_slot(acpi_ha
slot->bus = bridge->pci_bus;
slot->device = device;
INIT_LIST_HEAD(&slot->funcs);
- mutex_init(&slot->crit_sect);

list_add_tail(&slot->node, &bridge->slots);

@@ -723,7 +722,6 @@ static void acpiphp_check_bridge(struct
struct pci_bus *bus = slot->bus;
struct pci_dev *dev, *tmp;

- mutex_lock(&slot->crit_sect);
if (slot_no_hotplug(slot)) {
; /* do nothing */
} else if (get_slot_status(slot) == ACPI_STA_ALL) {
@@ -738,7 +736,6 @@ static void acpiphp_check_bridge(struct
} else {
disable_slot(slot);
}
- mutex_unlock(&slot->crit_sect);
}
}

@@ -821,12 +818,8 @@ static void hotplug_event(acpi_handle ha
} else {
struct acpiphp_slot *slot = func->slot;

- if (slot->flags & SLOT_IS_GOING_AWAY)
- break;
-
- mutex_lock(&slot->crit_sect);
- enable_slot(slot);
- mutex_unlock(&slot->crit_sect);
+ if (!(slot->flags & SLOT_IS_GOING_AWAY))
+ enable_slot(slot);
}
break;

@@ -837,7 +830,6 @@ static void hotplug_event(acpi_handle ha
acpiphp_check_bridge(bridge);
} else {
struct acpiphp_slot *slot = func->slot;
- int ret;

if (slot->flags & SLOT_IS_GOING_AWAY)
break;
@@ -846,10 +838,7 @@ static void hotplug_event(acpi_handle ha
* Check if anything has changed in the slot and rescan
* from the parent if that's the case.
*/
- mutex_lock(&slot->crit_sect);
- ret = acpiphp_rescan_slot(slot);
- mutex_unlock(&slot->crit_sect);
- if (ret)
+ if (acpiphp_rescan_slot(slot))
acpiphp_check_bridge(func->parent);
}
break;
@@ -1055,13 +1044,10 @@ int acpiphp_enable_slot(struct acpiphp_s
if (slot->flags & SLOT_IS_GOING_AWAY)
return -ENODEV;

- mutex_lock(&slot->crit_sect);
/* configure all functions */
if (!(slot->flags & SLOT_ENABLED))
enable_slot(slot);

- mutex_unlock(&slot->crit_sect);
-
pci_unlock_rescan_remove();
return 0;
}
@@ -1077,8 +1063,6 @@ static int acpiphp_disable_and_eject_slo
if (slot->flags & SLOT_IS_GOING_AWAY)
return -ENODEV;

- mutex_lock(&slot->crit_sect);
-
/* unconfigure all functions */
disable_slot(slot);

@@ -1092,7 +1076,6 @@ static int acpiphp_disable_and_eject_slo
break;
}

- mutex_unlock(&slot->crit_sect);
return 0;
}

Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -93,7 +93,6 @@ struct acpiphp_slot {
struct list_head funcs; /* one slot may have different
objects (i.e. for each function) */
struct slot *slot;
- struct mutex crit_sect;

u8 device; /* pci device# */
u32 flags; /* see below */

2014-02-02 00:22:24

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 11/13] ACPI / hotplug / PCI: Simplify hotplug_event()

From: Rafael J. Wysocki <[email protected]>

A few lines of code can be cut from hotplug_event() by defining
and initializing the slot variable at the top of the function,
so do that.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -792,6 +792,7 @@ static void hotplug_event(acpi_handle ha
{
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
+ struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
@@ -813,14 +814,11 @@ static void hotplug_event(acpi_handle ha
pr_debug("%s: Bus check notify on %s\n", __func__, objname);
pr_debug("%s: re-enumerating slots under %s\n",
__func__, objname);
- if (bridge) {
+ if (bridge)
acpiphp_check_bridge(bridge);
- } else {
- struct acpiphp_slot *slot = func->slot;
+ else if (!(slot->flags & SLOT_IS_GOING_AWAY))
+ enable_slot(slot);

- if (!(slot->flags & SLOT_IS_GOING_AWAY))
- enable_slot(slot);
- }
break;

case ACPI_NOTIFY_DEVICE_CHECK:
@@ -828,12 +826,7 @@ static void hotplug_event(acpi_handle ha
pr_debug("%s: Device check notify on %s\n", __func__, objname);
if (bridge) {
acpiphp_check_bridge(bridge);
- } else {
- struct acpiphp_slot *slot = func->slot;
-
- if (slot->flags & SLOT_IS_GOING_AWAY)
- break;
-
+ } else if (!(slot->flags & SLOT_IS_GOING_AWAY)) {
/*
* Check if anything has changed in the slot and rescan
* from the parent if that's the case.
@@ -846,7 +839,7 @@ static void hotplug_event(acpi_handle ha
case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
pr_debug("%s: Device eject notify on %s\n", __func__, objname);
- acpiphp_disable_and_eject_slot(func->slot);
+ acpiphp_disable_and_eject_slot(slot);
break;
}

2014-02-02 00:46:30

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 4/6] ACPI / hotplug / PCI: Rework the handling of eject requests

From: Rafael J. Wysocki <[email protected]>

To avoid the need to install a hotplug notify handler for each ACPI
namespace node representing a device and having a matching scan
handler, move the check whether or not the ejection of the given
device is enabled through its scan handler from acpi_hotplug_notify_cb()
to acpi_generic_hotplug_event(). Also, move the execution of
ACPI_OST_SC_EJECT_IN_PROGRESS _OST to acpi_generic_hotplug_event(),
because in acpi_hotplug_notify_cb() or in acpi_eject_store() we really
don't know whether or not the eject is going to be in progress (for
example, acpi_hotplug_execute() may still fail without queuing up the
work item).

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -461,6 +461,12 @@ static int acpi_generic_hotplug_event(st
return acpi_scan_device_check(adev);
case ACPI_NOTIFY_EJECT_REQUEST:
case ACPI_OST_EC_OSPM_EJECT:
+ if (adev->handler && !adev->handler->hotplug.enabled) {
+ dev_info(&adev->dev, "Eject disabled\n");
+ return -EPERM;
+ }
+ acpi_evaluate_hotplug_ost(adev->handle, ACPI_NOTIFY_EJECT_REQUEST,
+ ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
return acpi_scan_hot_remove(adev);
}
return -EINVAL;
@@ -485,6 +491,10 @@ static void acpi_device_hotplug(void *da

if (adev->handler) {
error = acpi_generic_hotplug_event(adev, src);
+ if (error == -EPERM) {
+ ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
+ goto err_out;
+ }
} else {
int (*event)(struct acpi_device *, u32);

@@ -514,7 +524,6 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- struct acpi_scan_handler *handler = data;
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;
@@ -530,13 +539,6 @@ static void acpi_hotplug_notify_cb(acpi_

case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (handler && !handler->hotplug.enabled) {
- acpi_handle_err(handle, "Eject disabled\n");
- 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);
break;

case ACPI_NOTIFY_DEVICE_WAKE:
@@ -637,8 +639,6 @@ acpi_eject_store(struct device *d, struc
if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
return -ENODEV;

- acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
- ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
get_device(&acpi_device->dev);
status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device,
ACPI_OST_EC_OSPM_EJECT);

2014-02-02 00:46:29

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 5/6] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

From: Rafael J. Wysocki <[email protected]>

Since acpi_hotplug_notify_cb() does not use its data argument any
more, the second argument of acpi_install_hotplug_notify_handler()
can be dropped, so do that and update its callers accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 6 +++---
drivers/pci/hotplug/acpiphp_glue.c | 2 +-
include/acpi/acpi_bus.h | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -582,10 +582,10 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+void acpi_install_hotplug_notify_handler(acpi_handle handle)
{
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, data);
+ acpi_hotplug_notify_cb, NULL);
}

void acpi_remove_hotplug_notify_handler(acpi_handle handle)
@@ -2024,7 +2024,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_hotplug_notify_handler(handle, handler);
+ acpi_install_hotplug_notify_handler(handle);
break;
}
}
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -362,7 +362,7 @@ static acpi_status register_slot(acpi_ha

/* install notify handler */
if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle, NULL);
+ acpi_install_hotplug_notify_handler(handle);

return AE_OK;
}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -443,7 +443,7 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_install_hotplug_notify_handler(acpi_handle handle);
void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**

2014-02-02 00:46:55

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 0/6] ACPI / hotplug / PCI: Consolidation of ACPIPHP with ACPI core device hotplug

On Wednesday, January 29, 2014 12:57:06 AM Rafael J. Wysocki wrote:
> On Tuesday, January 28, 2014 11:10:30 PM Rafael J. Wysocki wrote:
> > Hi All,
> >
> > It looks like there's time for more adventurous stuff. :-)
> >
> > The following series is on top of the one I sent on Sunday:
> >
> > https://lkml.org/lkml/2014/1/26/191
> >
> > The final outcome of the patches below is that all ACPI hotplug notifications
> > for PCI devices and for core system things like CPU, memory, PCI roots etc.,
> > will be dispatched from acpi_bus_notify() and it is not necessary to install a
> > separate hotplug notify handler for each device any more.
> >
> > [1/5] Attach ACPIPHP hotplug contexts to struct acpi_device objects.
> > [2/5] Introduce wrappers for installing and removing hotplug notify handlers
> > (those wrappers go away later on, but they are useful for separating
> > changes).
> > [3/5] Consolidate ACPI hotplug signaling for PCI and ACPI core.
> > [4/5] Simplify notify handle registration wrapper.
> > [5/5] Dispatch ACPI hotplug notifications for "core" devices and PCI from acpi_bus_notify().
>
> Unfortunately, I realized that patches [3-5/5] were buggy. The bugs were
> kind of subtle and might not be easy to reproduce, but they were bugs anyway. :-)
>
> A respin of the whole series follows.

After the Mika's testing it turned out that they were more buggy than I had
though. Oh well.

The following patchset is a reworked version of the previous one. Functionality-wise
the final result should be very similar, but not exactly the same.

[1/6] Fix a theoretical race condition in acpi_hotplug_notify_cb().
[2/6] Move the hotplug context lock definition to the ACPI core (from ACPIPHP).
[3/6] Consolidate ACPI hotplug signaling for PCI and ACPI core (this is a combination
of patches [1-3/5] from the previous series).
[4/6] Rework the handling of eject requests in the ACPI core.
[5/6] Simplify a routine for installing hotplug notify handlers.
[6/6] Dispatch ACPI hotplug notifications for "core" devices and PCI from acpi_bus_notify().

This is on top of https://lkml.org/lkml/2014/2/1/123 which in turn is on top of
the current mainline.

For the adventurous all this stuff is on the test-next branch of linux-pm.git.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-02 00:46:28

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 6/6] ACPI / hotplug / PCI: Hotplug notifications from acpi_bus_notify()

From: Rafael J. Wysocki <[email protected]>

Since acpi_bus_notify() is executed on all notifications for all
devices anyway, rename acpi_hotplug_notify_cb() to acpi_system_notify()
and call it directly from acpi_bus_notify() instead of installing
notify handlers pointing to it for all hotplug devices.

This change reduces both the size and complexity of ACPI-based device
hotplug code. Moreover, since acpi_system_notify() only does
significant things for devices that either have an ACPI scan handler,
or have a hotplug context with .eject() defined, and those devices
had notify handlers pointing to acpi_hotplug_notify_cb() installed
before anyway, this modification shouldn't change functionality.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/bus.c | 42 -------------------------------
drivers/acpi/internal.h | 1
drivers/acpi/scan.c | 49 ++++---------------------------------
drivers/pci/hotplug/acpiphp.h | 1
drivers/pci/hotplug/acpiphp_glue.c | 16 +++++-------
include/acpi/acpi_bus.h | 2 -
6 files changed, 15 insertions(+), 96 deletions(-)

Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -346,47 +346,7 @@ static void acpi_bus_notify(acpi_handle
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
type, handle));

- switch (type) {
-
- case ACPI_NOTIFY_BUS_CHECK:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_EJECT_REQUEST:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
- /* TBD: Exactly what does 'light' mean? */
- break;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- /* TBD */
- break;
-
- case ACPI_NOTIFY_POWER_FAULT:
- /* TBD */
- break;
-
- default:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Received unknown/unsupported notification [%08x]\n",
- type));
- break;
- }
-
+ acpi_system_notify(handle, type);
acpi_bus_get_device(handle, &device);
if (device) {
driver = device->driver;
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -74,6 +74,7 @@ static inline void acpi_lpss_init(void)

bool acpi_queue_hotplug_work(struct work_struct *work);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
+void acpi_system_notify(acpi_handle handle, u32 type);

/* --------------------------------------------------------------------------
Device Node Initialization / Removal
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -522,7 +522,7 @@ static void acpi_device_hotplug(void *da
unlock_device_hotplug();
}

-static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
+void acpi_system_notify(acpi_handle handle, u32 type)
{
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
@@ -537,6 +537,11 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;

+ case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n");
+ /* TBD: Exactly what does 'light' mean? */
+ break;
+
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
break;
@@ -582,18 +587,6 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, NULL);
-}
-
-void acpi_remove_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb);
-}
-
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2005,34 +1998,6 @@ void acpi_scan_hotplug_enabled(struct ac
mutex_unlock(&acpi_scan_lock);
}

-static void acpi_scan_init_hotplug(acpi_handle handle, int type)
-{
- struct acpi_device_pnp pnp = {};
- struct acpi_hardware_id *hwid;
- struct acpi_scan_handler *handler;
-
- INIT_LIST_HEAD(&pnp.ids);
- acpi_set_pnp_ids(handle, &pnp, type);
-
- if (!pnp.type.hardware_id)
- goto out;
-
- /*
- * This relies on the fact that acpi_install_notify_handler() will not
- * install the same notify handler routine twice for the same handle.
- */
- list_for_each_entry(hwid, &pnp.ids, list) {
- handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- acpi_install_hotplug_notify_handler(handle);
- break;
- }
- }
-
-out:
- acpi_free_pnp_ids(&pnp);
-}
-
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **return_value)
{
@@ -2054,8 +2019,6 @@ static acpi_status acpi_bus_check_add(ac
return AE_OK;
}

- acpi_scan_init_hotplug(handle, type);
-
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -283,7 +283,6 @@ static acpi_status register_slot(acpi_ha
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- acpi_unlock_hp_context();

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -291,8 +290,14 @@ static acpi_status register_slot(acpi_ha
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;

+ /*
+ * Dock stations' notify handler should be used for dock devices instead
+ * of the common one, so clear hp.event in their contexts.
+ */
if (acpi_has_method(handle, "_DCK"))
- newfunc->flags |= FUNC_HAS_DCK;
+ context->hp.event = NULL;
+
+ acpi_unlock_hp_context();

/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
@@ -360,10 +365,6 @@ static acpi_status register_slot(acpi_ha
pr_debug("failed to register dock device\n");
}

- /* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle);
-
return AE_OK;
}

@@ -400,9 +401,6 @@ static void cleanup_bridge(struct acpiph

if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);
-
- if (!(func->flags & FUNC_HAS_DCK))
- acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -443,8 +443,6 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle);
-void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -167,7 +167,6 @@ struct acpiphp_attention_info

#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
-#define FUNC_HAS_DCK (0x00000004)

/* function prototypes */

2014-02-02 00:47:32

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 2/6] ACPI / hotplug / PCI: Define hotplug context lock in the core

From: Rafael J. Wysocki <[email protected]>

Subsequent changes will require the ACPI core to acquire the lock
protecting the ACPIPHP hotplug contexts, so move the definition of
the lock to the core and change its name to be more generic.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 11 +++++++++
drivers/pci/hotplug/acpiphp_glue.c | 41 ++++++++++++++++++-------------------
include/acpi/acpi_bus.h | 2 +
3 files changed, 33 insertions(+), 21 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -43,6 +43,7 @@ DEFINE_MUTEX(acpi_device_lock);
LIST_HEAD(acpi_wakeup_device_list);
static LIST_HEAD(acpi_device_del_list);
static DEFINE_MUTEX(acpi_device_del_lock);
+static DEFINE_MUTEX(acpi_hp_context_lock);

struct acpi_device_bus_id{
char bus_id[15];
@@ -62,6 +63,16 @@ void acpi_scan_lock_release(void)
}
EXPORT_SYMBOL_GPL(acpi_scan_lock_release);

+void acpi_lock_hp_context(void)
+{
+ mutex_lock(&acpi_hp_context_lock);
+}
+
+void acpi_unlock_hp_context(void)
+{
+ mutex_unlock(&acpi_hp_context_lock);
+}
+
int acpi_scan_add_handler(struct acpi_scan_handler *handler)
{
if (!handler || !handler->attach)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -58,7 +58,6 @@

static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
-static DEFINE_MUTEX(acpiphp_context_lock);

static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
@@ -75,7 +74,7 @@ static void acpiphp_context_handler(acpi
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @adev: ACPI device object to create the context for.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
@@ -100,7 +99,7 @@ static struct acpiphp_context *acpiphp_i
* acpiphp_get_context - Get hotplug context and grab a reference to it.
* @handle: ACPI object handle to get the context for.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
{
@@ -122,7 +121,7 @@ static struct acpiphp_context *acpiphp_g
*
* The context object is removed if there are no more references to it.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static void acpiphp_put_context(struct acpiphp_context *context)
{
@@ -151,7 +150,7 @@ static void free_bridge(struct kref *kre
struct acpiphp_slot *slot, *next;
struct acpiphp_func *func, *tmp;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();

bridge = container_of(kref, struct acpiphp_bridge, ref);

@@ -175,7 +174,7 @@ static void free_bridge(struct kref *kre
pci_dev_put(bridge->pci_dev);
kfree(bridge);

- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
}

/*
@@ -291,17 +290,17 @@ static acpi_status register_slot(acpi_ha
device = (adr >> 16) & 0xffff;
function = adr & 0xffff;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_init_context(adev);
if (!context) {
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
acpi_handle_err(handle, "No hotplug context\n");
return AE_NOT_EXIST;
}
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -319,9 +318,9 @@ static acpi_status register_slot(acpi_ha

slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
acpiphp_put_context(context);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return AE_NO_MEMORY;
}

@@ -396,7 +395,7 @@ static struct acpiphp_bridge *acpiphp_ha
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (context) {
bridge = context->bridge;
@@ -405,7 +404,7 @@ static struct acpiphp_bridge *acpiphp_ha

acpiphp_put_context(context);
}
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return bridge;
}

@@ -796,12 +795,12 @@ static void hotplug_event(u32 type, void
acpi_handle handle = context->adev->handle;
struct acpiphp_bridge *bridge;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
bridge = context->bridge;
if (bridge)
get_bridge(bridge);

- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();

pci_lock_rescan_remove();

@@ -902,16 +901,16 @@ static void handle_hotplug_event(acpi_ha
goto out;
}

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (context && !WARN_ON(context->adev->handle != handle)) {
get_bridge(context->func.parent);
acpiphp_put_context(context);
acpi_hotplug_execute(hotplug_event_work, context, type);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return;
}
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

out:
@@ -967,10 +966,10 @@ void acpiphp_enumerate_slots(struct pci_
* parent is going to be handled by pciehp, in which case this
* bridge is not interesting to us either.
*/
- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (!context) {
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
put_device(&bus->dev);
pci_dev_put(bridge->pci_dev);
kfree(bridge);
@@ -980,7 +979,7 @@ void acpiphp_enumerate_slots(struct pci_
context->bridge = bridge;
/* Get a reference to the parent bridge. */
get_bridge(context->func.parent);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
}

/* must be added to the list prior to calling register_slot */
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -402,6 +402,8 @@ static inline bool acpi_bus_can_wakeup(a

void acpi_scan_lock_acquire(void);
void acpi_scan_lock_release(void);
+void acpi_lock_hp_context(void);
+void acpi_unlock_hp_context(void);
int acpi_scan_add_handler(struct acpi_scan_handler *handler);
int acpi_bus_register_driver(struct acpi_driver *driver);
void acpi_bus_unregister_driver(struct acpi_driver *driver);

2014-02-02 00:47:31

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 1/6] ACPI / hotplug: Fix theoretical race in acpi_hotplug_notify_cb()

From: Rafael J. Wysocki <[email protected]>

There is a slight possibility for the ACPI device object pointed to
by adev in acpi_hotplug_notify_cb() to become invalid between the
acpi_bus_get_device() that it comes from and the subsequent get_device().
Namely, if acpi_scan_drop_device() runs concurrently with respect to
acpi_hotplug_notify_cb() and acpi_device_del_list is not empty,
acpi_device_del_work_fn() may delete the device object in question
without waiting for the ACPI events workqueue to drain, which very
well may happen right after a successful execution of
acpi_bus_get_device() in acpi_hotplug_notify_cb().

To prevent that from happening, run acpi_bus_get_device() and the
subsequent get_device() in acpi_hotplug_notify_cb() under
acpi_device_del_lock, so that the deletion of the given device
object cannot be queued up by acpi_scan_drop_device() between the
two.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -41,6 +41,8 @@ static DEFINE_MUTEX(acpi_scan_lock);
static LIST_HEAD(acpi_scan_handlers_list);
DEFINE_MUTEX(acpi_device_lock);
LIST_HEAD(acpi_wakeup_device_list);
+static LIST_HEAD(acpi_device_del_list);
+static DEFINE_MUTEX(acpi_device_del_lock);

struct acpi_device_bus_id{
char bus_id[15];
@@ -488,9 +490,6 @@ static void acpi_hotplug_notify_cb(acpi_
struct acpi_device *adev;
acpi_status status;

- if (acpi_bus_get_device(handle, &adev))
- goto err_out;
-
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
@@ -512,7 +511,13 @@ static void acpi_hotplug_notify_cb(acpi_
/* non-hotplug event; possibly handled by other handler */
return;
}
+ mutex_lock(&acpi_device_del_lock);
+ if (acpi_bus_get_device(handle, &adev)) {
+ mutex_unlock(&acpi_device_del_lock);
+ goto err_out;
+ }
get_device(&adev->dev);
+ mutex_unlock(&acpi_device_del_lock);
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
return;
@@ -1042,9 +1047,6 @@ static void acpi_device_del(struct acpi_
device_del(&device->dev);
}

-static LIST_HEAD(acpi_device_del_list);
-static DEFINE_MUTEX(acpi_device_del_lock);
-
static void acpi_device_del_work_fn(struct work_struct *work_not_used)
{
for (;;) {

2014-02-02 00:48:10

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 3/6] ACPI / hotplug / PCI: Consolidate ACPIPHP with ACPI core hotplug

From: Rafael J. Wysocki <[email protected]>

The ACPI-based PCI hotplug (ACPIPHP) code currently attaches its
hotplug context objects directly to ACPI namespace nodes representing
hotplug devices. However, after recent changes causing struct
acpi_device to be created for every namespace node representing a
device (regardless of its status), that is not necessary any more.
Moreover, it's vulnerable to a theoretical issue that the ACPI
handle passed in the context between handle_hotplug_event() and
hotplug_event_work() may become invalid in the meantime (as a
result of a concurrent table unload).

In principle, this issue might be addressed by adding a non-empty
release handler for ACPIPHP hotplug context objects analogous to
acpi_scan_drop_device(), but that would duplicate the code in that
function and in acpi_device_del_work_fn(). For this reason, it's
better to modify ACPIPHP to attach its device hotplug contexts to
struct device objects representing hotplug devices and make it
use acpi_hotplug_notify_cb() as its notify handler. At the same
time, acpi_device_hotplug() can be modified to dispatch the new
.hp.event() callback pointing to acpiphp_hotplug_event() from ACPI
device objects associated with PCI devices and use the generic
ACPI device hotplug code for device objects with scan handlers
attached to them.

This allows the existing code duplication between ACPIPHP and the
ACPI core to be reduced too and makes further ACPI-based device
hotplug consolidation possible.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 106 +++++++++++++++++++++-------
drivers/pci/hotplug/acpiphp.h | 9 +-
drivers/pci/hotplug/acpiphp_glue.c | 136 +++++++------------------------------
include/acpi/acpi_bus.h | 22 +++++
4 files changed, 136 insertions(+), 137 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -452,43 +452,61 @@ static int acpi_scan_bus_check(struct ac
return 0;
}

+static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
+{
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ return acpi_scan_bus_check(adev);
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ return acpi_scan_device_check(adev);
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ case ACPI_OST_EC_OSPM_EJECT:
+ return acpi_scan_hot_remove(adev);
+ }
+ return -EINVAL;
+}
+
static void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
- int error;
+ int error = -ENODEV;

lock_device_hotplug();
mutex_lock(&acpi_scan_lock);

/*
* The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it may have become invalid before
+ * are holding acpi_scan_lock, but it might have become invalid before
* that lock was acquired.
*/
if (adev->handle == INVALID_ACPI_HANDLE)
- goto out;
+ goto err_out;

- switch (src) {
- case ACPI_NOTIFY_BUS_CHECK:
- error = acpi_scan_bus_check(adev);
- break;
- case ACPI_NOTIFY_DEVICE_CHECK:
- error = acpi_scan_device_check(adev);
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- case ACPI_OST_EC_OSPM_EJECT:
- error = acpi_scan_hot_remove(adev);
- break;
- default:
- error = -EINVAL;
- break;
+ if (adev->handler) {
+ error = acpi_generic_hotplug_event(adev, src);
+ } else {
+ int (*event)(struct acpi_device *, u32);
+
+ acpi_lock_hp_context();
+ event = adev->hp ? adev->hp->event : NULL;
+ acpi_unlock_hp_context();
+ /*
+ * There may be additional notify handlers for device objects
+ * without the .event() callback, so ignore them here.
+ */
+ if (event)
+ error = event(adev, src);
+ else
+ goto out;
}
if (!error)
ost_code = ACPI_OST_SC_SUCCESS;

- out:
+ err_out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
+
+ out:
put_device(&adev->dev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
@@ -496,8 +514,8 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_scan_handler *handler = data;
+ u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;

@@ -505,27 +523,50 @@ static void acpi_hotplug_notify_cb(acpi_
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;
+
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;
+
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (!handler->hotplug.enabled) {
+ if (handler && !handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
- goto err_out;
+ goto out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
- default:
- /* non-hotplug event; possibly handled by other handler */
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
return;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
+ goto out;
+
+ default:
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto out;
}
+
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
mutex_lock(&acpi_device_del_lock);
if (acpi_bus_get_device(handle, &adev)) {
mutex_unlock(&acpi_device_del_lock);
- goto err_out;
+ goto out;
}
get_device(&adev->dev);
mutex_unlock(&acpi_device_del_lock);
@@ -535,10 +576,22 @@ static void acpi_hotplug_notify_cb(acpi_

put_device(&adev->dev);

- err_out:
+ out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+{
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb, data);
+}
+
+void acpi_remove_hotplug_notify_handler(acpi_handle handle)
+{
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb);
+}
+
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1971,8 +2024,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, handler);
+ acpi_install_hotplug_notify_handler(handle, handler);
break;
}
}
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -116,12 +116,17 @@ struct acpiphp_func {
};

struct acpiphp_context {
+ struct acpi_hotplug_context hp;
struct acpiphp_func func;
- struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};

+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *fu

static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
{
- return func_to_context(func)->adev;
+ return func_to_context(func)->hp.self;
}

static inline acpi_handle func_to_handle(struct acpiphp_func *func)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -59,17 +59,12 @@
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);

-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, void *data);
static void free_bridge(struct kref *kref);

-static void acpiphp_context_handler(acpi_handle handle, void *context)
-{
- /* Intentionally empty. */
-}
-
/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @adev: ACPI device object to create the context for.
@@ -79,39 +74,27 @@ static void acpiphp_context_handler(acpi
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
- acpi_status status;

context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;

- context->adev = adev;
context->refcount = 1;
- status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
- if (ACPI_FAILURE(status)) {
- kfree(context);
- return NULL;
- }
+ acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event);
return context;
}

/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
- * @handle: ACPI object handle to get the context for.
+ * @adev: ACPI device object to get the context for.
*
* Call under acpi_hp_context_lock.
*/
-static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- struct acpiphp_context *context = NULL;
- acpi_status status;
- void *data;
+ struct acpiphp_context *context = to_acpiphp_context(adev->hp);

- status = acpi_get_data(handle, acpiphp_context_handler, &data);
- if (ACPI_SUCCESS(status)) {
- context = data;
- context->refcount++;
- }
+ context->refcount++;
return context;
}

@@ -129,7 +112,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->adev->handle, acpiphp_context_handler);
+ context->hp.self->hp = NULL;
kfree(context);
}

@@ -378,14 +361,8 @@ static acpi_status register_slot(acpi_ha
}

/* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK)) {
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event,
- context);
- if (ACPI_FAILURE(status))
- acpi_handle_err(handle,
- "failed to install notify handler\n");
- }
+ if (!(newfunc->flags & FUNC_HAS_DCK))
+ acpi_install_hotplug_notify_handler(handle, NULL);

return AE_OK;
}
@@ -394,9 +371,13 @@ static struct acpiphp_bridge *acpiphp_ha
{
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;
+ struct acpi_device *adev;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return NULL;

acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (context) {
bridge = context->bridge;
if (bridge)
@@ -412,7 +393,6 @@ static void cleanup_bridge(struct acpiph
{
struct acpiphp_slot *slot;
struct acpiphp_func *func;
- acpi_status status;

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
@@ -421,13 +401,8 @@ static void cleanup_bridge(struct acpiph
if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);

- if (!(func->flags & FUNC_HAS_DCK)) {
- status = acpi_remove_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event);
- if (ACPI_FAILURE(status))
- pr_err("failed to remove notify handler\n");
- }
+ if (!(func->flags & FUNC_HAS_DCK))
+ acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
@@ -792,7 +767,7 @@ static void hotplug_event(u32 type, void
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
- acpi_handle handle = context->adev->handle;
+ acpi_handle handle = context->hp.self->handle;
struct acpiphp_bridge *bridge;

acpi_lock_hp_context();
@@ -842,79 +817,24 @@ static void hotplug_event(u32 type, void
put_bridge(bridge);
}

-static void hotplug_event_work(void *data, u32 type)
-{
- struct acpiphp_context *context = data;
-
- acpi_scan_lock_acquire();
-
- hotplug_event(type, context);
-
- acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(context->adev->handle, type,
- ACPI_OST_SC_SUCCESS, NULL);
- put_bridge(context->func.parent);
-}
-
-/**
- * handle_hotplug_event - handle ACPI hotplug event
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @data: pointer to acpiphp_context structure
- *
- * Handles ACPI event notification on slots.
- */
-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{
struct acpiphp_context *context;
- u32 ost_code = ACPI_OST_SC_SUCCESS;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- case ACPI_NOTIFY_DEVICE_CHECK:
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS;
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }

acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
- if (context && !WARN_ON(context->adev->handle != handle)) {
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_hotplug_execute(hotplug_event_work, context, type);
+ context = acpiphp_get_context(adev);
+ if (!context) {
acpi_unlock_hp_context();
- return;
+ return -ENODATA;
}
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
acpi_unlock_hp_context();
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
+ hotplug_event(type, context);
+
+ put_bridge(context->func.parent);
+ return 0;
}

/**
@@ -967,7 +887,7 @@ void acpiphp_enumerate_slots(struct pci_
* bridge is not interesting to us either.
*/
acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (!context) {
acpi_unlock_hp_context();
put_device(&bus->dev);
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -137,6 +137,16 @@ struct acpi_scan_handler {
};

/*
+ * ACPI Hotplug Context
+ * --------------------
+ */
+
+struct acpi_hotplug_context {
+ struct acpi_device *self;
+ int (*event)(struct acpi_device *, u32);
+};
+
+/*
* ACPI Driver
* -----------
*/
@@ -329,6 +339,7 @@ struct acpi_device {
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_scan_handler *handler;
+ struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
void *driver_data;
struct device dev;
@@ -351,6 +362,15 @@ static inline void acpi_set_device_statu
*((u32 *)&adev->status) = sta;
}

+static inline void acpi_set_hp_context(struct acpi_device *adev,
+ struct acpi_hotplug_context *hp,
+ int (*event)(struct acpi_device *, u32))
+{
+ hp->self = adev;
+ hp->event = event;
+ adev->hp = hp;
+}
+
/* acpi_device.dev.bus == &acpi_bus_type */
extern struct bus_type acpi_bus_type;

@@ -423,6 +443,8 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver

2014-02-02 16:47:09

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v2 1/6] ACPI / hotplug: Fix theoretical race in acpi_hotplug_notify_cb()

On Sunday, February 02, 2014 01:54:02 AM Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> There is a slight possibility for the ACPI device object pointed to
> by adev in acpi_hotplug_notify_cb() to become invalid between the
> acpi_bus_get_device() that it comes from and the subsequent get_device().
> Namely, if acpi_scan_drop_device() runs concurrently with respect to
> acpi_hotplug_notify_cb() and acpi_device_del_list is not empty,
> acpi_device_del_work_fn() may delete the device object in question
> without waiting for the ACPI events workqueue to drain, which very
> well may happen right after a successful execution of
> acpi_bus_get_device() in acpi_hotplug_notify_cb().
>
> To prevent that from happening, run acpi_bus_get_device() and the
> subsequent get_device() in acpi_hotplug_notify_cb() under
> acpi_device_del_lock, so that the deletion of the given device
> object cannot be queued up by acpi_scan_drop_device() between the
> two.
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>
> ---
> drivers/acpi/scan.c | 14 ++++++++------
> 1 file changed, 8 insertions(+), 6 deletions(-)
>
> Index: linux-pm/drivers/acpi/scan.c
> ===================================================================
> --- linux-pm.orig/drivers/acpi/scan.c
> +++ linux-pm/drivers/acpi/scan.c
> @@ -41,6 +41,8 @@ static DEFINE_MUTEX(acpi_scan_lock);
> static LIST_HEAD(acpi_scan_handlers_list);
> DEFINE_MUTEX(acpi_device_lock);
> LIST_HEAD(acpi_wakeup_device_list);
> +static LIST_HEAD(acpi_device_del_list);
> +static DEFINE_MUTEX(acpi_device_del_lock);
>
> struct acpi_device_bus_id{
> char bus_id[15];
> @@ -488,9 +490,6 @@ static void acpi_hotplug_notify_cb(acpi_
> struct acpi_device *adev;
> acpi_status status;
>
> - if (acpi_bus_get_device(handle, &adev))
> - goto err_out;
> -
> switch (type) {
> case ACPI_NOTIFY_BUS_CHECK:
> acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
> @@ -512,7 +511,13 @@ static void acpi_hotplug_notify_cb(acpi_
> /* non-hotplug event; possibly handled by other handler */
> return;
> }
> + mutex_lock(&acpi_device_del_lock);
> + if (acpi_bus_get_device(handle, &adev)) {
> + mutex_unlock(&acpi_device_del_lock);
> + goto err_out;
> + }
> get_device(&adev->dev);
> + mutex_unlock(&acpi_device_del_lock);
> status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
> if (ACPI_SUCCESS(status))
> return;

Well, that would have been good if it hand't been broken. :-(

acpi_scan_drop_device() which acquires acpi_device_del_lock is called under
the ACPICA's namespace mutex and acpi_bus_get_device() above acquires that
mutex, so this leads to a classical ABBA deadlock scenario. Bummer.

And I haven't been able to convince myself that what we're doing in
acpi_hotplug_notify_cb() is actually safe without any locking. Not to
mention acpi_bus_notify() for that matter. Moreover, the *only* safe
way to do that I'm seeing at the moment is to call the get_device()
under the ACPICA's namespace mutex, before it is released in
acpi_get_data().

Of course, ACPICA will need to be modified slightly for that to be
possible (sorry, Bob), but at least that *should* work, so I have a
new version of this patchset doing just that. I'll send it out
shortly.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-02 17:06:25

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v3 7/7] ACPI / hotplug / PCI: Hotplug notifications from acpi_bus_notify()

From: Rafael J. Wysocki <[email protected]>

Since acpi_bus_notify() is executed on all notifications for all
devices anyway, make it execute acpi_device_hotplug() for all
hotplug events instead of installing notify handlers pointing to
the same function for all hotplug devices.

This change reduces both the size and complexity of ACPI-based device
hotplug code. Moreover, since acpi_device_hotplug() only does
significant things for devices that have either an ACPI scan handler,
or a hotplug context with .eject() defined, and those devices
had notify handlers pointing to acpi_hotplug_notify_cb() installed
before anyway, this modification shouldn't change functionality.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/bus.c | 60 +++++++++++++---------
drivers/acpi/internal.h | 1
drivers/acpi/scan.c | 100 -------------------------------------
drivers/pci/hotplug/acpiphp.h | 1
drivers/pci/hotplug/acpiphp_glue.c | 16 ++---
include/acpi/acpi_bus.h | 2
6 files changed, 46 insertions(+), 134 deletions(-)

Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -340,62 +340,76 @@ static void acpi_bus_osc_support(void)
*/
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
{
- struct acpi_device *device = NULL;
+ struct acpi_device *adev;
struct acpi_driver *driver;
-
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
- type, handle));
+ acpi_status status;
+ u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

switch (type) {
-
case ACPI_NOTIFY_BUS_CHECK:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;

case ACPI_NOTIFY_DEVICE_CHECK:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;

case ACPI_NOTIFY_DEVICE_WAKE:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n");
break;

case ACPI_NOTIFY_EJECT_REQUEST:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
break;

case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n");
/* TBD: Exactly what does 'light' mean? */
break;

case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- /* TBD */
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
break;

case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- /* TBD */
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
break;

case ACPI_NOTIFY_POWER_FAULT:
- /* TBD */
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
break;

default:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Received unknown/unsupported notification [%08x]\n",
- type));
- break;
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto err;
}

- acpi_bus_get_acpi_device(handle, &device);
- if (device) {
- driver = device->driver;
- if (driver && driver->ops.notify &&
- (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
- driver->ops.notify(device, type);
+ if (acpi_bus_get_acpi_device(handle, &adev))
+ goto err;

- acpi_bus_put_acpi_device(device);
+ driver = adev->driver;
+ if (driver && driver->ops.notify &&
+ (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
+ driver->ops.notify(adev, type);
+
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
+ if (ACPI_SUCCESS(status))
+ return;
+ default:
+ break;
}
+ acpi_bus_put_acpi_device(adev);
+ return;
+
+ err:
+ acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

/* --------------------------------------------------------------------------
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -73,6 +73,7 @@ static inline void acpi_lpss_init(void)
#endif

bool acpi_queue_hotplug_work(struct work_struct *work);
+void acpi_device_hotplug(void *data, u32 src);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);

/* --------------------------------------------------------------------------
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -470,7 +470,7 @@ static int acpi_generic_hotplug_event(st
return -EINVAL;
}

-static void acpi_device_hotplug(void *data, u32 src)
+void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
@@ -520,74 +520,6 @@ static void acpi_device_hotplug(void *da
unlock_device_hotplug();
}

-static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
-{
- u32 ost_code = ACPI_OST_SC_SUCCESS;
- struct acpi_device *adev;
- acpi_status status;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK:
- acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
- break;
-
- case ACPI_NOTIFY_EJECT_REQUEST:
- acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }
-
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
- if (acpi_bus_get_acpi_device(handle, &adev))
- goto out;
-
- status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
- if (ACPI_SUCCESS(status))
- return;
-
- acpi_bus_put_acpi_device(adev);
-
- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
-}
-
-void acpi_install_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, NULL);
-}
-
-void acpi_remove_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb);
-}
-
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2025,34 +1957,6 @@ void acpi_scan_hotplug_enabled(struct ac
mutex_unlock(&acpi_scan_lock);
}

-static void acpi_scan_init_hotplug(acpi_handle handle, int type)
-{
- struct acpi_device_pnp pnp = {};
- struct acpi_hardware_id *hwid;
- struct acpi_scan_handler *handler;
-
- INIT_LIST_HEAD(&pnp.ids);
- acpi_set_pnp_ids(handle, &pnp, type);
-
- if (!pnp.type.hardware_id)
- goto out;
-
- /*
- * This relies on the fact that acpi_install_notify_handler() will not
- * install the same notify handler routine twice for the same handle.
- */
- list_for_each_entry(hwid, &pnp.ids, list) {
- handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- acpi_install_hotplug_notify_handler(handle);
- break;
- }
- }
-
-out:
- acpi_free_pnp_ids(&pnp);
-}
-
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **return_value)
{
@@ -2074,8 +1978,6 @@ static acpi_status acpi_bus_check_add(ac
return AE_OK;
}

- acpi_scan_init_hotplug(handle, type);
-
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -283,7 +283,6 @@ static acpi_status register_slot(acpi_ha
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- acpi_unlock_hp_context();

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -291,8 +290,14 @@ static acpi_status register_slot(acpi_ha
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;

+ /*
+ * Dock stations' notify handler should be used for dock devices instead
+ * of the common one, so clear hp.event in their contexts.
+ */
if (acpi_has_method(handle, "_DCK"))
- newfunc->flags |= FUNC_HAS_DCK;
+ context->hp.event = NULL;
+
+ acpi_unlock_hp_context();

/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
@@ -360,10 +365,6 @@ static acpi_status register_slot(acpi_ha
pr_debug("failed to register dock device\n");
}

- /* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle);
-
return AE_OK;
}

@@ -400,9 +401,6 @@ static void cleanup_bridge(struct acpiph

if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);
-
- if (!(func->flags & FUNC_HAS_DCK))
- acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -443,8 +443,6 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle);
-void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -167,7 +167,6 @@ struct acpiphp_attention_info

#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
-#define FUNC_HAS_DCK (0x00000004)

/* function prototypes */

2014-02-02 17:07:21

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v3 0/7] ACPI / hotplug / PCI: Consolidation of ACPIPHP with ACPI core device hotplug

On Sunday, February 02, 2014 01:52:26 AM Rafael J. Wysocki wrote:
> On Wednesday, January 29, 2014 12:57:06 AM Rafael J. Wysocki wrote:
> > On Tuesday, January 28, 2014 11:10:30 PM Rafael J. Wysocki wrote:
> > > Hi All,
> > >
> > > It looks like there's time for more adventurous stuff. :-)
> > >
> > > The following series is on top of the one I sent on Sunday:
> > >
> > > https://lkml.org/lkml/2014/1/26/191
> > >
> > > The final outcome of the patches below is that all ACPI hotplug notifications
> > > for PCI devices and for core system things like CPU, memory, PCI roots etc.,
> > > will be dispatched from acpi_bus_notify() and it is not necessary to install a
> > > separate hotplug notify handler for each device any more.
> > >
> > > [1/5] Attach ACPIPHP hotplug contexts to struct acpi_device objects.
> > > [2/5] Introduce wrappers for installing and removing hotplug notify handlers
> > > (those wrappers go away later on, but they are useful for separating
> > > changes).
> > > [3/5] Consolidate ACPI hotplug signaling for PCI and ACPI core.
> > > [4/5] Simplify notify handle registration wrapper.
> > > [5/5] Dispatch ACPI hotplug notifications for "core" devices and PCI from acpi_bus_notify().
> >
> > Unfortunately, I realized that patches [3-5/5] were buggy. The bugs were
> > kind of subtle and might not be easy to reproduce, but they were bugs anyway. :-)
> >
> > A respin of the whole series follows.
>
> After the Mika's testing it turned out that they were more buggy than I had
> though. Oh well.
>
> The following patchset is a reworked version of the previous one. Functionality-wise
> the final result should be very similar, but not exactly the same.
>
> [1/6] Fix a theoretical race condition in acpi_hotplug_notify_cb().
> [2/6] Move the hotplug context lock definition to the ACPI core (from ACPIPHP).
> [3/6] Consolidate ACPI hotplug signaling for PCI and ACPI core (this is a combination
> of patches [1-3/5] from the previous series).
> [4/6] Rework the handling of eject requests in the ACPI core.
> [5/6] Simplify a routine for installing hotplug notify handlers.
> [6/6] Dispatch ACPI hotplug notifications for "core" devices and PCI from acpi_bus_notify().
>
> This is on top of https://lkml.org/lkml/2014/2/1/123 which in turn is on top of
> the current mainline.
>
> For the adventurous all this stuff is on the test-next branch of linux-pm.git.

As stated in the message at http://marc.info/?l=linux-acpi&m=139135963030012&w=4 ,
patch [1/6] was actaully wrong and the whole patchset had to be reworked for that
reason. What follows is an entirely new version:

[1/7] Add a new function to ACPICA allowing a callback to be executed under the
namespace mutex after calling acpi_ns_get_attached_data().

[2/7] Use the new ACPICA's function to fix a couple of potential races related
to ACPI notifies.

[3/7] Same as [2/6] above.
[4/7] Same as [3/6] above, rebased.
[5/7] Same as [4/6] above.
[6/7] Same as [5/6] above.
[7/7] Dispatch ACPI hotplug notifications for "core" devices and PCI from
acpi_bus_notify(). This actually is different from [6/6] above, although
it serves the same purpose.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-02 17:07:23

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v3 3/7] ACPI / hotplug / PCI: Define hotplug context lock in the core

From: Rafael J. Wysocki <[email protected]>

Subsequent changes will require the ACPI core to acquire the lock
protecting the ACPIPHP hotplug contexts, so move the definition of
the lock to the core and change its name to be more generic.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 11 +++++++++
drivers/pci/hotplug/acpiphp_glue.c | 41 ++++++++++++++++++-------------------
include/acpi/acpi_bus.h | 2 +
3 files changed, 33 insertions(+), 21 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -41,6 +41,7 @@ static DEFINE_MUTEX(acpi_scan_lock);
static LIST_HEAD(acpi_scan_handlers_list);
DEFINE_MUTEX(acpi_device_lock);
LIST_HEAD(acpi_wakeup_device_list);
+static DEFINE_MUTEX(acpi_hp_context_lock);

struct acpi_device_bus_id{
char bus_id[15];
@@ -60,6 +61,16 @@ void acpi_scan_lock_release(void)
}
EXPORT_SYMBOL_GPL(acpi_scan_lock_release);

+void acpi_lock_hp_context(void)
+{
+ mutex_lock(&acpi_hp_context_lock);
+}
+
+void acpi_unlock_hp_context(void)
+{
+ mutex_unlock(&acpi_hp_context_lock);
+}
+
int acpi_scan_add_handler(struct acpi_scan_handler *handler)
{
if (!handler || !handler->attach)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -58,7 +58,6 @@

static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
-static DEFINE_MUTEX(acpiphp_context_lock);

static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
@@ -75,7 +74,7 @@ static void acpiphp_context_handler(acpi
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @adev: ACPI device object to create the context for.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
@@ -100,7 +99,7 @@ static struct acpiphp_context *acpiphp_i
* acpiphp_get_context - Get hotplug context and grab a reference to it.
* @handle: ACPI object handle to get the context for.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
{
@@ -122,7 +121,7 @@ static struct acpiphp_context *acpiphp_g
*
* The context object is removed if there are no more references to it.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static void acpiphp_put_context(struct acpiphp_context *context)
{
@@ -151,7 +150,7 @@ static void free_bridge(struct kref *kre
struct acpiphp_slot *slot, *next;
struct acpiphp_func *func, *tmp;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();

bridge = container_of(kref, struct acpiphp_bridge, ref);

@@ -175,7 +174,7 @@ static void free_bridge(struct kref *kre
pci_dev_put(bridge->pci_dev);
kfree(bridge);

- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
}

/*
@@ -291,17 +290,17 @@ static acpi_status register_slot(acpi_ha
device = (adr >> 16) & 0xffff;
function = adr & 0xffff;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_init_context(adev);
if (!context) {
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
acpi_handle_err(handle, "No hotplug context\n");
return AE_NOT_EXIST;
}
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -319,9 +318,9 @@ static acpi_status register_slot(acpi_ha

slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
acpiphp_put_context(context);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return AE_NO_MEMORY;
}

@@ -396,7 +395,7 @@ static struct acpiphp_bridge *acpiphp_ha
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (context) {
bridge = context->bridge;
@@ -405,7 +404,7 @@ static struct acpiphp_bridge *acpiphp_ha

acpiphp_put_context(context);
}
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return bridge;
}

@@ -796,12 +795,12 @@ static void hotplug_event(u32 type, void
acpi_handle handle = context->adev->handle;
struct acpiphp_bridge *bridge;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
bridge = context->bridge;
if (bridge)
get_bridge(bridge);

- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();

pci_lock_rescan_remove();

@@ -902,16 +901,16 @@ static void handle_hotplug_event(acpi_ha
goto out;
}

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (context && !WARN_ON(context->adev->handle != handle)) {
get_bridge(context->func.parent);
acpiphp_put_context(context);
acpi_hotplug_execute(hotplug_event_work, context, type);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return;
}
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

out:
@@ -967,10 +966,10 @@ void acpiphp_enumerate_slots(struct pci_
* parent is going to be handled by pciehp, in which case this
* bridge is not interesting to us either.
*/
- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (!context) {
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
put_device(&bus->dev);
pci_dev_put(bridge->pci_dev);
kfree(bridge);
@@ -980,7 +979,7 @@ void acpiphp_enumerate_slots(struct pci_
context->bridge = bridge;
/* Get a reference to the parent bridge. */
get_bridge(context->func.parent);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
}

/* must be added to the list prior to calling register_slot */
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -402,6 +402,8 @@ static inline bool acpi_bus_can_wakeup(a

void acpi_scan_lock_acquire(void);
void acpi_scan_lock_release(void);
+void acpi_lock_hp_context(void);
+void acpi_unlock_hp_context(void);
int acpi_scan_add_handler(struct acpi_scan_handler *handler);
int acpi_bus_register_driver(struct acpi_driver *driver);
void acpi_bus_unregister_driver(struct acpi_driver *driver);

2014-02-02 17:07:19

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v3 1/7] ACPICA: Introduce acpi_get_data_full() and rework acpi_get_data()

From: Rafael J. Wysocki <[email protected]>

Introduce a new function, acpi_get_data_full(), working in analogy
with acpi_get_data() except that it can execute a callback provided
as its 4th argument right after acpi_ns_get_attached_data() has
returned a success.

That will allow Linux to reference count the object pointed to by
*data before the namespace mutex is released so as to ensure that it
will not be freed going forward until the reference to it acquired
by acpi_get_data_full() is dropped.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/acpica/nsxfeval.c | 33 ++++++++++++++++++++++++++++++---
include/acpi/acpixf.h | 4 ++++
2 files changed, 34 insertions(+), 3 deletions(-)

Index: linux-pm/drivers/acpi/acpica/nsxfeval.c
===================================================================
--- linux-pm.orig/drivers/acpi/acpica/nsxfeval.c
+++ linux-pm/drivers/acpi/acpica/nsxfeval.c
@@ -923,19 +923,22 @@ ACPI_EXPORT_SYMBOL(acpi_detach_data)

/*******************************************************************************
*
- * FUNCTION: acpi_get_data
+ * FUNCTION: acpi_get_data_full
*
* PARAMETERS: obj_handle - Namespace node
* handler - Handler used in call to attach_data
* data - Where the data is returned
+ * callback - function to execute before returning
*
* RETURN: Status
*
- * DESCRIPTION: Retrieve data that was previously attached to a namespace node.
+ * DESCRIPTION: Retrieve data that was previously attached to a namespace node
+ * and execute a callback before returning.
*
******************************************************************************/
acpi_status
-acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data)
+acpi_get_data_full(acpi_handle obj_handle, acpi_object_handler handler,
+ void **data, void (*callback)(void *))
{
struct acpi_namespace_node *node;
acpi_status status;
@@ -960,10 +963,34 @@ acpi_get_data(acpi_handle obj_handle, ac
}

status = acpi_ns_get_attached_data(node, handler, data);
+ if (ACPI_SUCCESS(status) && callback) {
+ callback(*data);
+ }

unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return (status);
}

+ACPI_EXPORT_SYMBOL(acpi_get_data_full)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_data
+ *
+ * PARAMETERS: obj_handle - Namespace node
+ * handler - Handler used in call to attach_data
+ * data - Where the data is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieve data that was previously attached to a namespace node.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data)
+{
+ return acpi_get_data_full(obj_handle, handler, data, NULL);
+}
+
ACPI_EXPORT_SYMBOL(acpi_get_data)
Index: linux-pm/include/acpi/acpixf.h
===================================================================
--- linux-pm.orig/include/acpi/acpixf.h
+++ linux-pm/include/acpi/acpixf.h
@@ -230,6 +230,10 @@ acpi_attach_data(acpi_handle object, acp
acpi_status acpi_detach_data(acpi_handle object, acpi_object_handler handler);

acpi_status
+acpi_get_data_full(acpi_handle object, acpi_object_handler handler, void **data,
+ void (*callback)(void *));
+
+acpi_status
acpi_get_data(acpi_handle object, acpi_object_handler handler, void **data);

acpi_status

2014-02-02 17:07:17

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v3 2/7] ACPI / hotplug: Fix potential races in notify handlers

From: Rafael J. Wysocki <[email protected]>

There is a slight possibility for the ACPI device object pointed to
by adev in acpi_hotplug_notify_cb() to become invalid between the
acpi_bus_get_device() that it comes from and the subsequent dereference
of that pointer under get_device(). Namely, if acpi_scan_drop_device()
runs in parallel with acpi_hotplug_notify_cb(), acpi_device_del_work_fn()
queued up by it may delete the device object in question right after
a successful execution of acpi_bus_get_device() in acpi_bus_notify().

An analogous problem is present in acpi_bus_notify() where the device
pointer coming from acpi_bus_get_device() may become invalid before
it subsequent dereference in the "if" block.

To prevent that from happening, introduce a new function,
acpi_bus_get_acpi_device(), working analogously to acpi_bus_get_device()
except that it will grab a reference to the ACPI device object returned
by it and it will do that under the ACPICA's namespace mutex. Then,
make both acpi_hotplug_notify_cb() and acpi_bus_notify() use
acpi_bus_get_acpi_device() instead of acpi_bus_get_device() so as to
ensure that the pointers used by them will not become stale at one
point.

In addition to that, introduce acpi_bus_put_acpi_device() as a wrapper
around put_device() to be used along with acpi_bus_get_acpi_device()
and make the (new) users of the latter use acpi_bus_put_acpi_device()
too.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/bus.c | 4 +++-
drivers/acpi/internal.h | 2 ++
drivers/acpi/scan.c | 38 ++++++++++++++++++++++++++++++--------
3 files changed, 35 insertions(+), 9 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -476,7 +476,7 @@ static void acpi_device_hotplug(void *da

out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
- put_device(&adev->dev);
+ acpi_bus_put_acpi_device(adev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
}
@@ -488,9 +488,6 @@ static void acpi_hotplug_notify_cb(acpi_
struct acpi_device *adev;
acpi_status status;

- if (acpi_bus_get_device(handle, &adev))
- goto err_out;
-
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
@@ -512,12 +509,14 @@ static void acpi_hotplug_notify_cb(acpi_
/* non-hotplug event; possibly handled by other handler */
return;
}
- get_device(&adev->dev);
+ if (acpi_bus_get_acpi_device(handle, &adev))
+ goto err_out;
+
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
return;

- put_device(&adev->dev);
+ acpi_bus_put_acpi_device(adev);

err_out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
@@ -1112,14 +1111,16 @@ static void acpi_scan_drop_device(acpi_h
mutex_unlock(&acpi_device_del_lock);
}

-int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
+static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device,
+ void (*callback)(void *))
{
acpi_status status;

if (!device)
return -EINVAL;

- status = acpi_get_data(handle, acpi_scan_drop_device, (void **)device);
+ status = acpi_get_data_full(handle, acpi_scan_drop_device,
+ (void **)device, callback);
if (ACPI_FAILURE(status) || !*device) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
handle));
@@ -1127,8 +1128,29 @@ int acpi_bus_get_device(acpi_handle hand
}
return 0;
}
+
+int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
+{
+ return acpi_get_device_data(handle, device, NULL);
+}
EXPORT_SYMBOL(acpi_bus_get_device);

+static void get_acpi_device(void *dev)
+{
+ if (dev)
+ get_device(&((struct acpi_device *)dev)->dev);
+}
+
+int acpi_bus_get_acpi_device(acpi_handle handle, struct acpi_device **adev_p)
+{
+ return acpi_get_device_data(handle, adev_p, get_acpi_device);
+}
+
+void acpi_bus_put_acpi_device(struct acpi_device *adev)
+{
+ put_device(&adev->dev);
+}
+
int acpi_device_add(struct acpi_device *device,
void (*release)(struct device *))
{
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -81,6 +81,8 @@ bool acpi_scan_is_offline(struct acpi_de
#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING)

+int acpi_bus_get_acpi_device(acpi_handle handle, struct acpi_device **adev_p);
+void acpi_bus_put_acpi_device(struct acpi_device *adev);
int acpi_device_add(struct acpi_device *device,
void (*release)(struct device *));
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -387,12 +387,14 @@ static void acpi_bus_notify(acpi_handle
break;
}

- acpi_bus_get_device(handle, &device);
+ acpi_bus_get_acpi_device(handle, &device);
if (device) {
driver = device->driver;
if (driver && driver->ops.notify &&
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
driver->ops.notify(device, type);
+
+ acpi_bus_put_acpi_device(device);
}
}

2014-02-02 17:07:16

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v3 4/7] ACPI / hotplug / PCI: Consolidate ACPIPHP with ACPI core hotplug

From: Rafael J. Wysocki <[email protected]>

The ACPI-based PCI hotplug (ACPIPHP) code currently attaches its
hotplug context objects directly to ACPI namespace nodes representing
hotplug devices. However, after recent changes causing struct
acpi_device to be created for every namespace node representing a
device (regardless of its status), that is not necessary any more.
Moreover, it's vulnerable to a theoretical issue that the ACPI
handle passed in the context between handle_hotplug_event() and
hotplug_event_work() may become invalid in the meantime (as a
result of a concurrent table unload).

In principle, this issue might be addressed by adding a non-empty
release handler for ACPIPHP hotplug context objects analogous to
acpi_scan_drop_device(), but that would duplicate the code in that
function and in acpi_device_del_work_fn(). For this reason, it's
better to modify ACPIPHP to attach its device hotplug contexts to
struct device objects representing hotplug devices and make it
use acpi_hotplug_notify_cb() as its notify handler. At the same
time, acpi_device_hotplug() can be modified to dispatch the new
.hp.event() callback pointing to acpiphp_hotplug_event() from ACPI
device objects associated with PCI devices and use the generic
ACPI device hotplug code for device objects with scan handlers
attached to them.

This allows the existing code duplication between ACPIPHP and the
ACPI core to be reduced too and makes further ACPI-based device
hotplug consolidation possible.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 106 +++++++++++++++++++++-------
drivers/pci/hotplug/acpiphp.h | 9 +-
drivers/pci/hotplug/acpiphp_glue.c | 136 +++++++------------------------------
include/acpi/acpi_bus.h | 22 +++++
4 files changed, 136 insertions(+), 137 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -450,43 +450,61 @@ static int acpi_scan_bus_check(struct ac
return 0;
}

+static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
+{
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ return acpi_scan_bus_check(adev);
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ return acpi_scan_device_check(adev);
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ case ACPI_OST_EC_OSPM_EJECT:
+ return acpi_scan_hot_remove(adev);
+ }
+ return -EINVAL;
+}
+
static void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
- int error;
+ int error = -ENODEV;

lock_device_hotplug();
mutex_lock(&acpi_scan_lock);

/*
* The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it may have become invalid before
+ * are holding acpi_scan_lock, but it might have become invalid before
* that lock was acquired.
*/
if (adev->handle == INVALID_ACPI_HANDLE)
- goto out;
+ goto err_out;

- switch (src) {
- case ACPI_NOTIFY_BUS_CHECK:
- error = acpi_scan_bus_check(adev);
- break;
- case ACPI_NOTIFY_DEVICE_CHECK:
- error = acpi_scan_device_check(adev);
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- case ACPI_OST_EC_OSPM_EJECT:
- error = acpi_scan_hot_remove(adev);
- break;
- default:
- error = -EINVAL;
- break;
+ if (adev->handler) {
+ error = acpi_generic_hotplug_event(adev, src);
+ } else {
+ int (*event)(struct acpi_device *, u32);
+
+ acpi_lock_hp_context();
+ event = adev->hp ? adev->hp->event : NULL;
+ acpi_unlock_hp_context();
+ /*
+ * There may be additional notify handlers for device objects
+ * without the .event() callback, so ignore them here.
+ */
+ if (event)
+ error = event(adev, src);
+ else
+ goto out;
}
if (!error)
ost_code = ACPI_OST_SC_SUCCESS;

- out:
+ err_out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
+
+ out:
acpi_bus_put_acpi_device(adev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
@@ -494,8 +512,8 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_scan_handler *handler = data;
+ u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;

@@ -503,25 +521,48 @@ static void acpi_hotplug_notify_cb(acpi_
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;
+
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;
+
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (!handler->hotplug.enabled) {
+ if (handler && !handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
- goto err_out;
+ goto out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
- default:
- /* non-hotplug event; possibly handled by other handler */
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
return;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
+ goto out;
+
+ default:
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto out;
}
+
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
if (acpi_bus_get_acpi_device(handle, &adev))
- goto err_out;
+ goto out;

status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
@@ -529,10 +570,22 @@ static void acpi_hotplug_notify_cb(acpi_

acpi_bus_put_acpi_device(adev);

- err_out:
+ out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+{
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb, data);
+}
+
+void acpi_remove_hotplug_notify_handler(acpi_handle handle)
+{
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb);
+}
+
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1991,8 +2044,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, handler);
+ acpi_install_hotplug_notify_handler(handle, handler);
break;
}
}
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -116,12 +116,17 @@ struct acpiphp_func {
};

struct acpiphp_context {
+ struct acpi_hotplug_context hp;
struct acpiphp_func func;
- struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};

+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *fu

static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
{
- return func_to_context(func)->adev;
+ return func_to_context(func)->hp.self;
}

static inline acpi_handle func_to_handle(struct acpiphp_func *func)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -59,17 +59,12 @@
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);

-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, void *data);
static void free_bridge(struct kref *kref);

-static void acpiphp_context_handler(acpi_handle handle, void *context)
-{
- /* Intentionally empty. */
-}
-
/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @adev: ACPI device object to create the context for.
@@ -79,39 +74,27 @@ static void acpiphp_context_handler(acpi
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
- acpi_status status;

context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;

- context->adev = adev;
context->refcount = 1;
- status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
- if (ACPI_FAILURE(status)) {
- kfree(context);
- return NULL;
- }
+ acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event);
return context;
}

/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
- * @handle: ACPI object handle to get the context for.
+ * @adev: ACPI device object to get the context for.
*
* Call under acpi_hp_context_lock.
*/
-static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- struct acpiphp_context *context = NULL;
- acpi_status status;
- void *data;
+ struct acpiphp_context *context = to_acpiphp_context(adev->hp);

- status = acpi_get_data(handle, acpiphp_context_handler, &data);
- if (ACPI_SUCCESS(status)) {
- context = data;
- context->refcount++;
- }
+ context->refcount++;
return context;
}

@@ -129,7 +112,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->adev->handle, acpiphp_context_handler);
+ context->hp.self->hp = NULL;
kfree(context);
}

@@ -378,14 +361,8 @@ static acpi_status register_slot(acpi_ha
}

/* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK)) {
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event,
- context);
- if (ACPI_FAILURE(status))
- acpi_handle_err(handle,
- "failed to install notify handler\n");
- }
+ if (!(newfunc->flags & FUNC_HAS_DCK))
+ acpi_install_hotplug_notify_handler(handle, NULL);

return AE_OK;
}
@@ -394,9 +371,13 @@ static struct acpiphp_bridge *acpiphp_ha
{
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;
+ struct acpi_device *adev;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return NULL;

acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (context) {
bridge = context->bridge;
if (bridge)
@@ -412,7 +393,6 @@ static void cleanup_bridge(struct acpiph
{
struct acpiphp_slot *slot;
struct acpiphp_func *func;
- acpi_status status;

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
@@ -421,13 +401,8 @@ static void cleanup_bridge(struct acpiph
if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);

- if (!(func->flags & FUNC_HAS_DCK)) {
- status = acpi_remove_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event);
- if (ACPI_FAILURE(status))
- pr_err("failed to remove notify handler\n");
- }
+ if (!(func->flags & FUNC_HAS_DCK))
+ acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
@@ -792,7 +767,7 @@ static void hotplug_event(u32 type, void
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
- acpi_handle handle = context->adev->handle;
+ acpi_handle handle = context->hp.self->handle;
struct acpiphp_bridge *bridge;

acpi_lock_hp_context();
@@ -842,79 +817,24 @@ static void hotplug_event(u32 type, void
put_bridge(bridge);
}

-static void hotplug_event_work(void *data, u32 type)
-{
- struct acpiphp_context *context = data;
-
- acpi_scan_lock_acquire();
-
- hotplug_event(type, context);
-
- acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(context->adev->handle, type,
- ACPI_OST_SC_SUCCESS, NULL);
- put_bridge(context->func.parent);
-}
-
-/**
- * handle_hotplug_event - handle ACPI hotplug event
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @data: pointer to acpiphp_context structure
- *
- * Handles ACPI event notification on slots.
- */
-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{
struct acpiphp_context *context;
- u32 ost_code = ACPI_OST_SC_SUCCESS;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- case ACPI_NOTIFY_DEVICE_CHECK:
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS;
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }

acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
- if (context && !WARN_ON(context->adev->handle != handle)) {
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_hotplug_execute(hotplug_event_work, context, type);
+ context = acpiphp_get_context(adev);
+ if (!context) {
acpi_unlock_hp_context();
- return;
+ return -ENODATA;
}
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
acpi_unlock_hp_context();
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
+ hotplug_event(type, context);
+
+ put_bridge(context->func.parent);
+ return 0;
}

/**
@@ -967,7 +887,7 @@ void acpiphp_enumerate_slots(struct pci_
* bridge is not interesting to us either.
*/
acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (!context) {
acpi_unlock_hp_context();
put_device(&bus->dev);
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -137,6 +137,16 @@ struct acpi_scan_handler {
};

/*
+ * ACPI Hotplug Context
+ * --------------------
+ */
+
+struct acpi_hotplug_context {
+ struct acpi_device *self;
+ int (*event)(struct acpi_device *, u32);
+};
+
+/*
* ACPI Driver
* -----------
*/
@@ -329,6 +339,7 @@ struct acpi_device {
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_scan_handler *handler;
+ struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
void *driver_data;
struct device dev;
@@ -351,6 +362,15 @@ static inline void acpi_set_device_statu
*((u32 *)&adev->status) = sta;
}

+static inline void acpi_set_hp_context(struct acpi_device *adev,
+ struct acpi_hotplug_context *hp,
+ int (*event)(struct acpi_device *, u32))
+{
+ hp->self = adev;
+ hp->event = event;
+ adev->hp = hp;
+}
+
/* acpi_device.dev.bus == &acpi_bus_type */
extern struct bus_type acpi_bus_type;

@@ -423,6 +443,8 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver

2014-02-02 17:07:13

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v3 5/7] ACPI / hotplug / PCI: Rework the handling of eject requests

From: Rafael J. Wysocki <[email protected]>

To avoid the need to install a hotplug notify handler for each ACPI
namespace node representing a device and having a matching scan
handler, move the check whether or not the ejection of the given
device is enabled through its scan handler from acpi_hotplug_notify_cb()
to acpi_generic_hotplug_event(). Also, move the execution of
ACPI_OST_SC_EJECT_IN_PROGRESS _OST to acpi_generic_hotplug_event(),
because in acpi_hotplug_notify_cb() or in acpi_eject_store() we really
don't know whether or not the eject is going to be in progress (for
example, acpi_hotplug_execute() may still fail without queuing up the
work item).

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -459,6 +459,12 @@ static int acpi_generic_hotplug_event(st
return acpi_scan_device_check(adev);
case ACPI_NOTIFY_EJECT_REQUEST:
case ACPI_OST_EC_OSPM_EJECT:
+ if (adev->handler && !adev->handler->hotplug.enabled) {
+ dev_info(&adev->dev, "Eject disabled\n");
+ return -EPERM;
+ }
+ acpi_evaluate_hotplug_ost(adev->handle, ACPI_NOTIFY_EJECT_REQUEST,
+ ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
return acpi_scan_hot_remove(adev);
}
return -EINVAL;
@@ -483,6 +489,10 @@ static void acpi_device_hotplug(void *da

if (adev->handler) {
error = acpi_generic_hotplug_event(adev, src);
+ if (error == -EPERM) {
+ ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
+ goto err_out;
+ }
} else {
int (*event)(struct acpi_device *, u32);

@@ -512,7 +522,6 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- struct acpi_scan_handler *handler = data;
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;
@@ -528,13 +537,6 @@ static void acpi_hotplug_notify_cb(acpi_

case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (handler && !handler->hotplug.enabled) {
- acpi_handle_err(handle, "Eject disabled\n");
- 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);
break;

case ACPI_NOTIFY_DEVICE_WAKE:
@@ -631,8 +633,6 @@ acpi_eject_store(struct device *d, struc
if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
return -ENODEV;

- acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
- ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
get_device(&acpi_device->dev);
status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device,
ACPI_OST_EC_OSPM_EJECT);

2014-02-02 17:07:11

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v3 6/7] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

From: Rafael J. Wysocki <[email protected]>

Since acpi_hotplug_notify_cb() does not use its data argument any
more, the second argument of acpi_install_hotplug_notify_handler()
can be dropped, so do that and update its callers accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 6 +++---
drivers/pci/hotplug/acpiphp_glue.c | 2 +-
include/acpi/acpi_bus.h | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -576,10 +576,10 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+void acpi_install_hotplug_notify_handler(acpi_handle handle)
{
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, data);
+ acpi_hotplug_notify_cb, NULL);
}

void acpi_remove_hotplug_notify_handler(acpi_handle handle)
@@ -2044,7 +2044,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_hotplug_notify_handler(handle, handler);
+ acpi_install_hotplug_notify_handler(handle);
break;
}
}
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -362,7 +362,7 @@ static acpi_status register_slot(acpi_ha

/* install notify handler */
if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle, NULL);
+ acpi_install_hotplug_notify_handler(handle);

return AE_OK;
}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -443,7 +443,7 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_install_hotplug_notify_handler(acpi_handle handle);
void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**

2014-02-03 01:33:27

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH] ACPI / hotplug / PCI: Scan root bus under the PCI rescan-remove lock

From: Rafael J. Wysocki <[email protected]>

Since acpiphp_check_bridge() called by acpiphp_check_host_bridge()
does things that require PCI rescan-remove locking around it,
make acpiphp_check_host_bridge() use that locking.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---

One more thing I overlooked in the PCI rescan-remove locking patchset.

I just found it sitting there in a dark dusty corner and staring at me in
horror when I approached it with a vacuum cleaner ...

Anyway, 3.14-rc2 material on top of patches [1-2/13] from this series:
https://lkml.org/lkml/2014/2/1/123

Thanks,
Rafael

---
drivers/pci/hotplug/acpiphp_glue.c | 4 ++++
1 file changed, 4 insertions(+)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -829,7 +829,11 @@ void acpiphp_check_host_bridge(acpi_hand

bridge = acpiphp_handle_to_bridge(handle);
if (bridge) {
+ pci_lock_rescan_remove();
+
acpiphp_check_bridge(bridge);
+
+ pci_unlock_rescan_remove();
put_bridge(bridge);
}
}

2014-02-03 10:37:26

by Mika Westerberg

[permalink] [raw]
Subject: Re: [PATCH v2 0/13] ACPI / hotplug / PCI: Updates on top of changes merged recently

On Sun, Feb 02, 2014 at 01:19:33AM +0100, Rafael J. Wysocki wrote:
> On Monday, January 27, 2014 01:37:17 AM Rafael J. Wysocki wrote:
> > Hi All,
> >
> > ACPIPHP can be simplified a bit on top of some PCI and ACPI changes merged
> > recently and the following series of patches implements those simplifications:
> >
> > [1/11] Fix up two kerneldoc comments in acpiphp_glue.c.
> > [2/11] Get rid of an unnecessary label in register_slot().
> > [3/11] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
> > [4/11] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
> > [5/11] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
> > [6/11] Drop acpiphp_bus_add() (which has only one user).
> > [7/11] Drop crit_sect mutexes (that are redundant).
> > [8/11] Clean up the usage of the slot variable in hotplug_event().
> > [9/11] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
> > [10/11] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
> > [11/11] Drop handle argument from the member functions of struct acpi_dock_ops.
> >
> > All of that is relateively straightforward, but I have some more intrusive changes
> > on top of it in the works. They will be posted separately later this week.
>
> I've learned a couple of things since I sent this patchset. First, all
> bus->devices list walks that may remove PCI devices should be done in reverse
> order or they can crash if virtual functions are involved. Second, hotplug_event()
> (in acpiphp_glue.c) has to acquire pci_rescan_remove_lock by itself, because it
> may be called from multiple places and all of them need that lock to be held.
> That is done by patches [1-2/13] which I'm planning to push as fixes for 3.14-rc2.
>
> The rest is pretty much the same as last time except that the old patch [9/11]
> became [3/13] in this series and it has been changed so that the list is walked in
> reverse order.

On Intel NUC and DZ77RE-75K,

Tested-by: Mika Westerberg <[email protected]>

2014-02-03 10:38:09

by Mika Westerberg

[permalink] [raw]
Subject: Re: [PATCH v3 0/7] ACPI / hotplug / PCI: Consolidation of ACPIPHP with ACPI core device hotplug

On Sun, Feb 02, 2014 at 06:11:19PM +0100, Rafael J. Wysocki wrote:
> As stated in the message at http://marc.info/?l=linux-acpi&m=139135963030012&w=4 ,
> patch [1/6] was actaully wrong and the whole patchset had to be reworked for that
> reason. What follows is an entirely new version:
>
> [1/7] Add a new function to ACPICA allowing a callback to be executed under the
> namespace mutex after calling acpi_ns_get_attached_data().
>
> [2/7] Use the new ACPICA's function to fix a couple of potential races related
> to ACPI notifies.
>
> [3/7] Same as [2/6] above.
> [4/7] Same as [3/6] above, rebased.
> [5/7] Same as [4/6] above.
> [6/7] Same as [5/6] above.
> [7/7] Dispatch ACPI hotplug notifications for "core" devices and PCI from
> acpi_bus_notify(). This actually is different from [6/6] above, although
> it serves the same purpose.
>

On Intel NUC and DZ77RE-75K,

Tested-by: Mika Westerberg <[email protected]>

2014-02-03 21:36:43

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v3 0/7] ACPI / hotplug / PCI: Consolidation of ACPIPHP with ACPI core device hotplug

On Monday, February 03, 2014 12:45:11 PM Mika Westerberg wrote:
> On Sun, Feb 02, 2014 at 06:11:19PM +0100, Rafael J. Wysocki wrote:
> > As stated in the message at http://marc.info/?l=linux-acpi&m=139135963030012&w=4 ,
> > patch [1/6] was actaully wrong and the whole patchset had to be reworked for that
> > reason. What follows is an entirely new version:
> >
> > [1/7] Add a new function to ACPICA allowing a callback to be executed under the
> > namespace mutex after calling acpi_ns_get_attached_data().
> >
> > [2/7] Use the new ACPICA's function to fix a couple of potential races related
> > to ACPI notifies.
> >
> > [3/7] Same as [2/6] above.
> > [4/7] Same as [3/6] above, rebased.
> > [5/7] Same as [4/6] above.
> > [6/7] Same as [5/6] above.
> > [7/7] Dispatch ACPI hotplug notifications for "core" devices and PCI from
> > acpi_bus_notify(). This actually is different from [6/6] above, although
> > it serves the same purpose.
> >
>
> On Intel NUC and DZ77RE-75K,
>
> Tested-by: Mika Westerberg <[email protected]>

Thanks a lot for testing!

In the meantime, though, I found two more race conditions in ACPIPHP related to
races with sysfs-triggered device remove. I have patches to fix them, but then
I had to rebase this series (and the ACPIPHP one this is based on) on top of
those fixes.

I'm going to resend the two patchsets as one series including the fixes
mentioned above. I tried to avoid sending out a series of 20+ patches, because
then it's almost guaranteed that no one will look at them unless they happen to
break things in testing, but I guess I have no choice but to do that at this
point.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-03 23:28:34

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v2 0/24] ACPI / hotplug / PCI: ACPIPHP updates and consolidation with ACPI core

On Monday, February 03, 2014 12:44:08 PM Mika Westerberg wrote:
> On Sun, Feb 02, 2014 at 01:19:33AM +0100, Rafael J. Wysocki wrote:
> > On Monday, January 27, 2014 01:37:17 AM Rafael J. Wysocki wrote:
> > > Hi All,
> > >
> > > ACPIPHP can be simplified a bit on top of some PCI and ACPI changes merged
> > > recently and the following series of patches implements those simplifications:
> > >
> > > [1/11] Fix up two kerneldoc comments in acpiphp_glue.c.
> > > [2/11] Get rid of an unnecessary label in register_slot().
> > > [3/11] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
> > > [4/11] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
> > > [5/11] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
> > > [6/11] Drop acpiphp_bus_add() (which has only one user).
> > > [7/11] Drop crit_sect mutexes (that are redundant).
> > > [8/11] Clean up the usage of the slot variable in hotplug_event().
> > > [9/11] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
> > > [10/11] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
> > > [11/11] Drop handle argument from the member functions of struct acpi_dock_ops.
> > >
> > > All of that is relateively straightforward, but I have some more intrusive changes
> > > on top of it in the works. They will be posted separately later this week.
> >
> > I've learned a couple of things since I sent this patchset. First, all
> > bus->devices list walks that may remove PCI devices should be done in reverse
> > order or they can crash if virtual functions are involved. Second, hotplug_event()
> > (in acpiphp_glue.c) has to acquire pci_rescan_remove_lock by itself, because it
> > may be called from multiple places and all of them need that lock to be held.
> > That is done by patches [1-2/13] which I'm planning to push as fixes for 3.14-rc2.
> >
> > The rest is pretty much the same as last time except that the old patch [9/11]
> > became [3/13] in this series and it has been changed so that the list is walked in
> > reverse order.
>
> On Intel NUC and DZ77RE-75K,
>
> Tested-by: Mika Westerberg <[email protected]>

Thanks!

As I said previously, I have found two concurrency-related bugs more in ACPIPHP
and I needed to put fixes for those bugs at the top of the series (after previous
[1-2/13] and the patch at https://patchwork.kernel.org/patch/3567701/). For this
reason, I also had to rebase the ACPI/PCI hotplug consolidation patchset
(https://lkml.org/lkml/2014/2/2/87) which got a few additional cosmetic changes too.

The following is a combination of the two series with one patch dropped (it would
conflict with [4-5/24]) and a few patches added. It is on top of 3.14-rc1.

Patches [1-5/24], that are regarded as 3.14-rc2 material, are on the bleeding-edge
branch of linux-pm.git. The remaining patches will show up in bleeding-edge
shortly.

[1/24] Remove entries from bus->devices in reverse order (in ACPIPHP).
[2/24] Move PCI rescan-remove locking to hotplug_event().
[3/24] Scan root bus under the PCI rescan-remove lock
[4/24] Fix race in handle_hotplug_event() related to concurrent bridge removal.
[5/24] Fix race in hotplug_event() related to dock events and concurrent bridge removal.
[6/24] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
[7/24] Fix up two kerneldoc comments in acpiphp_glue.c.
[8/24] Get rid of an unnecessary label in register_slot().
[9/24] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
[10/24] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
[11/24] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
[12/24] Drop acpiphp_bus_add() (which has only one user).
[13/24] Drop crit_sect mutexes (that are redundant).
[14/24] Clean up the usage of the slot variable in hotplug_event().
[15/24] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
[16/24] Do not pass ACPI handle to hotplug_event().
[17/24] Add a new function to ACPICA allowing a callback to be executed under the
namespace mutex after calling acpi_ns_get_attached_data().
[18/24] Use the new ACPICA's function to fix a couple of potential races related
to ACPI notifies.
[19/24] Move the hotplug context lock definition to the ACPI core (from ACPIPHP).
[20/24] Consolidate ACPI hotplug signaling for PCI and ACPI core.
[21/24] Rework the handling of eject requests in the ACPI core.
[22/24] Simplify a routine for installing hotplug notify handlers.
[23/24] Dispatch ACPI hotplug notifications for "core" devices and PCI from
acpi_bus_notify().
[24/24] Pass struct acpi_device pointer to acpiphp_check_host_bridge().

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-03 23:28:33

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 2/24][Resend] ACPI / hotplug / PCI: Move PCI rescan-remove locking to hotplug_event()

From: Rafael J. Wysocki <[email protected]>

Commit 9217a984671e (ACPI / hotplug / PCI: Use global PCI rescan-remove
locking) modified ACPIPHP to protect its PCI device removal and addition
code paths from races against sysfs-driven rescan and remove operations
with the help of PCI rescan-remove locking. However, it overlooked the
fact that hotplug_event_work() is not the only caller of hotplug_event()
which may also be called by dock_hotplug_event() and that code path
is missing the PCI rescan-remove locking. This means that, although
the PCI rescan-remove lock is held as appropriate during the handling
of events originating from handle_hotplug_event(), the ACPIPHP's
operations resulting from dock events may still suffer the race
conditions that commit 9217a984671e was supposed to eliminate.

To address that problem, move the PCI rescan-remove locking from
hotplug_event_work() to hotplug_event() so that it is used regardless
of the way that function is invoked.

Revamps: 9217a984671e (ACPI / hotplug / PCI: Use global PCI rescan-remove locking)
Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -852,6 +852,7 @@ static void hotplug_event(acpi_handle ha

mutex_unlock(&acpiphp_context_lock);

+ pci_lock_rescan_remove();
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);

switch (type) {
@@ -905,6 +906,7 @@ static void hotplug_event(acpi_handle ha
break;
}

+ pci_unlock_rescan_remove();
if (bridge)
put_bridge(bridge);
}
@@ -915,11 +917,9 @@ static void hotplug_event_work(void *dat
acpi_handle handle = context->handle;

acpi_scan_lock_acquire();
- pci_lock_rescan_remove();

hotplug_event(handle, type, context);

- pci_unlock_rescan_remove();
acpi_scan_lock_release();
acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);

2014-02-03 23:28:32

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 4/24][New] ACPI / hotplug / PCI: Fix bridge removal race in handle_hotplug_event()

From: Rafael J. Wysocki <[email protected]>

If a PCI bridge with an ACPIPHP context attached is removed via
sysfs, the code path executed as a result is the following:

pci_stop_and_remove_bus_device_locked
pci_remove_bus
pcibios_remove_bus
acpi_pci_remove_bus
acpiphp_remove_slots
cleanup_bridge
put_bridge
free_bridge
acpiphp_put_context (for each child, under context lock)
kfree (child context)

Now, if a hotplug notify is dispatched for one of the bridge's
children and the timing is such that handle_hotplug_event() for
that notify is executed while free_bridge() above is running,
the get_bridge(context->func.parent) in handle_hotplug_event()
will not really help, because it is too late to prevent the bridge
from going away and the child's context may be freed before
hotplug_event_work() scheduled from handle_hotplug_event()
dereferences the pointer to it passed via the data argument.
That will cause a kernel crash to happpen in hotplug_event_work().

To prevent that from happening, make handle_hotplug_event()
check the is_going_away flag of the function's parent bridge
(under acpiphp_context_lock) and bail out if it's set. Also,
make cleanup_bridge() set the bridge's is_going_away flag under
acpiphp_context_lock so that it cannot be changed between the
check and the subsequent get_bridge(context->func.parent) in
handle_hotplug_event().

Then, in the above scenario, handle_hotplug_event() will notice
that context->func.parent->is_going_away is already set and it
will exit immediately preventing the crash from happening.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -441,7 +441,9 @@ static void cleanup_bridge(struct acpiph
list_del(&bridge->list);
mutex_unlock(&bridge_mutex);

+ mutex_lock(&acpiphp_context_lock);
bridge->is_going_away = true;
+ mutex_unlock(&acpiphp_context_lock);
}

/**
@@ -941,6 +943,7 @@ static void handle_hotplug_event(acpi_ha
{
struct acpiphp_context *context;
u32 ost_code = ACPI_OST_SC_SUCCESS;
+ acpi_status status;

switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
@@ -976,13 +979,20 @@ static void handle_hotplug_event(acpi_ha

mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
- if (context && !WARN_ON(context->handle != handle)) {
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_hotplug_execute(hotplug_event_work, context, type);
+ if (!context || WARN_ON(context->handle != handle)
+ || context->func.parent->is_going_away)
+ goto err_out;
+
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
+ status = acpi_hotplug_execute(hotplug_event_work, context, type);
+ if (ACPI_SUCCESS(status)) {
mutex_unlock(&acpiphp_context_lock);
return;
}
+ put_bridge(context->func.parent);
+
+ err_out:
mutex_unlock(&acpiphp_context_lock);
ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

2014-02-03 23:29:09

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 1/24][Resend] ACPI / hotplug / PCI: Remove entries from bus->devices in reverse order

From: Rafael J. Wysocki <[email protected]>

According to the changelog of commit 29ed1f29b68a (PCI: pciehp: Fix null
pointer deref when hot-removing SR-IOV device) it is unsafe to walk the
bus->devices list of a PCI bus and remove devices from it in direct order,
because that may lead to NULL pointer dereferences related to virtual
functions.

For this reason, change all of the bus->devices list walks in
acpiphp_glue.c during which devices may be removed to be carried out in
reverse order.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -742,7 +742,7 @@ static void trim_stale_devices(struct pc

/* The device is a bridge. so check the bus below it. */
pm_runtime_get_sync(&dev->dev);
- list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
+ list_for_each_entry_safe_reverse(child, tmp, &bus->devices, bus_list)
trim_stale_devices(child);

pm_runtime_put(&dev->dev);
@@ -773,8 +773,8 @@ static void acpiphp_check_bridge(struct
; /* do nothing */
} else if (get_slot_status(slot) == ACPI_STA_ALL) {
/* remove stale devices if any */
- list_for_each_entry_safe(dev, tmp, &bus->devices,
- bus_list)
+ list_for_each_entry_safe_reverse(dev, tmp,
+ &bus->devices, bus_list)
if (PCI_SLOT(dev->devfn) == slot->device)
trim_stale_devices(dev);

@@ -805,7 +805,7 @@ static void acpiphp_sanitize_bus(struct
int i;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM;

- list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
+ list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) {
for (i=0; i<PCI_BRIDGE_RESOURCES; i++) {
struct resource *res = &dev->resource[i];
if ((res->flags & type_mask) && !res->start &&

2014-02-03 23:29:59

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 3/24][Resend] ACPI / hotplug / PCI: Scan root bus under the PCI rescan-remove lock

From: Rafael J. Wysocki <[email protected]>

Since acpiphp_check_bridge() called by acpiphp_check_host_bridge()
does things that require PCI rescan-remove locking around it,
make acpiphp_check_host_bridge() use that locking.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 4 ++++
1 file changed, 4 insertions(+)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -829,7 +829,11 @@ void acpiphp_check_host_bridge(acpi_hand

bridge = acpiphp_handle_to_bridge(handle);
if (bridge) {
+ pci_lock_rescan_remove();
+
acpiphp_check_bridge(bridge);
+
+ pci_unlock_rescan_remove();
put_bridge(bridge);
}
}

2014-02-03 23:28:30

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 8/24][Resend] ACPI / hotplug / PCI: Simplify register_slot()

From: Rafael J. Wysocki <[email protected]>

The err label in register_slot() is only jumped to from one place,
so move the code under the label to that place and drop the label.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -335,8 +335,10 @@ static acpi_status register_slot(acpi_ha

slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
- status = AE_NO_MEMORY;
- goto err;
+ mutex_lock(&acpiphp_context_lock);
+ acpiphp_put_context(context);
+ mutex_unlock(&acpiphp_context_lock);
+ return AE_NO_MEMORY;
}

slot->bus = bridge->pci_bus;
@@ -404,12 +406,6 @@ static acpi_status register_slot(acpi_ha
}

return AE_OK;
-
- err:
- mutex_lock(&acpiphp_context_lock);
- acpiphp_put_context(context);
- mutex_unlock(&acpiphp_context_lock);
- return status;
}

static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)

2014-02-03 23:30:35

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 5/24][New] ACPI / hotplug / PCI: Fix bridge removal race vs dock events

From: Rafael J. Wysocki <[email protected]>

If a PCI bridge with an ACPIPHP context attached is removed via
sysfs, the code path executed as a result is the following:

pci_stop_and_remove_bus_device_locked
pci_remove_bus
pcibios_remove_bus
acpi_pci_remove_bus
acpiphp_remove_slots
cleanup_bridge
unregister_hotplug_dock_device (drops dock references to the bridge)
put_bridge
free_bridge
acpiphp_put_context (for each child, under context lock)
kfree (context)

Now, if a dock event affecting one of the bridge's child devices
occurs (roughly at the same time), it will lead to the following code
path:

acpi_dock_deferred_cb
dock_notify
handle_eject_request
hot_remove_dock_devices
dock_hotplug_event
hotplug_event (dereferences context)

That may lead to a kernel crash in hotplug_event() if it is executed
after the last kfree() in the bridge removal code path.

To prevent that from happening, add a wrapper around hotplug_event()
called dock_event() and point the .handler pointer in acpiphp_dock_ops
to it. Make that wrapper retrieve the device's ACPIPHP context using
acpiphp_get_context() (instead of taking it from the data argument)
under acpiphp_context_lock and check if the parent bridge's
is_going_away flag is set. If that flag is set, it will return
immediately and if it is not set it will grab a reference to the
device's parent bridge before executing hotplug_event().

Then, in the above scenario, the reference to the parent bridge
held by dock_event() will prevent free_bridge() from being executed
for it until hotplug_event() returns.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -210,10 +210,29 @@ static void post_dock_fixups(acpi_handle
}
}

+static void dock_event(acpi_handle handle, u32 type, void *data)
+{
+ struct acpiphp_context *context;
+
+ mutex_lock(&acpiphp_context_lock);
+ context = acpiphp_get_context(handle);
+ if (!context || WARN_ON(context->handle != handle)
+ || context->func.parent->is_going_away) {
+ mutex_unlock(&acpiphp_context_lock);
+ return;
+ }
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
+ mutex_unlock(&acpiphp_context_lock);
+
+ hotplug_event(handle, type, data);
+
+ put_bridge(context->func.parent);
+}

static const struct acpi_dock_ops acpiphp_dock_ops = {
.fixup = post_dock_fixups,
- .handler = hotplug_event,
+ .handler = dock_event,
};

/* Check whether the PCI device is managed by native PCIe hotplug driver */

2014-02-03 23:30:59

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 6/24][Resend] ACPI / hotplug / PCI: Simplify disable_slot()

From: Rafael J. Wysocki <[email protected]>

After recent PCI core changes related to the rescan/remove locking,
the ACPIPHP's disable_slot() function is only called under the
general PCI rescan/remove lock, so it doesn't have to use
dev_in_slot() any more to avoid race conditions. Make it simply
walk the devices on the bus and drop the ones in the slot being
disabled and drop dev_in_slot() which has no more users.

Moreover, to avoid problems described in the changelog of commit
29ed1f29b68a (PCI: pciehp: Fix null pointer deref when hot-removing
SR-IOV device), make disable_slot() carry out the list walk in
reverse order.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 28 +++++-----------------------
1 file changed, 5 insertions(+), 23 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -625,32 +625,15 @@ static void __ref enable_slot(struct acp
}
}

-/* return first device in slot, acquiring a reference on it */
-static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
-{
- struct pci_bus *bus = slot->bus;
- struct pci_dev *dev;
- struct pci_dev *ret = NULL;
-
- down_read(&pci_bus_sem);
- list_for_each_entry(dev, &bus->devices, bus_list)
- if (PCI_SLOT(dev->devfn) == slot->device) {
- ret = pci_dev_get(dev);
- break;
- }
- up_read(&pci_bus_sem);
-
- return ret;
-}
-
/**
* disable_slot - disable a slot
* @slot: ACPI PHP slot
*/
static void disable_slot(struct acpiphp_slot *slot)
{
+ struct pci_bus *bus = slot->bus;
+ struct pci_dev *dev, *prev;
struct acpiphp_func *func;
- struct pci_dev *pdev;

/*
* enable_slot() enumerates all functions in this device via
@@ -658,10 +641,9 @@ static void disable_slot(struct acpiphp_
* methods (_EJ0, etc.) or not. Therefore, we remove all functions
* here.
*/
- while ((pdev = dev_in_slot(slot))) {
- pci_stop_and_remove_bus_device(pdev);
- pci_dev_put(pdev);
- }
+ list_for_each_entry_safe_reverse(dev, prev, &bus->devices, bus_list)
+ if (PCI_SLOT(dev->devfn) == slot->device)
+ pci_stop_and_remove_bus_device(dev);

list_for_each_entry(func, &slot->funcs, sibling)
acpiphp_bus_trim(func_to_handle(func));

2014-02-03 23:31:22

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 7/24][Resend] ACPI / hotplug / PCI: Proper kerneldoc comments for enumeration/removal

From: Rafael J. Wysocki <[email protected]>

Add proper kerneldoc comments describing acpiphp_enumerate_slots()
and acpiphp_remove_slots().

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -1001,9 +1001,12 @@ static void handle_hotplug_event(acpi_ha
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-/*
- * Create hotplug slots for the PCI bus.
- * It should always return 0 to avoid skipping following notifiers.
+/**
+ * acpiphp_enumerate_slots - Enumerate PCI slots for a given bus.
+ * @bus: PCI bus to enumerate the slots for.
+ *
+ * A "slot" is an object associated with a PCI device number. All functions
+ * (PCI devices) with the same bus and device number belong to the same slot.
*/
void acpiphp_enumerate_slots(struct pci_bus *bus)
{
@@ -1076,7 +1079,10 @@ void acpiphp_enumerate_slots(struct pci_
}
}

-/* Destroy hotplug slots associated with the PCI bus */
+/**
+ * acpiphp_remove_slots - Remove slot objects associated with a given bus.
+ * @bus: PCI bus to remove the slot objects for.
+ */
void acpiphp_remove_slots(struct pci_bus *bus)
{
struct acpiphp_bridge *bridge;

2014-02-03 23:28:27

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 19/24][Update] ACPI / hotplug / PCI: Define hotplug context lock in the core

From: Rafael J. Wysocki <[email protected]>

Subsequent changes will require the ACPI core to acquire the lock
protecting the ACPIPHP hotplug contexts, so move the definition of
the lock to the core and change its name to be more generic.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 11 +++++++
drivers/pci/hotplug/acpiphp_glue.c | 51 ++++++++++++++++++-------------------
include/acpi/acpi_bus.h | 2 +
3 files changed, 38 insertions(+), 26 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -41,6 +41,7 @@ static DEFINE_MUTEX(acpi_scan_lock);
static LIST_HEAD(acpi_scan_handlers_list);
DEFINE_MUTEX(acpi_device_lock);
LIST_HEAD(acpi_wakeup_device_list);
+static DEFINE_MUTEX(acpi_hp_context_lock);

struct acpi_device_bus_id{
char bus_id[15];
@@ -60,6 +61,16 @@ void acpi_scan_lock_release(void)
}
EXPORT_SYMBOL_GPL(acpi_scan_lock_release);

+void acpi_lock_hp_context(void)
+{
+ mutex_lock(&acpi_hp_context_lock);
+}
+
+void acpi_unlock_hp_context(void)
+{
+ mutex_unlock(&acpi_hp_context_lock);
+}
+
int acpi_scan_add_handler(struct acpi_scan_handler *handler)
{
if (!handler || !handler->attach)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -58,7 +58,6 @@

static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
-static DEFINE_MUTEX(acpiphp_context_lock);

static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
@@ -75,7 +74,7 @@ static void acpiphp_context_handler(acpi
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @adev: ACPI device object to create the context for.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
@@ -100,7 +99,7 @@ static struct acpiphp_context *acpiphp_i
* acpiphp_get_context - Get hotplug context and grab a reference to it.
* @handle: ACPI object handle to get the context for.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
{
@@ -122,7 +121,7 @@ static struct acpiphp_context *acpiphp_g
*
* The context object is removed if there are no more references to it.
*
- * Call under acpiphp_context_lock.
+ * Call under acpi_hp_context_lock.
*/
static void acpiphp_put_context(struct acpiphp_context *context)
{
@@ -151,7 +150,7 @@ static void free_bridge(struct kref *kre
struct acpiphp_slot *slot, *next;
struct acpiphp_func *func, *tmp;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();

bridge = container_of(kref, struct acpiphp_bridge, ref);

@@ -175,7 +174,7 @@ static void free_bridge(struct kref *kre
pci_dev_put(bridge->pci_dev);
kfree(bridge);

- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
}

/*
@@ -214,16 +213,16 @@ static void dock_event(acpi_handle handl
{
struct acpiphp_context *context;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (!context || WARN_ON(context->adev->handle != handle)
|| context->func.parent->is_going_away) {
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return;
}
get_bridge(context->func.parent);
acpiphp_put_context(context);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();

hotplug_event(type, context);

@@ -310,17 +309,17 @@ static acpi_status register_slot(acpi_ha
device = (adr >> 16) & 0xffff;
function = adr & 0xffff;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_init_context(adev);
if (!context) {
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
acpi_handle_err(handle, "No hotplug context\n");
return AE_NOT_EXIST;
}
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -338,9 +337,9 @@ static acpi_status register_slot(acpi_ha

slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
acpiphp_put_context(context);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return AE_NO_MEMORY;
}

@@ -415,7 +414,7 @@ static struct acpiphp_bridge *acpiphp_ha
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (context) {
bridge = context->bridge;
@@ -424,7 +423,7 @@ static struct acpiphp_bridge *acpiphp_ha

acpiphp_put_context(context);
}
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return bridge;
}

@@ -458,9 +457,9 @@ static void cleanup_bridge(struct acpiph
list_del(&bridge->list);
mutex_unlock(&bridge_mutex);

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
bridge->is_going_away = true;
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
}

/**
@@ -820,12 +819,12 @@ static void hotplug_event(u32 type, stru
struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
bridge = context->bridge;
if (bridge)
get_bridge(bridge);

- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();

pci_lock_rescan_remove();

@@ -927,7 +926,7 @@ static void handle_hotplug_event(acpi_ha
goto out;
}

- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (!context || WARN_ON(context->adev->handle != handle)
|| context->func.parent->is_going_away)
@@ -937,13 +936,13 @@ static void handle_hotplug_event(acpi_ha
acpiphp_put_context(context);
status = acpi_hotplug_execute(hotplug_event_work, context, type);
if (ACPI_SUCCESS(status)) {
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
return;
}
put_bridge(context->func.parent);

err_out:
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

out:
@@ -999,10 +998,10 @@ void acpiphp_enumerate_slots(struct pci_
* parent is going to be handled by pciehp, in which case this
* bridge is not interesting to us either.
*/
- mutex_lock(&acpiphp_context_lock);
+ acpi_lock_hp_context();
context = acpiphp_get_context(handle);
if (!context) {
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
put_device(&bus->dev);
pci_dev_put(bridge->pci_dev);
kfree(bridge);
@@ -1012,7 +1011,7 @@ void acpiphp_enumerate_slots(struct pci_
context->bridge = bridge;
/* Get a reference to the parent bridge. */
get_bridge(context->func.parent);
- mutex_unlock(&acpiphp_context_lock);
+ acpi_unlock_hp_context();
}

/* must be added to the list prior to calling register_slot */
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -404,6 +404,8 @@ static inline bool acpi_bus_can_wakeup(a

void acpi_scan_lock_acquire(void);
void acpi_scan_lock_release(void);
+void acpi_lock_hp_context(void);
+void acpi_unlock_hp_context(void);
int acpi_scan_add_handler(struct acpi_scan_handler *handler);
int acpi_bus_register_driver(struct acpi_driver *driver);
void acpi_bus_unregister_driver(struct acpi_driver *driver);

2014-02-03 23:31:43

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 9/24][Resend] ACPI / hotplug / PCI: Drop acpiphp_bus_trim()

From: Rafael J. Wysocki <[email protected]>

If trim_stale_devices() calls acpi_bus_trim() directly, we can
save a potentially costly acpi_bus_get_device() invocation. After
making that change acpiphp_bus_trim() would only be called from one
place, so move the code from it to that place and drop it.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 30 +++++++++++-------------------
1 file changed, 11 insertions(+), 19 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -489,19 +489,6 @@ static unsigned char acpiphp_max_busnr(s
}

/**
- * acpiphp_bus_trim - Trim device objects in an ACPI namespace subtree.
- * @handle: ACPI device object handle to start from.
- */
-static void acpiphp_bus_trim(acpi_handle handle)
-{
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(handle, &adev);
- if (adev)
- acpi_bus_trim(adev);
-}
-
-/**
* acpiphp_bus_add - Scan ACPI namespace subtree.
* @handle: ACPI object handle to start the scan from.
*/
@@ -641,8 +628,12 @@ static void disable_slot(struct acpiphp_
if (PCI_SLOT(dev->devfn) == slot->device)
pci_stop_and_remove_bus_device(dev);

- list_for_each_entry(func, &slot->funcs, sibling)
- acpiphp_bus_trim(func_to_handle(func));
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev;
+
+ if (!acpi_bus_get_device(func_to_handle(func), &adev))
+ acpi_bus_trim(adev);
+ }

slot->flags &= (~SLOT_ENABLED);
}
@@ -714,11 +705,12 @@ static unsigned int get_slot_status(stru
*/
static void trim_stale_devices(struct pci_dev *dev)
{
- acpi_handle handle = ACPI_HANDLE(&dev->dev);
+ struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
struct pci_bus *bus = dev->subordinate;
bool alive = false;

- if (handle) {
+ if (adev) {
+ acpi_handle handle = adev->handle;
acpi_status status;
unsigned long long sta;

@@ -734,8 +726,8 @@ static void trim_stale_devices(struct pc
}
if (!alive) {
pci_stop_and_remove_bus_device(dev);
- if (handle)
- acpiphp_bus_trim(handle);
+ if (adev)
+ acpi_bus_trim(adev);
} else if (bus) {
struct pci_dev *child, *tmp;

2014-02-03 23:32:39

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 10/24][Resend] ACPI / hotplug / PCI: Rework acpiphp_no_hotplug()

From: Rafael J. Wysocki <[email protected]>

If a struct acpi_device pointer is passed to acpiphp_no_hotplug()
instead of an ACPI handle, the function won't need to call
acpi_bus_get_device(), which may be costly, any more. Then,
trim_stale_devices() can call acpiphp_no_hotplug() passing
the struct acpi_device object it already has directly to that
function.

Make those changes and update slot_no_hotplug() accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -638,11 +638,8 @@ static void disable_slot(struct acpiphp_
slot->flags &= (~SLOT_ENABLED);
}

-static bool acpiphp_no_hotplug(acpi_handle handle)
+static bool acpiphp_no_hotplug(struct acpi_device *adev)
{
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(handle, &adev);
return adev && adev->flags.no_hotplug;
}

@@ -650,10 +647,13 @@ static bool slot_no_hotplug(struct acpip
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling)
- if (acpiphp_no_hotplug(func_to_handle(func)))
- return true;
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev = NULL;

+ acpi_bus_get_device(func_to_handle(func), &adev);
+ if (acpiphp_no_hotplug(adev))
+ return true;
+ }
return false;
}

@@ -710,13 +710,12 @@ static void trim_stale_devices(struct pc
bool alive = false;

if (adev) {
- acpi_handle handle = adev->handle;
acpi_status status;
unsigned long long sta;

- status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
alive = (ACPI_SUCCESS(status) && sta == ACPI_STA_ALL)
- || acpiphp_no_hotplug(handle);
+ || acpiphp_no_hotplug(adev);
}
if (!alive) {
u32 v;

2014-02-03 23:32:59

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 11/24][Update] ACPI / hotplug / PCI: Store acpi_device pointer in acpiphp_context

From: Rafael J. Wysocki <[email protected]>

After recent modifications of the ACPI core making it create a struct
acpi_device object for every namespace node representing a device
regardless of the current status of that device the ACPIPHP code
can store a struct acpi_device pointer instead of an ACPI handle
in struct acpiphp_context. This immediately makes it possible to
avoid making potentially costly calls to acpi_bus_get_device() in
two places and allows some more simplifications to be made going
forward.

The reason why that is correct is because ACPIPHP only installs
hotify handlers for namespace nodes that exist when
acpiphp_enumerate_slots() is called for their parent bridge.
That only happens if the parent bridge has an ACPI companion
associated with it, which means that the ACPI namespace scope
in question has been scanned already at that point. That, in
turn, means that struct acpi_device objects have been created
for all namespace nodes in that scope and pointers to those
objects can be stored directly instead of their ACPI handles.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp.h | 9 +++++--
drivers/pci/hotplug/acpiphp_glue.c | 46 +++++++++++++++++--------------------
2 files changed, 29 insertions(+), 26 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -117,8 +117,8 @@ struct acpiphp_func {
};

struct acpiphp_context {
- acpi_handle handle;
struct acpiphp_func func;
+ struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};
@@ -128,9 +128,14 @@ static inline struct acpiphp_context *fu
return container_of(func, struct acpiphp_context, func);
}

+static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
+{
+ return func_to_context(func)->adev;
+}
+
static inline acpi_handle func_to_handle(struct acpiphp_func *func)
{
- return func_to_context(func)->handle;
+ return func_to_acpi_device(func)->handle;
}

/*
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -73,11 +73,11 @@ static void acpiphp_context_handler(acpi

/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
- * @handle: ACPI object handle to create the context for.
+ * @adev: ACPI device object to create the context for.
*
* Call under acpiphp_context_lock.
*/
-static struct acpiphp_context *acpiphp_init_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
acpi_status status;
@@ -86,9 +86,9 @@ static struct acpiphp_context *acpiphp_i
if (!context)
return NULL;

- context->handle = handle;
+ context->adev = adev;
context->refcount = 1;
- status = acpi_attach_data(handle, acpiphp_context_handler, context);
+ status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
if (ACPI_FAILURE(status)) {
kfree(context);
return NULL;
@@ -118,7 +118,7 @@ static struct acpiphp_context *acpiphp_g

/**
* acpiphp_put_context - Drop a reference to ACPI hotplug context.
- * @handle: ACPI object handle to put the context for.
+ * @context: ACPI hotplug context to drop a reference to.
*
* The context object is removed if there are no more references to it.
*
@@ -130,7 +130,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->handle, acpiphp_context_handler);
+ acpi_detach_data(context->adev->handle, acpiphp_context_handler);
kfree(context);
}

@@ -216,7 +216,7 @@ static void dock_event(acpi_handle handl

mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
- if (!context || WARN_ON(context->handle != handle)
+ if (!context || WARN_ON(context->adev->handle != handle)
|| context->func.parent->is_going_away) {
mutex_unlock(&acpiphp_context_lock);
return;
@@ -284,6 +284,7 @@ static acpi_status register_slot(acpi_ha
{
struct acpiphp_bridge *bridge = data;
struct acpiphp_context *context;
+ struct acpi_device *adev;
struct acpiphp_slot *slot;
struct acpiphp_func *newfunc;
acpi_status status = AE_OK;
@@ -303,12 +304,14 @@ static acpi_status register_slot(acpi_ha
"can't evaluate _ADR (%#x)\n", status);
return AE_OK;
}
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;

device = (adr >> 16) & 0xffff;
function = adr & 0xffff;

mutex_lock(&acpiphp_context_lock);
- context = acpiphp_init_context(handle);
+ context = acpiphp_init_context(adev);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
acpi_handle_err(handle, "No hotplug context\n");
@@ -628,12 +631,8 @@ static void disable_slot(struct acpiphp_
if (PCI_SLOT(dev->devfn) == slot->device)
pci_stop_and_remove_bus_device(dev);

- list_for_each_entry(func, &slot->funcs, sibling) {
- struct acpi_device *adev;
-
- if (!acpi_bus_get_device(func_to_handle(func), &adev))
- acpi_bus_trim(adev);
- }
+ list_for_each_entry(func, &slot->funcs, sibling)
+ acpi_bus_trim(func_to_acpi_device(func));

slot->flags &= (~SLOT_ENABLED);
}
@@ -647,13 +646,10 @@ static bool slot_no_hotplug(struct acpip
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling) {
- struct acpi_device *adev = NULL;
-
- acpi_bus_get_device(func_to_handle(func), &adev);
- if (acpiphp_no_hotplug(adev))
+ list_for_each_entry(func, &slot->funcs, sibling)
+ if (acpiphp_no_hotplug(func_to_acpi_device(func)))
return true;
- }
+
return false;
}

@@ -908,7 +904,7 @@ static void hotplug_event(acpi_handle ha
static void hotplug_event_work(void *data, u32 type)
{
struct acpiphp_context *context = data;
- acpi_handle handle = context->handle;
+ acpi_handle handle = context->adev->handle;

acpi_scan_lock_acquire();

@@ -967,7 +963,7 @@ static void handle_hotplug_event(acpi_ha

mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
- if (!context || WARN_ON(context->handle != handle)
+ if (!context || WARN_ON(context->adev->handle != handle)
|| context->func.parent->is_going_away)
goto err_out;

@@ -998,16 +994,18 @@ static void handle_hotplug_event(acpi_ha
void acpiphp_enumerate_slots(struct pci_bus *bus)
{
struct acpiphp_bridge *bridge;
+ struct acpi_device *adev;
acpi_handle handle;
acpi_status status;

if (acpiphp_disabled)
return;

- handle = ACPI_HANDLE(bus->bridge);
- if (!handle)
+ adev = ACPI_COMPANION(bus->bridge);
+ if (!adev)
return;

+ handle = adev->handle;
bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
if (!bridge) {
acpi_handle_err(handle, "No memory for bridge object\n");

2014-02-03 23:33:21

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 12/24][Resend] ACPI / hotplug / PCI: Drop acpiphp_bus_add()

From: Rafael J. Wysocki <[email protected]>

acpiphp_bus_add() is only called from one place, so move the code out
of it into that place and drop it. Also make that code use
func_to_acpi_device() to get the struct acpi_device pointer it needs
instead of calling acpi_bus_get_device() which may be costly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 22 ++++++----------------
1 file changed, 6 insertions(+), 16 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -491,20 +491,6 @@ static unsigned char acpiphp_max_busnr(s
return max;
}

-/**
- * acpiphp_bus_add - Scan ACPI namespace subtree.
- * @handle: ACPI object handle to start the scan from.
- */
-static void acpiphp_bus_add(acpi_handle handle)
-{
- struct acpi_device *adev = NULL;
-
- acpi_bus_scan(handle);
- acpi_bus_get_device(handle, &adev);
- if (acpi_device_enumerated(adev))
- acpi_device_set_power(adev, ACPI_STATE_D0);
-}
-
static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
{
struct acpiphp_func *func;
@@ -544,9 +530,13 @@ static int acpiphp_rescan_slot(struct ac
{
struct acpiphp_func *func;

- list_for_each_entry(func, &slot->funcs, sibling)
- acpiphp_bus_add(func_to_handle(func));
+ list_for_each_entry(func, &slot->funcs, sibling) {
+ struct acpi_device *adev = func_to_acpi_device(func);

+ acpi_bus_scan(adev->handle);
+ if (acpi_device_enumerated(adev))
+ acpi_device_set_power(adev, ACPI_STATE_D0);
+ }
return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0));
}

2014-02-03 23:28:26

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 22/24][Resend] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

From: Rafael J. Wysocki <[email protected]>

Since acpi_hotplug_notify_cb() does not use its data argument any
more, the second argument of acpi_install_hotplug_notify_handler()
can be dropped, so do that and update its callers accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/acpi/scan.c | 6 +++---
drivers/pci/hotplug/acpiphp_glue.c | 2 +-
include/acpi/acpi_bus.h | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -577,10 +577,10 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+void acpi_install_hotplug_notify_handler(acpi_handle handle)
{
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, data);
+ acpi_hotplug_notify_cb, NULL);
}

void acpi_remove_hotplug_notify_handler(acpi_handle handle)
@@ -2048,7 +2048,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_hotplug_notify_handler(handle, handler);
+ acpi_install_hotplug_notify_handler(handle);
break;
}
}
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -372,7 +372,7 @@ static acpi_status register_slot(acpi_ha

/* install notify handler */
if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle, NULL);
+ acpi_install_hotplug_notify_handler(handle);

return AE_OK;
}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -445,7 +445,7 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_install_hotplug_notify_handler(acpi_handle handle);
void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**

2014-02-03 23:34:11

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 13/24][Resend] ACPI / hotplug / PCI: Drop crit_sect locking

From: Rafael J. Wysocki <[email protected]>

After recent PCI core changes related to the rescan/remove locking,
the code sections under crit_sect mutexes from ACPIPHP slot objects
are always executed under the general PCI rescan/remove lock.
For this reason, the crit_sect mutexes are simply redundant, so drop
them.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp.h | 1 -
drivers/pci/hotplug/acpiphp_glue.c | 23 +++--------------------
2 files changed, 3 insertions(+), 21 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -347,7 +347,6 @@ static acpi_status register_slot(acpi_ha
slot->bus = bridge->pci_bus;
slot->device = device;
INIT_LIST_HEAD(&slot->funcs);
- mutex_init(&slot->crit_sect);

list_add_tail(&slot->node, &bridge->slots);

@@ -744,7 +743,6 @@ static void acpiphp_check_bridge(struct
struct pci_bus *bus = slot->bus;
struct pci_dev *dev, *tmp;

- mutex_lock(&slot->crit_sect);
if (slot_no_hotplug(slot)) {
; /* do nothing */
} else if (get_slot_status(slot) == ACPI_STA_ALL) {
@@ -759,7 +757,6 @@ static void acpiphp_check_bridge(struct
} else {
disable_slot(slot);
}
- mutex_unlock(&slot->crit_sect);
}
}

@@ -846,12 +843,8 @@ static void hotplug_event(acpi_handle ha
} else {
struct acpiphp_slot *slot = func->slot;

- if (slot->flags & SLOT_IS_GOING_AWAY)
- break;
-
- mutex_lock(&slot->crit_sect);
- enable_slot(slot);
- mutex_unlock(&slot->crit_sect);
+ if (!(slot->flags & SLOT_IS_GOING_AWAY))
+ enable_slot(slot);
}
break;

@@ -862,7 +855,6 @@ static void hotplug_event(acpi_handle ha
acpiphp_check_bridge(bridge);
} else {
struct acpiphp_slot *slot = func->slot;
- int ret;

if (slot->flags & SLOT_IS_GOING_AWAY)
break;
@@ -871,10 +863,7 @@ static void hotplug_event(acpi_handle ha
* Check if anything has changed in the slot and rescan
* from the parent if that's the case.
*/
- mutex_lock(&slot->crit_sect);
- ret = acpiphp_rescan_slot(slot);
- mutex_unlock(&slot->crit_sect);
- if (ret)
+ if (acpiphp_rescan_slot(slot))
acpiphp_check_bridge(func->parent);
}
break;
@@ -1088,13 +1077,10 @@ int acpiphp_enable_slot(struct acpiphp_s
if (slot->flags & SLOT_IS_GOING_AWAY)
return -ENODEV;

- mutex_lock(&slot->crit_sect);
/* configure all functions */
if (!(slot->flags & SLOT_ENABLED))
enable_slot(slot);

- mutex_unlock(&slot->crit_sect);
-
pci_unlock_rescan_remove();
return 0;
}
@@ -1110,8 +1096,6 @@ static int acpiphp_disable_and_eject_slo
if (slot->flags & SLOT_IS_GOING_AWAY)
return -ENODEV;

- mutex_lock(&slot->crit_sect);
-
/* unconfigure all functions */
disable_slot(slot);

@@ -1125,7 +1109,6 @@ static int acpiphp_disable_and_eject_slo
break;
}

- mutex_unlock(&slot->crit_sect);
return 0;
}

Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -93,7 +93,6 @@ struct acpiphp_slot {
struct list_head funcs; /* one slot may have different
objects (i.e. for each function) */
struct slot *slot;
- struct mutex crit_sect;

u8 device; /* pci device# */
u32 flags; /* see below */

2014-02-03 23:34:49

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 14/24][Update] ACPI / hotplug / PCI: Simplify hotplug_event()

From: Rafael J. Wysocki <[email protected]>

A few lines of code can be cut from hotplug_event() by defining
and initializing the slot variable at the top of the function,
so do that.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -817,6 +817,7 @@ static void hotplug_event(acpi_handle ha
{
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
+ struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
@@ -838,14 +839,11 @@ static void hotplug_event(acpi_handle ha
pr_debug("%s: Bus check notify on %s\n", __func__, objname);
pr_debug("%s: re-enumerating slots under %s\n",
__func__, objname);
- if (bridge) {
+ if (bridge)
acpiphp_check_bridge(bridge);
- } else {
- struct acpiphp_slot *slot = func->slot;
+ else if (!(slot->flags & SLOT_IS_GOING_AWAY))
+ enable_slot(slot);

- if (!(slot->flags & SLOT_IS_GOING_AWAY))
- enable_slot(slot);
- }
break;

case ACPI_NOTIFY_DEVICE_CHECK:
@@ -853,12 +851,7 @@ static void hotplug_event(acpi_handle ha
pr_debug("%s: Device check notify on %s\n", __func__, objname);
if (bridge) {
acpiphp_check_bridge(bridge);
- } else {
- struct acpiphp_slot *slot = func->slot;
-
- if (slot->flags & SLOT_IS_GOING_AWAY)
- break;
-
+ } else if (!(slot->flags & SLOT_IS_GOING_AWAY)) {
/*
* Check if anything has changed in the slot and rescan
* from the parent if that's the case.
@@ -871,7 +864,7 @@ static void hotplug_event(acpi_handle ha
case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
pr_debug("%s: Device eject notify on %s\n", __func__, objname);
- acpiphp_disable_and_eject_slot(func->slot);
+ acpiphp_disable_and_eject_slot(slot);
break;
}

2014-02-03 23:35:52

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 15/24][Resend] ACPI / hotplug / PCI: Use acpi_handle_debug() in hotplug_event()

From: Rafael J. Wysocki <[email protected]>

Make hotplug_event() use acpi_handle_debug() instead of an open-coded
debug message printing and clean up the messages printed by it.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -819,9 +819,6 @@ static void hotplug_event(acpi_handle ha
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
- char objname[64];
- struct acpi_buffer buffer = { .length = sizeof(objname),
- .pointer = objname };

mutex_lock(&acpiphp_context_lock);
bridge = context->bridge;
@@ -831,14 +828,11 @@ static void hotplug_event(acpi_handle ha
mutex_unlock(&acpiphp_context_lock);

pci_lock_rescan_remove();
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);

switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/* bus re-enumerate */
- pr_debug("%s: Bus check notify on %s\n", __func__, objname);
- pr_debug("%s: re-enumerating slots under %s\n",
- __func__, objname);
+ acpi_handle_debug(handle, "Bus check in %s()\n", __func__);
if (bridge)
acpiphp_check_bridge(bridge);
else if (!(slot->flags & SLOT_IS_GOING_AWAY))
@@ -848,7 +842,7 @@ static void hotplug_event(acpi_handle ha

case ACPI_NOTIFY_DEVICE_CHECK:
/* device check */
- pr_debug("%s: Device check notify on %s\n", __func__, objname);
+ acpi_handle_debug(handle, "Device check in %s()\n", __func__);
if (bridge) {
acpiphp_check_bridge(bridge);
} else if (!(slot->flags & SLOT_IS_GOING_AWAY)) {
@@ -863,7 +857,7 @@ static void hotplug_event(acpi_handle ha

case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
- pr_debug("%s: Device eject notify on %s\n", __func__, objname);
+ acpi_handle_debug(handle, "Eject request in %s()\n", __func__);
acpiphp_disable_and_eject_slot(slot);
break;
}

2014-02-03 23:28:23

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 24/24][New] ACPI / hotplug / PCI: Rework acpiphp_check_host_bridge()

From: Rafael J. Wysocki <[email protected]>

Since the only existing caller of acpiphp_check_host_bridge(),
which is acpi_pci_root_scan_dependent(), already has a struct
acpi_device pointer needed to obtain the ACPIPHP context, it
doesn't make sense to execute acpi_bus_get_device() on its
handle in acpiphp_handle_to_bridge() just in order to get that
pointer back.

For this reason, modify acpiphp_check_host_bridge() to take
a struct acpi_device pointer as its argument and rearrange the
code accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/pci_root.c | 2 +-
drivers/pci/hotplug/acpiphp_glue.c | 11 +++--------
include/linux/pci-acpi.h | 4 ++--
3 files changed, 6 insertions(+), 11 deletions(-)

Index: linux-pm/include/linux/pci-acpi.h
===================================================================
--- linux-pm.orig/include/linux/pci-acpi.h
+++ linux-pm/include/linux/pci-acpi.h
@@ -59,12 +59,12 @@ static inline void acpi_pci_slot_remove(
void acpiphp_init(void);
void acpiphp_enumerate_slots(struct pci_bus *bus);
void acpiphp_remove_slots(struct pci_bus *bus);
-void acpiphp_check_host_bridge(acpi_handle handle);
+void acpiphp_check_host_bridge(struct acpi_device *adev);
#else
static inline void acpiphp_init(void) { }
static inline void acpiphp_enumerate_slots(struct pci_bus *bus) { }
static inline void acpiphp_remove_slots(struct pci_bus *bus) { }
-static inline void acpiphp_check_host_bridge(acpi_handle handle) { }
+static inline void acpiphp_check_host_bridge(struct acpi_device *adev) { }
#endif

#else /* CONFIG_ACPI */
Index: linux-pm/drivers/acpi/pci_root.c
===================================================================
--- linux-pm.orig/drivers/acpi/pci_root.c
+++ linux-pm/drivers/acpi/pci_root.c
@@ -51,7 +51,7 @@ static void acpi_pci_root_remove(struct

static int acpi_pci_root_scan_dependent(struct acpi_device *adev)
{
- acpiphp_check_host_bridge(adev->handle);
+ acpiphp_check_host_bridge(adev);
return 0;
}

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -378,15 +378,11 @@ static acpi_status register_slot(acpi_ha
return AE_OK;
}

-static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
+static struct acpiphp_bridge *acpiphp_dev_to_bridge(struct acpi_device *adev)
{
- struct acpi_device *adev = acpi_bus_get_acpi_device(handle);
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;

- if (!adev)
- return NULL;
-
acpi_lock_hp_context();
context = acpiphp_get_context(adev);
if (context) {
@@ -397,7 +393,6 @@ static struct acpiphp_bridge *acpiphp_ha
acpiphp_put_context(context);
}
acpi_unlock_hp_context();
- acpi_bus_put_acpi_device(adev);
return bridge;
}

@@ -764,11 +759,11 @@ static void acpiphp_sanitize_bus(struct
* ACPI event handlers
*/

-void acpiphp_check_host_bridge(acpi_handle handle)
+void acpiphp_check_host_bridge(struct acpi_device *adev)
{
struct acpiphp_bridge *bridge;

- bridge = acpiphp_handle_to_bridge(handle);
+ bridge = acpiphp_dev_to_bridge(adev);
if (bridge) {
pci_lock_rescan_remove();

2014-02-03 23:36:24

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 17/24][Resend] ACPICA: Introduce acpi_get_data_full() and rework acpi_get_data()

From: Rafael J. Wysocki <[email protected]>

Introduce a new function, acpi_get_data_full(), working in analogy
with acpi_get_data() except that it can execute a callback provided
as its 4th argument right after acpi_ns_get_attached_data() has
returned a success.

That will allow Linux to reference count the object pointed to by
*data before the namespace mutex is released so as to ensure that it
will not be freed going forward until the reference to it acquired
by acpi_get_data_full() is dropped.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/acpi/acpica/nsxfeval.c | 33 ++++++++++++++++++++++++++++++---
include/acpi/acpixf.h | 4 ++++
2 files changed, 34 insertions(+), 3 deletions(-)

Index: linux-pm/drivers/acpi/acpica/nsxfeval.c
===================================================================
--- linux-pm.orig/drivers/acpi/acpica/nsxfeval.c
+++ linux-pm/drivers/acpi/acpica/nsxfeval.c
@@ -923,19 +923,22 @@ ACPI_EXPORT_SYMBOL(acpi_detach_data)

/*******************************************************************************
*
- * FUNCTION: acpi_get_data
+ * FUNCTION: acpi_get_data_full
*
* PARAMETERS: obj_handle - Namespace node
* handler - Handler used in call to attach_data
* data - Where the data is returned
+ * callback - function to execute before returning
*
* RETURN: Status
*
- * DESCRIPTION: Retrieve data that was previously attached to a namespace node.
+ * DESCRIPTION: Retrieve data that was previously attached to a namespace node
+ * and execute a callback before returning.
*
******************************************************************************/
acpi_status
-acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data)
+acpi_get_data_full(acpi_handle obj_handle, acpi_object_handler handler,
+ void **data, void (*callback)(void *))
{
struct acpi_namespace_node *node;
acpi_status status;
@@ -960,10 +963,34 @@ acpi_get_data(acpi_handle obj_handle, ac
}

status = acpi_ns_get_attached_data(node, handler, data);
+ if (ACPI_SUCCESS(status) && callback) {
+ callback(*data);
+ }

unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return (status);
}

+ACPI_EXPORT_SYMBOL(acpi_get_data_full)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_get_data
+ *
+ * PARAMETERS: obj_handle - Namespace node
+ * handler - Handler used in call to attach_data
+ * data - Where the data is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieve data that was previously attached to a namespace node.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data)
+{
+ return acpi_get_data_full(obj_handle, handler, data, NULL);
+}
+
ACPI_EXPORT_SYMBOL(acpi_get_data)
Index: linux-pm/include/acpi/acpixf.h
===================================================================
--- linux-pm.orig/include/acpi/acpixf.h
+++ linux-pm/include/acpi/acpixf.h
@@ -230,6 +230,10 @@ acpi_attach_data(acpi_handle object, acp
acpi_status acpi_detach_data(acpi_handle object, acpi_object_handler handler);

acpi_status
+acpi_get_data_full(acpi_handle object, acpi_object_handler handler, void **data,
+ void (*callback)(void *));
+
+acpi_status
acpi_get_data(acpi_handle object, acpi_object_handler handler, void **data);

acpi_status

2014-02-03 23:36:22

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 16/24][New] ACPI / hotplug / PCI: Do not pass ACPI handle to hotplug_event()

From: Rafael J. Wysocki <[email protected]>

Since hotplug_event() can get the ACPI handle needed for debug
printouts from its context argument, there's no need to pass the
handle to it. Moreover, the second argument's type may be changed
to (struct acpiphp_context *), because that's what is always passed
to hotplug_event() as the second argument anyway.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/pci/hotplug/acpiphp_glue.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -63,7 +63,7 @@ static DEFINE_MUTEX(acpiphp_context_lock
static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
-static void hotplug_event(acpi_handle handle, u32 type, void *data);
+static void hotplug_event(u32 type, struct acpiphp_context *context);
static void free_bridge(struct kref *kref);

static void acpiphp_context_handler(acpi_handle handle, void *context)
@@ -225,7 +225,7 @@ static void dock_event(acpi_handle handl
acpiphp_put_context(context);
mutex_unlock(&acpiphp_context_lock);

- hotplug_event(handle, type, data);
+ hotplug_event(type, context);

put_bridge(context->func.parent);
}
@@ -813,9 +813,9 @@ void acpiphp_check_host_bridge(acpi_hand

static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);

-static void hotplug_event(acpi_handle handle, u32 type, void *data)
+static void hotplug_event(u32 type, struct acpiphp_context *context)
{
- struct acpiphp_context *context = data;
+ acpi_handle handle = context->adev->handle;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
@@ -870,14 +870,14 @@ static void hotplug_event(acpi_handle ha
static void hotplug_event_work(void *data, u32 type)
{
struct acpiphp_context *context = data;
- acpi_handle handle = context->adev->handle;

acpi_scan_lock_acquire();

- hotplug_event(handle, type, context);
+ hotplug_event(type, context);

acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);
+ acpi_evaluate_hotplug_ost(context->adev->handle, type,
+ ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);
}

2014-02-03 23:37:53

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 18/24][Update] ACPI / hotplug: Fix potential race in acpi_bus_notify()

From: Rafael J. Wysocki <[email protected]>

There is a slight possibility for the ACPI device object pointed to
by adev in acpi_hotplug_notify_cb() to become invalid between the
acpi_bus_get_device() that it comes from and the subsequent dereference
of that pointer under get_device(). Namely, if acpi_scan_drop_device()
runs in parallel with acpi_hotplug_notify_cb(), acpi_device_del_work_fn()
queued up by it may delete the device object in question right after
a successful execution of acpi_bus_get_device() in acpi_bus_notify().

An analogous problem is present in acpi_bus_notify() where the device
pointer coming from acpi_bus_get_device() may become invalid before
it subsequent dereference in the "if" block.

To prevent that from happening, introduce a new function,
acpi_bus_get_acpi_device(), working analogously to acpi_bus_get_device()
except that it will grab a reference to the ACPI device object returned
by it and it will do that under the ACPICA's namespace mutex. Then,
make both acpi_hotplug_notify_cb() and acpi_bus_notify() use
acpi_bus_get_acpi_device() instead of acpi_bus_get_device() so as to
ensure that the pointers used by them will not become stale at one
point.

In addition to that, introduce acpi_bus_put_acpi_device() as a wrapper
around put_device() to be used along with acpi_bus_get_acpi_device()
and make the (new) users of the latter use acpi_bus_put_acpi_device()
too.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/bus.c | 6 ++++--
drivers/acpi/scan.c | 42 ++++++++++++++++++++++++++++++++++--------
include/acpi/acpi_bus.h | 2 ++
3 files changed, 40 insertions(+), 10 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -476,7 +476,7 @@ static void acpi_device_hotplug(void *da

out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
- put_device(&adev->dev);
+ acpi_bus_put_acpi_device(adev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
}
@@ -488,9 +488,6 @@ static void acpi_hotplug_notify_cb(acpi_
struct acpi_device *adev;
acpi_status status;

- if (acpi_bus_get_device(handle, &adev))
- goto err_out;
-
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
@@ -512,12 +509,15 @@ static void acpi_hotplug_notify_cb(acpi_
/* non-hotplug event; possibly handled by other handler */
return;
}
- get_device(&adev->dev);
+ adev = acpi_bus_get_acpi_device(handle);
+ if (!adev)
+ goto err_out;
+
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
return;

- put_device(&adev->dev);
+ acpi_bus_put_acpi_device(adev);

err_out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
@@ -1112,14 +1112,16 @@ static void acpi_scan_drop_device(acpi_h
mutex_unlock(&acpi_device_del_lock);
}

-int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
+static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device,
+ void (*callback)(void *))
{
acpi_status status;

if (!device)
return -EINVAL;

- status = acpi_get_data(handle, acpi_scan_drop_device, (void **)device);
+ status = acpi_get_data_full(handle, acpi_scan_drop_device,
+ (void **)device, callback);
if (ACPI_FAILURE(status) || !*device) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
handle));
@@ -1127,8 +1129,32 @@ int acpi_bus_get_device(acpi_handle hand
}
return 0;
}
+
+int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
+{
+ return acpi_get_device_data(handle, device, NULL);
+}
EXPORT_SYMBOL(acpi_bus_get_device);

+static void get_acpi_device(void *dev)
+{
+ if (dev)
+ get_device(&((struct acpi_device *)dev)->dev);
+}
+
+struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle)
+{
+ struct acpi_device *adev = NULL;
+
+ acpi_get_device_data(handle, &adev, get_acpi_device);
+ return adev;
+}
+
+void acpi_bus_put_acpi_device(struct acpi_device *adev)
+{
+ put_device(&adev->dev);
+}
+
int acpi_device_add(struct acpi_device *device,
void (*release)(struct device *))
{
Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -340,7 +340,7 @@ static void acpi_bus_osc_support(void)
*/
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
{
- struct acpi_device *device = NULL;
+ struct acpi_device *device;
struct acpi_driver *driver;

ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
@@ -387,12 +387,14 @@ static void acpi_bus_notify(acpi_handle
break;
}

- acpi_bus_get_device(handle, &device);
+ device = acpi_bus_get_acpi_device(handle);
if (device) {
driver = device->driver;
if (driver && driver->ops.notify &&
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
driver->ops.notify(device, type);
+
+ acpi_bus_put_acpi_device(device);
}
}

Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -381,6 +381,8 @@ extern int unregister_acpi_notifier(stru
*/

int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device);
+struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle);
+void acpi_bus_put_acpi_device(struct acpi_device *adev);
acpi_status acpi_bus_get_status_handle(acpi_handle handle,
unsigned long long *sta);
int acpi_bus_get_status(struct acpi_device *device);

2014-02-03 23:38:19

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 20/24][Update] ACPI / hotplug / PCI: Consolidate ACPIPHP with ACPI core hotplug

From: Rafael J. Wysocki <[email protected]>

The ACPI-based PCI hotplug (ACPIPHP) code currently attaches its
hotplug context objects directly to ACPI namespace nodes representing
hotplug devices. However, after recent changes causing struct
acpi_device to be created for every namespace node representing a
device (regardless of its status), that is not necessary any more.
Moreover, it's vulnerable to a theoretical issue that the ACPI
handle passed in the context between handle_hotplug_event() and
hotplug_event_work() may become invalid in the meantime (as a
result of a concurrent table unload).

In principle, this issue might be addressed by adding a non-empty
release handler for ACPIPHP hotplug context objects analogous to
acpi_scan_drop_device(), but that would duplicate the code in that
function and in acpi_device_del_work_fn(). For this reason, it's
better to modify ACPIPHP to attach its device hotplug contexts to
struct device objects representing hotplug devices and make it
use acpi_hotplug_notify_cb() as its notify handler. At the same
time, acpi_device_hotplug() can be modified to dispatch the new
.hp.event() callback pointing to acpiphp_hotplug_event() from ACPI
device objects associated with PCI devices and use the generic
ACPI device hotplug code for device objects with scan handlers
attached to them.

This allows the existing code duplication between ACPIPHP and the
ACPI core to be reduced too and makes further ACPI-based device
hotplug consolidation possible.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/scan.c | 106 +++++++++++++++++------
drivers/pci/hotplug/acpiphp.h | 9 +-
drivers/pci/hotplug/acpiphp_glue.c | 164 +++++++------------------------------
include/acpi/acpi_bus.h | 22 ++++
4 files changed, 142 insertions(+), 159 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -450,43 +450,61 @@ static int acpi_scan_bus_check(struct ac
return 0;
}

+static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
+{
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ return acpi_scan_bus_check(adev);
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ return acpi_scan_device_check(adev);
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ case ACPI_OST_EC_OSPM_EJECT:
+ return acpi_scan_hot_remove(adev);
+ }
+ return -EINVAL;
+}
+
static void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
- int error;
+ int error = -ENODEV;

lock_device_hotplug();
mutex_lock(&acpi_scan_lock);

/*
* The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it may have become invalid before
+ * are holding acpi_scan_lock, but it might have become invalid before
* that lock was acquired.
*/
if (adev->handle == INVALID_ACPI_HANDLE)
- goto out;
+ goto err_out;

- switch (src) {
- case ACPI_NOTIFY_BUS_CHECK:
- error = acpi_scan_bus_check(adev);
- break;
- case ACPI_NOTIFY_DEVICE_CHECK:
- error = acpi_scan_device_check(adev);
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- case ACPI_OST_EC_OSPM_EJECT:
- error = acpi_scan_hot_remove(adev);
- break;
- default:
- error = -EINVAL;
- break;
+ if (adev->handler) {
+ error = acpi_generic_hotplug_event(adev, src);
+ } else {
+ int (*event)(struct acpi_device *, u32);
+
+ acpi_lock_hp_context();
+ event = adev->hp ? adev->hp->event : NULL;
+ acpi_unlock_hp_context();
+ /*
+ * There may be additional notify handlers for device objects
+ * without the .event() callback, so ignore them here.
+ */
+ if (event)
+ error = event(adev, src);
+ else
+ goto out;
}
if (!error)
ost_code = ACPI_OST_SC_SUCCESS;

- out:
+ err_out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
+
+ out:
acpi_bus_put_acpi_device(adev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
@@ -494,8 +512,8 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_scan_handler *handler = data;
+ u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;

@@ -503,26 +521,49 @@ static void acpi_hotplug_notify_cb(acpi_
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;
+
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;
+
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (!handler->hotplug.enabled) {
+ if (handler && !handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
- goto err_out;
+ goto out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
- default:
- /* non-hotplug event; possibly handled by other handler */
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
return;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
+ goto out;
+
+ default:
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto out;
}
+
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
adev = acpi_bus_get_acpi_device(handle);
if (!adev)
- goto err_out;
+ goto out;

status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
@@ -530,10 +571,22 @@ static void acpi_hotplug_notify_cb(acpi_

acpi_bus_put_acpi_device(adev);

- err_out:
+ out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+{
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb, data);
+}
+
+void acpi_remove_hotplug_notify_handler(acpi_handle handle)
+{
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb);
+}
+
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1995,8 +2048,7 @@ static void acpi_scan_init_hotplug(acpi_
list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
if (handler) {
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, handler);
+ acpi_install_hotplug_notify_handler(handle, handler);
break;
}
}
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -116,12 +116,17 @@ struct acpiphp_func {
};

struct acpiphp_context {
+ struct acpi_hotplug_context hp;
struct acpiphp_func func;
- struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};

+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *fu

static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
{
- return func_to_context(func)->adev;
+ return func_to_context(func)->hp.self;
}

static inline acpi_handle func_to_handle(struct acpiphp_func *func)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -59,17 +59,12 @@
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);

-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, struct acpiphp_context *context);
static void free_bridge(struct kref *kref);

-static void acpiphp_context_handler(acpi_handle handle, void *context)
-{
- /* Intentionally empty. */
-}
-
/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @adev: ACPI device object to create the context for.
@@ -79,39 +74,27 @@ static void acpiphp_context_handler(acpi
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
- acpi_status status;

context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;

- context->adev = adev;
context->refcount = 1;
- status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
- if (ACPI_FAILURE(status)) {
- kfree(context);
- return NULL;
- }
+ acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event);
return context;
}

/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
- * @handle: ACPI object handle to get the context for.
+ * @adev: ACPI device object to get the context for.
*
* Call under acpi_hp_context_lock.
*/
-static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- struct acpiphp_context *context = NULL;
- acpi_status status;
- void *data;
+ struct acpiphp_context *context = to_acpiphp_context(adev->hp);

- status = acpi_get_data(handle, acpiphp_context_handler, &data);
- if (ACPI_SUCCESS(status)) {
- context = data;
- context->refcount++;
- }
+ context->refcount++;
return context;
}

@@ -129,7 +112,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->adev->handle, acpiphp_context_handler);
+ context->hp.self->hp = NULL;
kfree(context);
}

@@ -211,22 +194,13 @@ static void post_dock_fixups(acpi_handle

static void dock_event(acpi_handle handle, u32 type, void *data)
{
- struct acpiphp_context *context;
+ struct acpi_device *adev;

- acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
- if (!context || WARN_ON(context->adev->handle != handle)
- || context->func.parent->is_going_away) {
- acpi_unlock_hp_context();
- return;
+ adev = acpi_bus_get_acpi_device(handle);
+ if (adev) {
+ acpiphp_hotplug_event(adev, type);
+ acpi_bus_put_acpi_device(adev);
}
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_unlock_hp_context();
-
- hotplug_event(type, context);
-
- put_bridge(context->func.parent);
}

static const struct acpi_dock_ops acpiphp_dock_ops = {
@@ -397,25 +371,23 @@ static acpi_status register_slot(acpi_ha
}

/* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK)) {
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event,
- context);
- if (ACPI_FAILURE(status))
- acpi_handle_err(handle,
- "failed to install notify handler\n");
- }
+ if (!(newfunc->flags & FUNC_HAS_DCK))
+ acpi_install_hotplug_notify_handler(handle, NULL);

return AE_OK;
}

static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
{
+ struct acpi_device *adev = acpi_bus_get_acpi_device(handle);
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;

+ if (!adev)
+ return NULL;
+
acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (context) {
bridge = context->bridge;
if (bridge)
@@ -424,6 +396,7 @@ static struct acpiphp_bridge *acpiphp_ha
acpiphp_put_context(context);
}
acpi_unlock_hp_context();
+ acpi_bus_put_acpi_device(adev);
return bridge;
}

@@ -431,7 +404,6 @@ static void cleanup_bridge(struct acpiph
{
struct acpiphp_slot *slot;
struct acpiphp_func *func;
- acpi_status status;

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
@@ -440,13 +412,8 @@ static void cleanup_bridge(struct acpiph
if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);

- if (!(func->flags & FUNC_HAS_DCK)) {
- status = acpi_remove_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event);
- if (ACPI_FAILURE(status))
- pr_err("failed to remove notify handler\n");
- }
+ if (!(func->flags & FUNC_HAS_DCK))
+ acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
@@ -814,7 +781,7 @@ static int acpiphp_disable_and_eject_slo

static void hotplug_event(u32 type, struct acpiphp_context *context)
{
- acpi_handle handle = context->adev->handle;
+ acpi_handle handle = context->hp.self->handle;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
@@ -866,87 +833,24 @@ static void hotplug_event(u32 type, stru
put_bridge(bridge);
}

-static void hotplug_event_work(void *data, u32 type)
-{
- struct acpiphp_context *context = data;
-
- acpi_scan_lock_acquire();
-
- hotplug_event(type, context);
-
- acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(context->adev->handle, type,
- ACPI_OST_SC_SUCCESS, NULL);
- put_bridge(context->func.parent);
-}
-
-/**
- * handle_hotplug_event - handle ACPI hotplug event
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @data: pointer to acpiphp_context structure
- *
- * Handles ACPI event notification on slots.
- */
-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{
struct acpiphp_context *context;
- u32 ost_code = ACPI_OST_SC_SUCCESS;
- acpi_status status;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- case ACPI_NOTIFY_DEVICE_CHECK:
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS;
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }

acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
- if (!context || WARN_ON(context->adev->handle != handle)
- || context->func.parent->is_going_away)
- goto err_out;
-
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- status = acpi_hotplug_execute(hotplug_event_work, context, type);
- if (ACPI_SUCCESS(status)) {
+ context = acpiphp_get_context(adev);
+ if (!context || context->func.parent->is_going_away) {
acpi_unlock_hp_context();
- return;
+ return -ENODATA;
}
- put_bridge(context->func.parent);
-
- err_out:
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
acpi_unlock_hp_context();
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
+ hotplug_event(type, context);
+
+ put_bridge(context->func.parent);
+ return 0;
}

/**
@@ -999,7 +903,7 @@ void acpiphp_enumerate_slots(struct pci_
* bridge is not interesting to us either.
*/
acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (!context) {
acpi_unlock_hp_context();
put_device(&bus->dev);
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -137,6 +137,16 @@ struct acpi_scan_handler {
};

/*
+ * ACPI Hotplug Context
+ * --------------------
+ */
+
+struct acpi_hotplug_context {
+ struct acpi_device *self;
+ int (*event)(struct acpi_device *, u32);
+};
+
+/*
* ACPI Driver
* -----------
*/
@@ -329,6 +339,7 @@ struct acpi_device {
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_scan_handler *handler;
+ struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
void *driver_data;
struct device dev;
@@ -351,6 +362,15 @@ static inline void acpi_set_device_statu
*((u32 *)&adev->status) = sta;
}

+static inline void acpi_set_hp_context(struct acpi_device *adev,
+ struct acpi_hotplug_context *hp,
+ int (*event)(struct acpi_device *, u32))
+{
+ hp->self = adev;
+ hp->event = event;
+ adev->hp = hp;
+}
+
/* acpi_device.dev.bus == &acpi_bus_type */
extern struct bus_type acpi_bus_type;

@@ -425,6 +445,8 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver

2014-02-03 23:38:43

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 23/24][Update] ACPI / hotplug / PCI: Hotplug notifications from acpi_bus_notify()

From: Rafael J. Wysocki <[email protected]>

Since acpi_bus_notify() is executed on all notifications for all
devices anyway, make it execute acpi_device_hotplug() for all
hotplug events instead of installing notify handlers pointing to
the same function for all hotplug devices.

This change reduces both the size and complexity of ACPI-based device
hotplug code. Moreover, since acpi_device_hotplug() only does
significant things for devices that have either an ACPI scan handler,
or a hotplug context with .eject() defined, and those devices
had notify handlers pointing to acpi_hotplug_notify_cb() installed
before anyway, this modification shouldn't change functionality.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/acpi/bus.c | 61 +++++++++++++---------
drivers/acpi/internal.h | 1
drivers/acpi/scan.c | 101 -------------------------------------
drivers/pci/hotplug/acpiphp.h | 1
drivers/pci/hotplug/acpiphp_glue.c | 24 ++++----
include/acpi/acpi_bus.h | 2
6 files changed, 53 insertions(+), 137 deletions(-)

Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -340,62 +340,77 @@ static void acpi_bus_osc_support(void)
*/
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
{
- struct acpi_device *device;
+ struct acpi_device *adev;
struct acpi_driver *driver;
-
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
- type, handle));
+ acpi_status status;
+ u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

switch (type) {
-
case ACPI_NOTIFY_BUS_CHECK:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;

case ACPI_NOTIFY_DEVICE_CHECK:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;

case ACPI_NOTIFY_DEVICE_WAKE:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n");
break;

case ACPI_NOTIFY_EJECT_REQUEST:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
break;

case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n");
/* TBD: Exactly what does 'light' mean? */
break;

case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- /* TBD */
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
break;

case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- /* TBD */
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
break;

case ACPI_NOTIFY_POWER_FAULT:
- /* TBD */
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
break;

default:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Received unknown/unsupported notification [%08x]\n",
- type));
- break;
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto err;
}

- device = acpi_bus_get_acpi_device(handle);
- if (device) {
- driver = device->driver;
- if (driver && driver->ops.notify &&
- (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
- driver->ops.notify(device, type);
+ adev = acpi_bus_get_acpi_device(handle);
+ if (!adev)
+ goto err;
+
+ driver = adev->driver;
+ if (driver && driver->ops.notify &&
+ (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
+ driver->ops.notify(adev, type);

- acpi_bus_put_acpi_device(device);
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
+ if (ACPI_SUCCESS(status))
+ return;
+ default:
+ break;
}
+ acpi_bus_put_acpi_device(adev);
+ return;
+
+ err:
+ acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

/* --------------------------------------------------------------------------
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -73,6 +73,7 @@ static inline void acpi_lpss_init(void)
#endif

bool acpi_queue_hotplug_work(struct work_struct *work);
+void acpi_device_hotplug(void *data, u32 src);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);

/* --------------------------------------------------------------------------
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -470,7 +470,7 @@ static int acpi_generic_hotplug_event(st
return -EINVAL;
}

-static void acpi_device_hotplug(void *data, u32 src)
+void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
@@ -520,75 +520,6 @@ static void acpi_device_hotplug(void *da
unlock_device_hotplug();
}

-static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
-{
- u32 ost_code = ACPI_OST_SC_SUCCESS;
- struct acpi_device *adev;
- acpi_status status;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK:
- acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
- break;
-
- case ACPI_NOTIFY_EJECT_REQUEST:
- acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }
-
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
- adev = acpi_bus_get_acpi_device(handle);
- if (!adev)
- goto out;
-
- status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
- if (ACPI_SUCCESS(status))
- return;
-
- acpi_bus_put_acpi_device(adev);
-
- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
-}
-
-void acpi_install_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, NULL);
-}
-
-void acpi_remove_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb);
-}
-
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2029,34 +1960,6 @@ void acpi_scan_hotplug_enabled(struct ac
mutex_unlock(&acpi_scan_lock);
}

-static void acpi_scan_init_hotplug(acpi_handle handle, int type)
-{
- struct acpi_device_pnp pnp = {};
- struct acpi_hardware_id *hwid;
- struct acpi_scan_handler *handler;
-
- INIT_LIST_HEAD(&pnp.ids);
- acpi_set_pnp_ids(handle, &pnp, type);
-
- if (!pnp.type.hardware_id)
- goto out;
-
- /*
- * This relies on the fact that acpi_install_notify_handler() will not
- * install the same notify handler routine twice for the same handle.
- */
- list_for_each_entry(hwid, &pnp.ids, list) {
- handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- acpi_install_hotplug_notify_handler(handle);
- break;
- }
- }
-
-out:
- acpi_free_pnp_ids(&pnp);
-}
-
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **return_value)
{
@@ -2078,8 +1981,6 @@ static acpi_status acpi_bus_check_add(ac
return AE_OK;
}

- acpi_scan_init_hotplug(handle, type);
-
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -293,7 +293,6 @@ static acpi_status register_slot(acpi_ha
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- acpi_unlock_hp_context();

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -301,8 +300,14 @@ static acpi_status register_slot(acpi_ha
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;

+ /*
+ * Dock stations' notify handler should be used for dock devices instead
+ * of the common one, so clear hp.event in their contexts.
+ */
if (acpi_has_method(handle, "_DCK"))
- newfunc->flags |= FUNC_HAS_DCK;
+ context->hp.event = NULL;
+
+ acpi_unlock_hp_context();

/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
@@ -370,10 +375,6 @@ static acpi_status register_slot(acpi_ha
pr_debug("failed to register dock device\n");
}

- /* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle);
-
return AE_OK;
}

@@ -407,13 +408,14 @@ static void cleanup_bridge(struct acpiph

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
- acpi_handle handle = func_to_handle(func);
+ struct acpi_device *adev = func_to_acpi_device(func);

- if (is_dock_device(handle))
- unregister_hotplug_dock_device(handle);
+ if (is_dock_device(adev->handle))
+ unregister_hotplug_dock_device(adev->handle);

- if (!(func->flags & FUNC_HAS_DCK))
- acpi_remove_hotplug_notify_handler(handle);
+ acpi_lock_hp_context();
+ adev->hp->event = NULL;
+ acpi_unlock_hp_context();
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -445,8 +445,6 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle);
-void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -167,7 +167,6 @@ struct acpiphp_attention_info

#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
-#define FUNC_HAS_DCK (0x00000004)

/* function prototypes */

2014-02-03 23:38:42

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 21/24][Resend] ACPI / hotplug / PCI: Rework the handling of eject requests

From: Rafael J. Wysocki <[email protected]>

To avoid the need to install a hotplug notify handler for each ACPI
namespace node representing a device and having a matching scan
handler, move the check whether or not the ejection of the given
device is enabled through its scan handler from acpi_hotplug_notify_cb()
to acpi_generic_hotplug_event(). Also, move the execution of
ACPI_OST_SC_EJECT_IN_PROGRESS _OST to acpi_generic_hotplug_event(),
because in acpi_hotplug_notify_cb() or in acpi_eject_store() we really
don't know whether or not the eject is going to be in progress (for
example, acpi_hotplug_execute() may still fail without queuing up the
work item).

Signed-off-by: Rafael J. Wysocki <[email protected]>
Tested-by: Mika Westerberg <[email protected]>
---
drivers/acpi/scan.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -459,6 +459,12 @@ static int acpi_generic_hotplug_event(st
return acpi_scan_device_check(adev);
case ACPI_NOTIFY_EJECT_REQUEST:
case ACPI_OST_EC_OSPM_EJECT:
+ if (adev->handler && !adev->handler->hotplug.enabled) {
+ dev_info(&adev->dev, "Eject disabled\n");
+ return -EPERM;
+ }
+ acpi_evaluate_hotplug_ost(adev->handle, ACPI_NOTIFY_EJECT_REQUEST,
+ ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
return acpi_scan_hot_remove(adev);
}
return -EINVAL;
@@ -483,6 +489,10 @@ static void acpi_device_hotplug(void *da

if (adev->handler) {
error = acpi_generic_hotplug_event(adev, src);
+ if (error == -EPERM) {
+ ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
+ goto err_out;
+ }
} else {
int (*event)(struct acpi_device *, u32);

@@ -512,7 +522,6 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- struct acpi_scan_handler *handler = data;
u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;
@@ -528,13 +537,6 @@ static void acpi_hotplug_notify_cb(acpi_

case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (handler && !handler->hotplug.enabled) {
- acpi_handle_err(handle, "Eject disabled\n");
- 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);
break;

case ACPI_NOTIFY_DEVICE_WAKE:
@@ -632,8 +634,6 @@ acpi_eject_store(struct device *d, struc
if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
return -ENODEV;

- acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
- ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
get_device(&acpi_device->dev);
status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device,
ACPI_OST_EC_OSPM_EJECT);

2014-02-05 10:07:06

by Mika Westerberg

[permalink] [raw]
Subject: Re: [PATCH v2 0/24] ACPI / hotplug / PCI: ACPIPHP updates and consolidation with ACPI core

On Tue, Feb 04, 2014 at 12:12:26AM +0100, Rafael J. Wysocki wrote:
> As I said previously, I have found two concurrency-related bugs more in ACPIPHP
> and I needed to put fixes for those bugs at the top of the series (after previous
> [1-2/13] and the patch at https://patchwork.kernel.org/patch/3567701/). For this
> reason, I also had to rebase the ACPI/PCI hotplug consolidation patchset
> (https://lkml.org/lkml/2014/2/2/87) which got a few additional cosmetic changes too.
>
> The following is a combination of the two series with one patch dropped (it would
> conflict with [4-5/24]) and a few patches added. It is on top of 3.14-rc1.
>
> Patches [1-5/24], that are regarded as 3.14-rc2 material, are on the bleeding-edge
> branch of linux-pm.git. The remaining patches will show up in bleeding-edge
> shortly.
>
> [1/24] Remove entries from bus->devices in reverse order (in ACPIPHP).
> [2/24] Move PCI rescan-remove locking to hotplug_event().
> [3/24] Scan root bus under the PCI rescan-remove lock
> [4/24] Fix race in handle_hotplug_event() related to concurrent bridge removal.
> [5/24] Fix race in hotplug_event() related to dock events and concurrent bridge removal.
> [6/24] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
> [7/24] Fix up two kerneldoc comments in acpiphp_glue.c.
> [8/24] Get rid of an unnecessary label in register_slot().
> [9/24] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
> [10/24] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
> [11/24] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
> [12/24] Drop acpiphp_bus_add() (which has only one user).
> [13/24] Drop crit_sect mutexes (that are redundant).
> [14/24] Clean up the usage of the slot variable in hotplug_event().
> [15/24] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
> [16/24] Do not pass ACPI handle to hotplug_event().
> [17/24] Add a new function to ACPICA allowing a callback to be executed under the
> namespace mutex after calling acpi_ns_get_attached_data().
> [18/24] Use the new ACPICA's function to fix a couple of potential races related
> to ACPI notifies.
> [19/24] Move the hotplug context lock definition to the ACPI core (from ACPIPHP).
> [20/24] Consolidate ACPI hotplug signaling for PCI and ACPI core.
> [21/24] Rework the handling of eject requests in the ACPI core.
> [22/24] Simplify a routine for installing hotplug notify handlers.
> [23/24] Dispatch ACPI hotplug notifications for "core" devices and PCI from
> acpi_bus_notify().
> [24/24] Pass struct acpi_device pointer to acpiphp_check_host_bridge().

Tried your latest bleeding-edge branch (it seemed to have all the above
patches) on my TBT test machines (Acer Aspire S5, Intel NUC and Intel
DZ77RE-75K). TBT hotplug works nicely, no deadlocks or anything like that.

Tested-by: Mika Westerberg <[email protected]>

2014-02-05 10:45:11

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v2 0/24] ACPI / hotplug / PCI: ACPIPHP updates and consolidation with ACPI core

On Wednesday, February 05, 2014 12:14:05 PM Mika Westerberg wrote:
> On Tue, Feb 04, 2014 at 12:12:26AM +0100, Rafael J. Wysocki wrote:
> > As I said previously, I have found two concurrency-related bugs more in ACPIPHP
> > and I needed to put fixes for those bugs at the top of the series (after previous
> > [1-2/13] and the patch at https://patchwork.kernel.org/patch/3567701/). For this
> > reason, I also had to rebase the ACPI/PCI hotplug consolidation patchset
> > (https://lkml.org/lkml/2014/2/2/87) which got a few additional cosmetic changes too.
> >
> > The following is a combination of the two series with one patch dropped (it would
> > conflict with [4-5/24]) and a few patches added. It is on top of 3.14-rc1.
> >
> > Patches [1-5/24], that are regarded as 3.14-rc2 material, are on the bleeding-edge
> > branch of linux-pm.git. The remaining patches will show up in bleeding-edge
> > shortly.
> >
> > [1/24] Remove entries from bus->devices in reverse order (in ACPIPHP).
> > [2/24] Move PCI rescan-remove locking to hotplug_event().
> > [3/24] Scan root bus under the PCI rescan-remove lock
> > [4/24] Fix race in handle_hotplug_event() related to concurrent bridge removal.
> > [5/24] Fix race in hotplug_event() related to dock events and concurrent bridge removal.
> > [6/24] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
> > [7/24] Fix up two kerneldoc comments in acpiphp_glue.c.
> > [8/24] Get rid of an unnecessary label in register_slot().
> > [9/24] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
> > [10/24] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
> > [11/24] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
> > [12/24] Drop acpiphp_bus_add() (which has only one user).
> > [13/24] Drop crit_sect mutexes (that are redundant).
> > [14/24] Clean up the usage of the slot variable in hotplug_event().
> > [15/24] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
> > [16/24] Do not pass ACPI handle to hotplug_event().
> > [17/24] Add a new function to ACPICA allowing a callback to be executed under the
> > namespace mutex after calling acpi_ns_get_attached_data().
> > [18/24] Use the new ACPICA's function to fix a couple of potential races related
> > to ACPI notifies.
> > [19/24] Move the hotplug context lock definition to the ACPI core (from ACPIPHP).
> > [20/24] Consolidate ACPI hotplug signaling for PCI and ACPI core.
> > [21/24] Rework the handling of eject requests in the ACPI core.
> > [22/24] Simplify a routine for installing hotplug notify handlers.
> > [23/24] Dispatch ACPI hotplug notifications for "core" devices and PCI from
> > acpi_bus_notify().
> > [24/24] Pass struct acpi_device pointer to acpiphp_check_host_bridge().
>
> Tried your latest bleeding-edge branch (it seemed to have all the above
> patches) on my TBT test machines (Acer Aspire S5, Intel NUC and Intel
> DZ77RE-75K).

Yes, bleeding-edge already has the patches (linux-next has them too now for
that matter).

> TBT hotplug works nicely, no deadlocks or anything like that.
>
> Tested-by: Mika Westerberg <[email protected]>

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-06 12:52:39

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update][PATCH 20/24] ACPI / hotplug / PCI: Consolidate ACPIPHP with ACPI core hotplug

From: Rafael J. Wysocki <[email protected]>

The ACPI-based PCI hotplug (ACPIPHP) code currently attaches its
hotplug context objects directly to ACPI namespace nodes representing
hotplug devices. However, after recent changes causing struct
acpi_device to be created for every namespace node representing a
device (regardless of its status), that is not necessary any more.
Moreover, it's vulnerable to the theoretical issue that the ACPI
handle passed in the context between handle_hotplug_event() and
hotplug_event_work() may become invalid in the meantime (as a
result of a concurrent table unload).

In principle, this issue might be addressed by adding a non-empty
release handler for ACPIPHP hotplug context objects analogous to
acpi_scan_drop_device(), but that would duplicate the code in that
function and in acpi_device_del_work_fn(). For this reason, it's
better to modify ACPIPHP to attach its device hotplug contexts to
struct device objects representing hotplug devices and make it
use acpi_hotplug_notify_cb() as its notify handler. At the same
time, acpi_device_hotplug() can be modified to dispatch the new
.hp.event() callback pointing to acpiphp_hotplug_event() from ACPI
device objects associated with PCI devices or use the generic
ACPI device hotplug code for device objects with matching scan
handlers.

This allows the existing code duplication between ACPIPHP and the
ACPI core to be reduced too and makes further ACPI-based device
hotplug consolidation possible.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---

The previous version would cause hotplug notifications to be ignored for
devices with matching scan handlers, but without any scan handlers attached
to them, so the would not get any hotplug notifications after acpi_bus_trim().

The fix is to introduce a flag meaning "this device should get hotplug
notifications" and set it when we know there's a matching scan handler
for a device.

Thanks,
Rafael

---
drivers/acpi/scan.c | 137 ++++++++++++++++++++----------
drivers/pci/hotplug/acpiphp.h | 9 +-
drivers/pci/hotplug/acpiphp_glue.c | 164 +++++++------------------------------
include/acpi/acpi_bus.h | 25 +++++
4 files changed, 154 insertions(+), 181 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -450,43 +450,61 @@ static int acpi_scan_bus_check(struct ac
return 0;
}

+static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
+{
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ return acpi_scan_bus_check(adev);
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ return acpi_scan_device_check(adev);
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ case ACPI_OST_EC_OSPM_EJECT:
+ return acpi_scan_hot_remove(adev);
+ }
+ return -EINVAL;
+}
+
static void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
- int error;
+ int error = -ENODEV;

lock_device_hotplug();
mutex_lock(&acpi_scan_lock);

/*
* The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it may have become invalid before
+ * are holding acpi_scan_lock, but it might have become invalid before
* that lock was acquired.
*/
if (adev->handle == INVALID_ACPI_HANDLE)
- goto out;
+ goto err_out;

- switch (src) {
- case ACPI_NOTIFY_BUS_CHECK:
- error = acpi_scan_bus_check(adev);
- break;
- case ACPI_NOTIFY_DEVICE_CHECK:
- error = acpi_scan_device_check(adev);
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- case ACPI_OST_EC_OSPM_EJECT:
- error = acpi_scan_hot_remove(adev);
- break;
- default:
- error = -EINVAL;
- break;
+ if (adev->flags.hotplug_notify) {
+ error = acpi_generic_hotplug_event(adev, src);
+ } else {
+ int (*event)(struct acpi_device *, u32);
+
+ acpi_lock_hp_context();
+ event = adev->hp ? adev->hp->event : NULL;
+ acpi_unlock_hp_context();
+ /*
+ * There may be additional notify handlers for device objects
+ * without the .event() callback, so ignore them here.
+ */
+ if (event)
+ error = event(adev, src);
+ else
+ goto out;
}
if (!error)
ost_code = ACPI_OST_SC_SUCCESS;

- out:
+ err_out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
+
+ out:
acpi_bus_put_acpi_device(adev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
@@ -494,8 +512,8 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_scan_handler *handler = data;
+ u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;

@@ -503,26 +521,49 @@ static void acpi_hotplug_notify_cb(acpi_
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;
+
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;
+
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (!handler->hotplug.enabled) {
+ if (handler && !handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
- goto err_out;
+ goto out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
- default:
- /* non-hotplug event; possibly handled by other handler */
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
return;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
+ goto out;
+
+ default:
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto out;
}
+
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
adev = acpi_bus_get_acpi_device(handle);
if (!adev)
- goto err_out;
+ goto out;

status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
@@ -530,10 +571,22 @@ static void acpi_hotplug_notify_cb(acpi_

acpi_bus_put_acpi_device(adev);

- err_out:
+ out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+{
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb, data);
+}
+
+void acpi_remove_hotplug_notify_handler(acpi_handle handle)
+{
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb);
+}
+
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1976,33 +2029,21 @@ void acpi_scan_hotplug_enabled(struct ac
mutex_unlock(&acpi_scan_lock);
}

-static void acpi_scan_init_hotplug(acpi_handle handle, int type)
+static void acpi_scan_init_hotplug(struct acpi_device *adev)
{
- struct acpi_device_pnp pnp = {};
struct acpi_hardware_id *hwid;
- struct acpi_scan_handler *handler;
-
- INIT_LIST_HEAD(&pnp.ids);
- acpi_set_pnp_ids(handle, &pnp, type);

- if (!pnp.type.hardware_id)
- goto out;
+ list_for_each_entry(hwid, &adev->pnp.ids, list) {
+ struct acpi_scan_handler *handler;

- /*
- * This relies on the fact that acpi_install_notify_handler() will not
- * install the same notify handler routine twice for the same handle.
- */
- list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, handler);
- break;
- }
- }
+ if (!handler)
+ continue;

-out:
- acpi_free_pnp_ids(&pnp);
+ acpi_install_hotplug_notify_handler(adev->handle, handler);
+ adev->flags.hotplug_notify = true;
+ break;
+ }
}

static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
@@ -2026,12 +2067,12 @@ static acpi_status acpi_bus_check_add(ac
return AE_OK;
}

- acpi_scan_init_hotplug(handle, type);
-
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;

+ acpi_scan_init_hotplug(device);
+
out:
if (!*return_value)
*return_value = device;
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -116,12 +116,17 @@ struct acpiphp_func {
};

struct acpiphp_context {
+ struct acpi_hotplug_context hp;
struct acpiphp_func func;
- struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};

+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *fu

static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
{
- return func_to_context(func)->adev;
+ return func_to_context(func)->hp.self;
}

static inline acpi_handle func_to_handle(struct acpiphp_func *func)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -59,17 +59,12 @@
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);

-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, struct acpiphp_context *context);
static void free_bridge(struct kref *kref);

-static void acpiphp_context_handler(acpi_handle handle, void *context)
-{
- /* Intentionally empty. */
-}
-
/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @adev: ACPI device object to create the context for.
@@ -79,39 +74,27 @@ static void acpiphp_context_handler(acpi
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
- acpi_status status;

context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;

- context->adev = adev;
context->refcount = 1;
- status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
- if (ACPI_FAILURE(status)) {
- kfree(context);
- return NULL;
- }
+ acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event);
return context;
}

/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
- * @handle: ACPI object handle to get the context for.
+ * @adev: ACPI device object to get the context for.
*
* Call under acpi_hp_context_lock.
*/
-static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- struct acpiphp_context *context = NULL;
- acpi_status status;
- void *data;
+ struct acpiphp_context *context = to_acpiphp_context(adev->hp);

- status = acpi_get_data(handle, acpiphp_context_handler, &data);
- if (ACPI_SUCCESS(status)) {
- context = data;
- context->refcount++;
- }
+ context->refcount++;
return context;
}

@@ -129,7 +112,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->adev->handle, acpiphp_context_handler);
+ context->hp.self->hp = NULL;
kfree(context);
}

@@ -211,22 +194,13 @@ static void post_dock_fixups(acpi_handle

static void dock_event(acpi_handle handle, u32 type, void *data)
{
- struct acpiphp_context *context;
+ struct acpi_device *adev;

- acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
- if (!context || WARN_ON(context->adev->handle != handle)
- || context->func.parent->is_going_away) {
- acpi_unlock_hp_context();
- return;
+ adev = acpi_bus_get_acpi_device(handle);
+ if (adev) {
+ acpiphp_hotplug_event(adev, type);
+ acpi_bus_put_acpi_device(adev);
}
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_unlock_hp_context();
-
- hotplug_event(type, context);
-
- put_bridge(context->func.parent);
}

static const struct acpi_dock_ops acpiphp_dock_ops = {
@@ -397,25 +371,23 @@ static acpi_status register_slot(acpi_ha
}

/* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK)) {
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event,
- context);
- if (ACPI_FAILURE(status))
- acpi_handle_err(handle,
- "failed to install notify handler\n");
- }
+ if (!(newfunc->flags & FUNC_HAS_DCK))
+ acpi_install_hotplug_notify_handler(handle, NULL);

return AE_OK;
}

static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
{
+ struct acpi_device *adev = acpi_bus_get_acpi_device(handle);
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;

+ if (!adev)
+ return NULL;
+
acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (context) {
bridge = context->bridge;
if (bridge)
@@ -424,6 +396,7 @@ static struct acpiphp_bridge *acpiphp_ha
acpiphp_put_context(context);
}
acpi_unlock_hp_context();
+ acpi_bus_put_acpi_device(adev);
return bridge;
}

@@ -431,7 +404,6 @@ static void cleanup_bridge(struct acpiph
{
struct acpiphp_slot *slot;
struct acpiphp_func *func;
- acpi_status status;

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
@@ -440,13 +412,8 @@ static void cleanup_bridge(struct acpiph
if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);

- if (!(func->flags & FUNC_HAS_DCK)) {
- status = acpi_remove_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event);
- if (ACPI_FAILURE(status))
- pr_err("failed to remove notify handler\n");
- }
+ if (!(func->flags & FUNC_HAS_DCK))
+ acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
@@ -814,7 +781,7 @@ static int acpiphp_disable_and_eject_slo

static void hotplug_event(u32 type, struct acpiphp_context *context)
{
- acpi_handle handle = context->adev->handle;
+ acpi_handle handle = context->hp.self->handle;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
@@ -866,87 +833,24 @@ static void hotplug_event(u32 type, stru
put_bridge(bridge);
}

-static void hotplug_event_work(void *data, u32 type)
-{
- struct acpiphp_context *context = data;
-
- acpi_scan_lock_acquire();
-
- hotplug_event(type, context);
-
- acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(context->adev->handle, type,
- ACPI_OST_SC_SUCCESS, NULL);
- put_bridge(context->func.parent);
-}
-
-/**
- * handle_hotplug_event - handle ACPI hotplug event
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @data: pointer to acpiphp_context structure
- *
- * Handles ACPI event notification on slots.
- */
-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{
struct acpiphp_context *context;
- u32 ost_code = ACPI_OST_SC_SUCCESS;
- acpi_status status;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- case ACPI_NOTIFY_DEVICE_CHECK:
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS;
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }

acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
- if (!context || WARN_ON(context->adev->handle != handle)
- || context->func.parent->is_going_away)
- goto err_out;
-
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- status = acpi_hotplug_execute(hotplug_event_work, context, type);
- if (ACPI_SUCCESS(status)) {
+ context = acpiphp_get_context(adev);
+ if (!context || context->func.parent->is_going_away) {
acpi_unlock_hp_context();
- return;
+ return -ENODATA;
}
- put_bridge(context->func.parent);
-
- err_out:
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
acpi_unlock_hp_context();
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
+ hotplug_event(type, context);
+
+ put_bridge(context->func.parent);
+ return 0;
}

/**
@@ -999,7 +903,7 @@ void acpiphp_enumerate_slots(struct pci_
* bridge is not interesting to us either.
*/
acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (!context) {
acpi_unlock_hp_context();
put_device(&bus->dev);
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -137,6 +137,16 @@ struct acpi_scan_handler {
};

/*
+ * ACPI Hotplug Context
+ * --------------------
+ */
+
+struct acpi_hotplug_context {
+ struct acpi_device *self;
+ int (*event)(struct acpi_device *, u32);
+};
+
+/*
* ACPI Driver
* -----------
*/
@@ -190,7 +200,8 @@ struct acpi_device_flags {
u32 initialized:1;
u32 visited:1;
u32 no_hotplug:1;
- u32 reserved:24;
+ u32 hotplug_notify:1;
+ u32 reserved:23;
};

/* File System */
@@ -329,6 +340,7 @@ struct acpi_device {
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_scan_handler *handler;
+ struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
void *driver_data;
struct device dev;
@@ -351,6 +363,15 @@ static inline void acpi_set_device_statu
*((u32 *)&adev->status) = sta;
}

+static inline void acpi_set_hp_context(struct acpi_device *adev,
+ struct acpi_hotplug_context *hp,
+ int (*event)(struct acpi_device *, u32))
+{
+ hp->self = adev;
+ hp->event = event;
+ adev->hp = hp;
+}
+
/* acpi_device.dev.bus == &acpi_bus_type */
extern struct bus_type acpi_bus_type;

@@ -425,6 +446,8 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver

2014-02-06 12:54:56

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update][PATCH 22/24] ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

From: Rafael J. Wysocki <[email protected]>
Subject: ACPI / hotplug / PCI: Simplify acpi_install_hotplug_notify_handler()

Since acpi_hotplug_notify_cb() does not use its data argument any
more, the second argument of acpi_install_hotplug_notify_handler()
can be dropped, so do that and update its callers accordingly.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---

This one had to be updated after patch [20/24] had been updated.

Thanks,
Rafael

---
drivers/acpi/scan.c | 6 +++---
drivers/pci/hotplug/acpiphp_glue.c | 2 +-
include/acpi/acpi_bus.h | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -577,10 +577,10 @@ static void acpi_hotplug_notify_cb(acpi_
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+void acpi_install_hotplug_notify_handler(acpi_handle handle)
{
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, data);
+ acpi_hotplug_notify_cb, NULL);
}

void acpi_remove_hotplug_notify_handler(acpi_handle handle)
@@ -2040,7 +2040,7 @@ static void acpi_scan_init_hotplug(struc
if (!handler)
continue;

- acpi_install_hotplug_notify_handler(adev->handle, handler);
+ acpi_install_hotplug_notify_handler(adev->handle);
adev->flags.hotplug_notify = true;
break;
}
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -372,7 +372,7 @@ static acpi_status register_slot(acpi_ha

/* install notify handler */
if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle, NULL);
+ acpi_install_hotplug_notify_handler(handle);

return AE_OK;
}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -446,7 +446,7 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_install_hotplug_notify_handler(acpi_handle handle);
void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**

2014-02-06 12:55:50

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update][PATCH 23/24] ACPI / hotplug / PCI: Hotplug notifications from acpi_bus_notify()

From: Rafael J. Wysocki <[email protected]>
Subject: ACPI / hotplug / PCI: Hotplug notifications from acpi_bus_notify()

Since acpi_bus_notify() is executed on all notifications for all
devices anyway, make it execute acpi_device_hotplug() for all
hotplug events instead of installing notify handlers pointing to
the same function for all hotplug devices.

This change reduces both the size and complexity of ACPI-based device
hotplug code. Moreover, since acpi_device_hotplug() only does
significant things for devices that have either an ACPI scan handler,
or a hotplug context with .eject() defined, and those devices
had notify handlers pointing to acpi_hotplug_notify_cb() installed
before anyway, this modification shouldn't change functionality.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---

This one had to be updated after patch [20/24] had been updated.

Thanks,
Rafael

---
drivers/acpi/bus.c | 61 +++++++++++++++++----------
drivers/acpi/internal.h | 1
drivers/acpi/scan.c | 81 ++-----------------------------------
drivers/pci/hotplug/acpiphp.h | 1
drivers/pci/hotplug/acpiphp_glue.c | 24 +++++-----
include/acpi/acpi_bus.h | 2
6 files changed, 57 insertions(+), 113 deletions(-)

Index: linux-pm/drivers/acpi/bus.c
===================================================================
--- linux-pm.orig/drivers/acpi/bus.c
+++ linux-pm/drivers/acpi/bus.c
@@ -340,62 +340,77 @@ static void acpi_bus_osc_support(void)
*/
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
{
- struct acpi_device *device;
+ struct acpi_device *adev;
struct acpi_driver *driver;
-
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
- type, handle));
+ acpi_status status;
+ u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

switch (type) {
-
case ACPI_NOTIFY_BUS_CHECK:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;

case ACPI_NOTIFY_DEVICE_CHECK:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;

case ACPI_NOTIFY_DEVICE_WAKE:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n");
break;

case ACPI_NOTIFY_EJECT_REQUEST:
- /* TBD */
+ acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
break;

case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
+ acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n");
/* TBD: Exactly what does 'light' mean? */
break;

case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- /* TBD */
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
break;

case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- /* TBD */
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
break;

case ACPI_NOTIFY_POWER_FAULT:
- /* TBD */
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
break;

default:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Received unknown/unsupported notification [%08x]\n",
- type));
- break;
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto err;
}

- device = acpi_bus_get_acpi_device(handle);
- if (device) {
- driver = device->driver;
- if (driver && driver->ops.notify &&
- (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
- driver->ops.notify(device, type);
+ adev = acpi_bus_get_acpi_device(handle);
+ if (!adev)
+ goto err;
+
+ driver = adev->driver;
+ if (driver && driver->ops.notify &&
+ (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
+ driver->ops.notify(adev, type);

- acpi_bus_put_acpi_device(device);
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
+ if (ACPI_SUCCESS(status))
+ return;
+ default:
+ break;
}
+ acpi_bus_put_acpi_device(adev);
+ return;
+
+ err:
+ acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

/* --------------------------------------------------------------------------
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -73,6 +73,7 @@ static inline void acpi_lpss_init(void)
#endif

bool acpi_queue_hotplug_work(struct work_struct *work);
+void acpi_device_hotplug(void *data, u32 src);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);

/* --------------------------------------------------------------------------
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -470,7 +470,7 @@ static int acpi_generic_hotplug_event(st
return -EINVAL;
}

-static void acpi_device_hotplug(void *data, u32 src)
+void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
@@ -520,75 +520,6 @@ static void acpi_device_hotplug(void *da
unlock_device_hotplug();
}

-static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
-{
- u32 ost_code = ACPI_OST_SC_SUCCESS;
- struct acpi_device *adev;
- acpi_status status;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
- break;
-
- case ACPI_NOTIFY_DEVICE_CHECK:
- acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
- break;
-
- case ACPI_NOTIFY_EJECT_REQUEST:
- acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }
-
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
- adev = acpi_bus_get_acpi_device(handle);
- if (!adev)
- goto out;
-
- status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
- if (ACPI_SUCCESS(status))
- return;
-
- acpi_bus_put_acpi_device(adev);
-
- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
-}
-
-void acpi_install_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, NULL);
-}
-
-void acpi_remove_hotplug_notify_handler(acpi_handle handle)
-{
- acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb);
-}
-
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2037,12 +1968,10 @@ static void acpi_scan_init_hotplug(struc
struct acpi_scan_handler *handler;

handler = acpi_scan_match_handler(hwid->id, NULL);
- if (!handler)
- continue;
-
- acpi_install_hotplug_notify_handler(adev->handle);
- adev->flags.hotplug_notify = true;
- break;
+ if (handler) {
+ adev->flags.hotplug_notify = true;
+ break;
+ }
}
}

Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -293,7 +293,6 @@ static acpi_status register_slot(acpi_ha
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
- acpi_unlock_hp_context();

if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
@@ -301,8 +300,14 @@ static acpi_status register_slot(acpi_ha
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;

+ /*
+ * Dock stations' notify handler should be used for dock devices instead
+ * of the common one, so clear hp.event in their contexts.
+ */
if (acpi_has_method(handle, "_DCK"))
- newfunc->flags |= FUNC_HAS_DCK;
+ context->hp.event = NULL;
+
+ acpi_unlock_hp_context();

/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
@@ -370,10 +375,6 @@ static acpi_status register_slot(acpi_ha
pr_debug("failed to register dock device\n");
}

- /* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK))
- acpi_install_hotplug_notify_handler(handle);
-
return AE_OK;
}

@@ -407,13 +408,14 @@ static void cleanup_bridge(struct acpiph

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
- acpi_handle handle = func_to_handle(func);
+ struct acpi_device *adev = func_to_acpi_device(func);

- if (is_dock_device(handle))
- unregister_hotplug_dock_device(handle);
+ if (is_dock_device(adev->handle))
+ unregister_hotplug_dock_device(adev->handle);

- if (!(func->flags & FUNC_HAS_DCK))
- acpi_remove_hotplug_notify_handler(handle);
+ acpi_lock_hp_context();
+ adev->hp->event = NULL;
+ acpi_unlock_hp_context();
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -446,8 +446,6 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
-void acpi_install_hotplug_notify_handler(acpi_handle handle);
-void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -167,7 +167,6 @@ struct acpiphp_attention_info

#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
-#define FUNC_HAS_DCK (0x00000004)

/* function prototypes */

2014-02-06 23:20:47

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update 2x][PATCH 20/24] ACPI / hotplug / PCI: Consolidate ACPIPHP with ACPI core hotplug

From: Rafael J. Wysocki <[email protected]>

The ACPI-based PCI hotplug (ACPIPHP) code currently attaches its
hotplug context objects directly to ACPI namespace nodes representing
hotplug devices. However, after recent changes causing struct
acpi_device to be created for every namespace node representing a
device (regardless of its status), that is not necessary any more.
Moreover, it's vulnerable to the theoretical issue that the ACPI
handle passed in the context between handle_hotplug_event() and
hotplug_event_work() may become invalid in the meantime (as a
result of a concurrent table unload).

In principle, this issue might be addressed by adding a non-empty
release handler for ACPIPHP hotplug context objects analogous to
acpi_scan_drop_device(), but that would duplicate the code in that
function and in acpi_device_del_work_fn(). For this reason, it's
better to modify ACPIPHP to attach its device hotplug contexts to
struct device objects representing hotplug devices and make it
use acpi_hotplug_notify_cb() as its notify handler. At the same
time, acpi_device_hotplug() can be modified to dispatch the new
.hp.event() callback pointing to acpiphp_hotplug_event() from ACPI
device objects associated with PCI devices or use the generic
ACPI device hotplug code for device objects with matching scan
handlers.

This allows the existing code duplication between ACPIPHP and the
ACPI core to be reduced too and makes further ACPI-based device
hotplug consolidation possible.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---

This update fixes the bug reported here: https://lkml.org/lkml/2014/2/6/212

None of the other patches in the series need to be updated because of that.

Thanks!

---
drivers/acpi/scan.c | 137 +++++++++++++++++++-----------
drivers/pci/hotplug/acpiphp.h | 9 +
drivers/pci/hotplug/acpiphp_glue.c | 168 ++++++++-----------------------------
include/acpi/acpi_bus.h | 25 +++++
4 files changed, 158 insertions(+), 181 deletions(-)

Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -450,43 +450,61 @@ static int acpi_scan_bus_check(struct ac
return 0;
}

+static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
+{
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ return acpi_scan_bus_check(adev);
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ return acpi_scan_device_check(adev);
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ case ACPI_OST_EC_OSPM_EJECT:
+ return acpi_scan_hot_remove(adev);
+ }
+ return -EINVAL;
+}
+
static void acpi_device_hotplug(void *data, u32 src)
{
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
- int error;
+ int error = -ENODEV;

lock_device_hotplug();
mutex_lock(&acpi_scan_lock);

/*
* The device object's ACPI handle cannot become invalid as long as we
- * are holding acpi_scan_lock, but it may have become invalid before
+ * are holding acpi_scan_lock, but it might have become invalid before
* that lock was acquired.
*/
if (adev->handle == INVALID_ACPI_HANDLE)
- goto out;
+ goto err_out;

- switch (src) {
- case ACPI_NOTIFY_BUS_CHECK:
- error = acpi_scan_bus_check(adev);
- break;
- case ACPI_NOTIFY_DEVICE_CHECK:
- error = acpi_scan_device_check(adev);
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- case ACPI_OST_EC_OSPM_EJECT:
- error = acpi_scan_hot_remove(adev);
- break;
- default:
- error = -EINVAL;
- break;
+ if (adev->flags.hotplug_notify) {
+ error = acpi_generic_hotplug_event(adev, src);
+ } else {
+ int (*event)(struct acpi_device *, u32);
+
+ acpi_lock_hp_context();
+ event = adev->hp ? adev->hp->event : NULL;
+ acpi_unlock_hp_context();
+ /*
+ * There may be additional notify handlers for device objects
+ * without the .event() callback, so ignore them here.
+ */
+ if (event)
+ error = event(adev, src);
+ else
+ goto out;
}
if (!error)
ost_code = ACPI_OST_SC_SUCCESS;

- out:
+ err_out:
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
+
+ out:
acpi_bus_put_acpi_device(adev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
@@ -494,8 +512,8 @@ static void acpi_device_hotplug(void *da

static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
{
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_scan_handler *handler = data;
+ u32 ost_code = ACPI_OST_SC_SUCCESS;
struct acpi_device *adev;
acpi_status status;

@@ -503,26 +521,49 @@ static void acpi_hotplug_notify_cb(acpi_
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
break;
+
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
break;
+
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- if (!handler->hotplug.enabled) {
+ if (handler && !handler->hotplug.enabled) {
acpi_handle_err(handle, "Eject disabled\n");
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
- goto err_out;
+ goto out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
- default:
- /* non-hotplug event; possibly handled by other handler */
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
return;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a frequency mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ acpi_handle_err(handle, "Device cannot be configured due "
+ "to a bus mode mismatch\n");
+ goto out;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ acpi_handle_err(handle, "Device has suffered a power fault\n");
+ goto out;
+
+ default:
+ acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
+ ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
+ goto out;
}
+
+ ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
adev = acpi_bus_get_acpi_device(handle);
if (!adev)
- goto err_out;
+ goto out;

status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
if (ACPI_SUCCESS(status))
@@ -530,10 +571,22 @@ static void acpi_hotplug_notify_cb(acpi_

acpi_bus_put_acpi_device(adev);

- err_out:
+ out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
}

+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data)
+{
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb, data);
+}
+
+void acpi_remove_hotplug_notify_handler(acpi_handle handle)
+{
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ acpi_hotplug_notify_cb);
+}
+
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1976,33 +2029,21 @@ void acpi_scan_hotplug_enabled(struct ac
mutex_unlock(&acpi_scan_lock);
}

-static void acpi_scan_init_hotplug(acpi_handle handle, int type)
+static void acpi_scan_init_hotplug(struct acpi_device *adev)
{
- struct acpi_device_pnp pnp = {};
struct acpi_hardware_id *hwid;
- struct acpi_scan_handler *handler;
-
- INIT_LIST_HEAD(&pnp.ids);
- acpi_set_pnp_ids(handle, &pnp, type);

- if (!pnp.type.hardware_id)
- goto out;
+ list_for_each_entry(hwid, &adev->pnp.ids, list) {
+ struct acpi_scan_handler *handler;

- /*
- * This relies on the fact that acpi_install_notify_handler() will not
- * install the same notify handler routine twice for the same handle.
- */
- list_for_each_entry(hwid, &pnp.ids, list) {
handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_hotplug_notify_cb, handler);
- break;
- }
- }
+ if (!handler)
+ continue;

-out:
- acpi_free_pnp_ids(&pnp);
+ acpi_install_hotplug_notify_handler(adev->handle, handler);
+ adev->flags.hotplug_notify = true;
+ break;
+ }
}

static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
@@ -2026,12 +2067,12 @@ static acpi_status acpi_bus_check_add(ac
return AE_OK;
}

- acpi_scan_init_hotplug(handle, type);
-
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;

+ acpi_scan_init_hotplug(device);
+
out:
if (!*return_value)
*return_value = device;
Index: linux-pm/drivers/pci/hotplug/acpiphp.h
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-pm/drivers/pci/hotplug/acpiphp.h
@@ -116,12 +116,17 @@ struct acpiphp_func {
};

struct acpiphp_context {
+ struct acpi_hotplug_context hp;
struct acpiphp_func func;
- struct acpi_device *adev;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};

+static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp)
+{
+ return container_of(hp, struct acpiphp_context, hp);
+}
+
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *fu

static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func)
{
- return func_to_context(func)->adev;
+ return func_to_context(func)->hp.self;
}

static inline acpi_handle func_to_handle(struct acpiphp_func *func)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -59,17 +59,12 @@
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);

-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, struct acpiphp_context *context);
static void free_bridge(struct kref *kref);

-static void acpiphp_context_handler(acpi_handle handle, void *context)
-{
- /* Intentionally empty. */
-}
-
/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @adev: ACPI device object to create the context for.
@@ -79,39 +74,31 @@ static void acpiphp_context_handler(acpi
static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
{
struct acpiphp_context *context;
- acpi_status status;

context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;

- context->adev = adev;
context->refcount = 1;
- status = acpi_attach_data(adev->handle, acpiphp_context_handler, context);
- if (ACPI_FAILURE(status)) {
- kfree(context);
- return NULL;
- }
+ acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event);
return context;
}

/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
- * @handle: ACPI object handle to get the context for.
+ * @adev: ACPI device object to get the context for.
*
* Call under acpi_hp_context_lock.
*/
-static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
+static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev)
{
- struct acpiphp_context *context = NULL;
- acpi_status status;
- void *data;
+ struct acpiphp_context *context;

- status = acpi_get_data(handle, acpiphp_context_handler, &data);
- if (ACPI_SUCCESS(status)) {
- context = data;
- context->refcount++;
- }
+ if (!adev->hp)
+ return NULL;
+
+ context = to_acpiphp_context(adev->hp);
+ context->refcount++;
return context;
}

@@ -129,7 +116,7 @@ static void acpiphp_put_context(struct a
return;

WARN_ON(context->bridge);
- acpi_detach_data(context->adev->handle, acpiphp_context_handler);
+ context->hp.self->hp = NULL;
kfree(context);
}

@@ -211,22 +198,13 @@ static void post_dock_fixups(acpi_handle

static void dock_event(acpi_handle handle, u32 type, void *data)
{
- struct acpiphp_context *context;
+ struct acpi_device *adev;

- acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
- if (!context || WARN_ON(context->adev->handle != handle)
- || context->func.parent->is_going_away) {
- acpi_unlock_hp_context();
- return;
+ adev = acpi_bus_get_acpi_device(handle);
+ if (adev) {
+ acpiphp_hotplug_event(adev, type);
+ acpi_bus_put_acpi_device(adev);
}
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_unlock_hp_context();
-
- hotplug_event(type, context);
-
- put_bridge(context->func.parent);
}

static const struct acpi_dock_ops acpiphp_dock_ops = {
@@ -397,25 +375,23 @@ static acpi_status register_slot(acpi_ha
}

/* install notify handler */
- if (!(newfunc->flags & FUNC_HAS_DCK)) {
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event,
- context);
- if (ACPI_FAILURE(status))
- acpi_handle_err(handle,
- "failed to install notify handler\n");
- }
+ if (!(newfunc->flags & FUNC_HAS_DCK))
+ acpi_install_hotplug_notify_handler(handle, NULL);

return AE_OK;
}

static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
{
+ struct acpi_device *adev = acpi_bus_get_acpi_device(handle);
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;

+ if (!adev)
+ return NULL;
+
acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (context) {
bridge = context->bridge;
if (bridge)
@@ -424,6 +400,7 @@ static struct acpiphp_bridge *acpiphp_ha
acpiphp_put_context(context);
}
acpi_unlock_hp_context();
+ acpi_bus_put_acpi_device(adev);
return bridge;
}

@@ -431,7 +408,6 @@ static void cleanup_bridge(struct acpiph
{
struct acpiphp_slot *slot;
struct acpiphp_func *func;
- acpi_status status;

list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
@@ -440,13 +416,8 @@ static void cleanup_bridge(struct acpiph
if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);

- if (!(func->flags & FUNC_HAS_DCK)) {
- status = acpi_remove_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- handle_hotplug_event);
- if (ACPI_FAILURE(status))
- pr_err("failed to remove notify handler\n");
- }
+ if (!(func->flags & FUNC_HAS_DCK))
+ acpi_remove_hotplug_notify_handler(handle);
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
@@ -814,7 +785,7 @@ static int acpiphp_disable_and_eject_slo

static void hotplug_event(u32 type, struct acpiphp_context *context)
{
- acpi_handle handle = context->adev->handle;
+ acpi_handle handle = context->hp.self->handle;
struct acpiphp_func *func = &context->func;
struct acpiphp_slot *slot = func->slot;
struct acpiphp_bridge *bridge;
@@ -866,87 +837,24 @@ static void hotplug_event(u32 type, stru
put_bridge(bridge);
}

-static void hotplug_event_work(void *data, u32 type)
-{
- struct acpiphp_context *context = data;
-
- acpi_scan_lock_acquire();
-
- hotplug_event(type, context);
-
- acpi_scan_lock_release();
- acpi_evaluate_hotplug_ost(context->adev->handle, type,
- ACPI_OST_SC_SUCCESS, NULL);
- put_bridge(context->func.parent);
-}
-
-/**
- * handle_hotplug_event - handle ACPI hotplug event
- * @handle: Notify()'ed acpi_handle
- * @type: Notify code
- * @data: pointer to acpiphp_context structure
- *
- * Handles ACPI event notification on slots.
- */
-static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
+static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
{
struct acpiphp_context *context;
- u32 ost_code = ACPI_OST_SC_SUCCESS;
- acpi_status status;
-
- switch (type) {
- case ACPI_NOTIFY_BUS_CHECK:
- case ACPI_NOTIFY_DEVICE_CHECK:
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS;
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
- break;
-
- case ACPI_NOTIFY_DEVICE_WAKE:
- return;
-
- case ACPI_NOTIFY_FREQUENCY_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a frequency mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_BUS_MODE_MISMATCH:
- acpi_handle_err(handle, "Device cannot be configured due "
- "to a bus mode mismatch\n");
- goto out;
-
- case ACPI_NOTIFY_POWER_FAULT:
- acpi_handle_err(handle, "Device has suffered a power fault\n");
- goto out;
-
- default:
- acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
- ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY;
- goto out;
- }

acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
- if (!context || WARN_ON(context->adev->handle != handle)
- || context->func.parent->is_going_away)
- goto err_out;
-
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- status = acpi_hotplug_execute(hotplug_event_work, context, type);
- if (ACPI_SUCCESS(status)) {
+ context = acpiphp_get_context(adev);
+ if (!context || context->func.parent->is_going_away) {
acpi_unlock_hp_context();
- return;
+ return -ENODATA;
}
- put_bridge(context->func.parent);
-
- err_out:
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
acpi_unlock_hp_context();
- ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;

- out:
- acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
+ hotplug_event(type, context);
+
+ put_bridge(context->func.parent);
+ return 0;
}

/**
@@ -999,7 +907,7 @@ void acpiphp_enumerate_slots(struct pci_
* bridge is not interesting to us either.
*/
acpi_lock_hp_context();
- context = acpiphp_get_context(handle);
+ context = acpiphp_get_context(adev);
if (!context) {
acpi_unlock_hp_context();
put_device(&bus->dev);
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -137,6 +137,16 @@ struct acpi_scan_handler {
};

/*
+ * ACPI Hotplug Context
+ * --------------------
+ */
+
+struct acpi_hotplug_context {
+ struct acpi_device *self;
+ int (*event)(struct acpi_device *, u32);
+};
+
+/*
* ACPI Driver
* -----------
*/
@@ -190,7 +200,8 @@ struct acpi_device_flags {
u32 initialized:1;
u32 visited:1;
u32 no_hotplug:1;
- u32 reserved:24;
+ u32 hotplug_notify:1;
+ u32 reserved:23;
};

/* File System */
@@ -329,6 +340,7 @@ struct acpi_device {
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_scan_handler *handler;
+ struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
void *driver_data;
struct device dev;
@@ -351,6 +363,15 @@ static inline void acpi_set_device_statu
*((u32 *)&adev->status) = sta;
}

+static inline void acpi_set_hp_context(struct acpi_device *adev,
+ struct acpi_hotplug_context *hp,
+ int (*event)(struct acpi_device *, u32))
+{
+ hp->self = adev;
+ hp->event = event;
+ adev->hp = hp;
+}
+
/* acpi_device.dev.bus == &acpi_bus_type */
extern struct bus_type acpi_bus_type;

@@ -425,6 +446,8 @@ static inline bool acpi_device_enumerate
typedef void (*acpi_hp_callback)(void *data, u32 src);

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
+void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data);
+void acpi_remove_hotplug_notify_handler(acpi_handle handle);

/**
* module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver

2014-02-11 00:20:09

by Bjorn Helgaas

[permalink] [raw]
Subject: Re: [PATCH 0/11] ACPI / hotplug / PCI: Updates on top of changes merged recently

On Mon, Jan 27, 2014 at 01:37:17AM +0100, Rafael J. Wysocki wrote:
> Hi All,
>
> ACPIPHP can be simplified a bit on top of some PCI and ACPI changes merged
> recently and the following series of patches implements those simplifications:
>
> [1/11] Fix up two kerneldoc comments in acpiphp_glue.c.
> [2/11] Get rid of an unnecessary label in register_slot().
> [3/11] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
> [4/11] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
> [5/11] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
> [6/11] Drop acpiphp_bus_add() (which has only one user).
> [7/11] Drop crit_sect mutexes (that are redundant).
> [8/11] Clean up the usage of the slot variable in hotplug_event().
> [9/11] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
> [10/11] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
> [11/11] Drop handle argument from the member functions of struct acpi_dock_ops.

I assume you'll handle all these through your tree, Rafael. Let me know if
you need me to do anything.

Bjorn

> All of that is relateively straightforward, but I have some more intrusive changes
> on top of it in the works. They will be posted separately later this week.
>
> Thanks!
>
> --
> I speak only for myself.
> Rafael J. Wysocki, Intel Open Source Technology Center.

2014-02-11 12:39:24

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 0/11] ACPI / hotplug / PCI: Updates on top of changes merged recently

On Monday, February 10, 2014 05:19:58 PM Bjorn Helgaas wrote:
> On Mon, Jan 27, 2014 at 01:37:17AM +0100, Rafael J. Wysocki wrote:
> > Hi All,
> >
> > ACPIPHP can be simplified a bit on top of some PCI and ACPI changes merged
> > recently and the following series of patches implements those simplifications:
> >
> > [1/11] Fix up two kerneldoc comments in acpiphp_glue.c.
> > [2/11] Get rid of an unnecessary label in register_slot().
> > [3/11] Drop acpiphp_bus_trim() and use acpi_bus_trim() instead of it directly.
> > [4/11] Move the acpi_bus_get_device() call out of acpiphp_no_hotplug().
> > [5/11] Store struct acpi_device pointers instead of ACPI handles in struct acpiphp_context.
> > [6/11] Drop acpiphp_bus_add() (which has only one user).
> > [7/11] Drop crit_sect mutexes (that are redundant).
> > [8/11] Clean up the usage of the slot variable in hotplug_event().
> > [9/11] Drop dev_in_slot() and rework disable_slot() to walk bus->devices directly.
> > [10/11] Use acpi_handle_debug() in hotplug_event() instead of open-coded stuff.
> > [11/11] Drop handle argument from the member functions of struct acpi_dock_ops.
>
> I assume you'll handle all these through your tree, Rafael. Let me know if
> you need me to do anything.

Yes, I'll handle these, thanks!

Rafael