Skip to content

Commit c56610a

Browse files
committed
ACPI: bus: Rework system-level device notification handling
For ACPI drivers that provide a ->notify() callback and set ACPI_DRIVER_ALL_NOTIFY_EVENTS in their flags, that callback can be invoked while either the ->add() or the ->remove() callback is running without any synchronization at the bus type level which is counter to the common-sense expectation that notification handling should only be enabled when the driver is actually bound to the device. As a result, if the driver is not careful enough, it's ->notify() callback may crash when it is invoked too early or too late [1]. This issue has been amplified by commit d6fb6ee ("ACPI: bus: Drop driver member of struct acpi_device") that made acpi_bus_notify() check for the presence of the driver and its ->notify() callback directly instead of using an extra driver pointer that was only set and cleared by the bus type code, but it was present before that commit although it was harder to reproduce then. It can be addressed by using the observation that acpi_device_install_notify_handler() can be modified to install the handler for all types of events when ACPI_DRIVER_ALL_NOTIFY_EVENTS is set in the driver flags, in which case acpi_bus_notify() will not need to invoke the driver's ->notify() callback any more and that callback will only be invoked after acpi_device_install_notify_handler() has run and before acpi_device_remove_notify_handler() runs, which implies the correct ordering with respect to the other ACPI driver callbacks. Modify the code accordingly and while at it, drop two redundant local variables from acpi_bus_notify() and turn its description comment into a proper kerneldoc one. Fixes: d6fb6ee ("ACPI: bus: Drop driver member of struct acpi_device") Link: https://lore.kernel.org/linux-acpi/[email protected] # [1] Reported-by: Pierre Asselin <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]> Tested-by: Pierre Asselin <[email protected]>
1 parent 197b6b6 commit c56610a

File tree

1 file changed

+37
-46
lines changed

1 file changed

+37
-46
lines changed

drivers/acpi/bus.c

Lines changed: 37 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -459,85 +459,67 @@ static void acpi_bus_osc_negotiate_usb_control(void)
459459
Notification Handling
460460
-------------------------------------------------------------------------- */
461461

462-
/*
463-
* acpi_bus_notify
464-
* ---------------
465-
* Callback for all 'system-level' device notifications (values 0x00-0x7F).
462+
/**
463+
* acpi_bus_notify - Global system-level (0x00-0x7F) notifications handler
464+
* @handle: Target ACPI object.
465+
* @type: Notification type.
466+
* @data: Ignored.
467+
*
468+
* This only handles notifications related to device hotplug.
466469
*/
467470
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
468471
{
469472
struct acpi_device *adev;
470-
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
471-
bool hotplug_event = false;
472473

473474
switch (type) {
474475
case ACPI_NOTIFY_BUS_CHECK:
475476
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
476-
hotplug_event = true;
477477
break;
478478

479479
case ACPI_NOTIFY_DEVICE_CHECK:
480480
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
481-
hotplug_event = true;
482481
break;
483482

484483
case ACPI_NOTIFY_DEVICE_WAKE:
485484
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n");
486-
break;
485+
return;
487486

488487
case ACPI_NOTIFY_EJECT_REQUEST:
489488
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
490-
hotplug_event = true;
491489
break;
492490

493491
case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
494492
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n");
495493
/* TBD: Exactly what does 'light' mean? */
496-
break;
494+
return;
497495

498496
case ACPI_NOTIFY_FREQUENCY_MISMATCH:
499497
acpi_handle_err(handle, "Device cannot be configured due "
500498
"to a frequency mismatch\n");
501-
break;
499+
return;
502500

503501
case ACPI_NOTIFY_BUS_MODE_MISMATCH:
504502
acpi_handle_err(handle, "Device cannot be configured due "
505503
"to a bus mode mismatch\n");
506-
break;
504+
return;
507505

508506
case ACPI_NOTIFY_POWER_FAULT:
509507
acpi_handle_err(handle, "Device has suffered a power fault\n");
510-
break;
508+
return;
511509

512510
default:
513511
acpi_handle_debug(handle, "Unknown event type 0x%x\n", type);
514-
break;
512+
return;
515513
}
516514

517515
adev = acpi_get_acpi_dev(handle);
518-
if (!adev)
519-
goto err;
520-
521-
if (adev->dev.driver) {
522-
struct acpi_driver *driver = to_acpi_driver(adev->dev.driver);
523-
524-
if (driver && driver->ops.notify &&
525-
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
526-
driver->ops.notify(adev, type);
527-
}
528-
529-
if (!hotplug_event) {
530-
acpi_put_acpi_dev(adev);
531-
return;
532-
}
533516

534-
if (ACPI_SUCCESS(acpi_hotplug_schedule(adev, type)))
517+
if (adev && ACPI_SUCCESS(acpi_hotplug_schedule(adev, type)))
535518
return;
536519

537520
acpi_put_acpi_dev(adev);
538521

539-
err:
540-
acpi_evaluate_ost(handle, type, ost_code, NULL);
522+
acpi_evaluate_ost(handle, type, ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
541523
}
542524

543525
static void acpi_notify_device(acpi_handle handle, u32 event, void *data)
@@ -562,42 +544,51 @@ static u32 acpi_device_fixed_event(void *data)
562544
return ACPI_INTERRUPT_HANDLED;
563545
}
564546

565-
static int acpi_device_install_notify_handler(struct acpi_device *device)
547+
static int acpi_device_install_notify_handler(struct acpi_device *device,
548+
struct acpi_driver *acpi_drv)
566549
{
567550
acpi_status status;
568551

569-
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
552+
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
570553
status =
571554
acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
572555
acpi_device_fixed_event,
573556
device);
574-
else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
557+
} else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) {
575558
status =
576559
acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
577560
acpi_device_fixed_event,
578561
device);
579-
else
580-
status = acpi_install_notify_handler(device->handle,
581-
ACPI_DEVICE_NOTIFY,
562+
} else {
563+
u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
564+
ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY;
565+
566+
status = acpi_install_notify_handler(device->handle, type,
582567
acpi_notify_device,
583568
device);
569+
}
584570

585571
if (ACPI_FAILURE(status))
586572
return -EINVAL;
587573
return 0;
588574
}
589575

590-
static void acpi_device_remove_notify_handler(struct acpi_device *device)
576+
static void acpi_device_remove_notify_handler(struct acpi_device *device,
577+
struct acpi_driver *acpi_drv)
591578
{
592-
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
579+
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
593580
acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
594581
acpi_device_fixed_event);
595-
else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
582+
} else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) {
596583
acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
597584
acpi_device_fixed_event);
598-
else
599-
acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
585+
} else {
586+
u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
587+
ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY;
588+
589+
acpi_remove_notify_handler(device->handle, type,
600590
acpi_notify_device);
591+
}
601592
}
602593

603594
/* Handle events targeting \_SB device (at present only graceful shutdown) */
@@ -1039,7 +1030,7 @@ static int acpi_device_probe(struct device *dev)
10391030
acpi_drv->name, acpi_dev->pnp.bus_id);
10401031

10411032
if (acpi_drv->ops.notify) {
1042-
ret = acpi_device_install_notify_handler(acpi_dev);
1033+
ret = acpi_device_install_notify_handler(acpi_dev, acpi_drv);
10431034
if (ret) {
10441035
if (acpi_drv->ops.remove)
10451036
acpi_drv->ops.remove(acpi_dev);
@@ -1062,7 +1053,7 @@ static void acpi_device_remove(struct device *dev)
10621053
struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
10631054

10641055
if (acpi_drv->ops.notify)
1065-
acpi_device_remove_notify_handler(acpi_dev);
1056+
acpi_device_remove_notify_handler(acpi_dev, acpi_drv);
10661057

10671058
if (acpi_drv->ops.remove)
10681059
acpi_drv->ops.remove(acpi_dev);

0 commit comments

Comments
 (0)