⬢ DragonFlyBSD Kernel Audit
DF-4410 / fix.diff
← back to finding ↓ download raw
fix.diff for DF-4410 — quiesce pending acpi_asus_led_task work before
destroying the LEDs/softc in detach (dying flag + outstanding counter +
wait).  Authored against the read-only sys/ tree; never applied there.
fix_status: not_testable (no ATK0100/ASUS010 firmware and no /dev/led on the
audit guest; see env.txt).

--- a/sys/dev/acpica/acpi_asus/acpi_asus.c
+++ b/sys/dev/acpica/acpi_asus/acpi_asus.c
@@ -116,6 +116,11 @@
 struct acpi_asus_softc {
 	device_t		dev;
 	ACPI_HANDLE		handle;
 	ACPI_HANDLE		lcdd_handle;
+
+	struct lock		led_lock;
+	int			led_dying;	/* no new tasks once set */
+	int			led_pending;	/* outstanding acpi_asus_led_task */
 
 	struct acpi_asus_model	*model;
@@ -732,6 +737,8 @@
 	sc = device_get_softc(dev);
 	acpi_sc = acpi_device_get_parent_softc(dev);
 
+	lockinit(&sc->led_lock, "asus led", 0, 0);
+
 	/* Build sysctl tree */
 	sysctl_ctx_init(&sc->sysctl_ctx);
@@ -838,6 +845,18 @@
 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 
 	sc = device_get_softc(dev);
+
+	/* Stop new LED tasks and drain pending ones before freeing anything
+	 * they touch (ledsc via led_destroy, softc via newbus). */
+	lockmgr(&sc->led_lock, LK_EXCLUSIVE);
+	sc->led_dying = 1;
+	while (sc->led_pending > 0)
+		lksleep(&sc->led_pending, &sc->led_lock, 0, "asusled", hz);
+	lockmgr(&sc->led_lock, LK_RELEASE);
+
 	/* Turn the lights off */
 	if (sc->model->bled_set)
 		led_destroy(sc->s_bled.cdev);
@@ -871,6 +890,8 @@
 	/* Free sysctl tree */
 	sysctl_ctx_free(&sc->sysctl_ctx);
 
+	lockuninit(&sc->led_lock);
+
 	return (0);
 }
 
 static void
 acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
@@ -916,7 +937,13 @@
 	default:
 		kprintf("acpi_asus_led: invalid LED type %d\n",
 		    (int)led->type);
-		return;
+		goto out;
 	}
 
 	acpi_SetInteger(sc->handle, method, state);
+out:
 	led->busy = 0;
+	lockmgr(&sc->led_lock, LK_EXCLUSIVE);
+	sc->led_pending--;
+	if (sc->led_pending == 0)
+		wakeup(&sc->led_pending);
+	lockmgr(&sc->led_lock, LK_RELEASE);
 }
 	
 static void
 acpi_asus_led(struct acpi_asus_led *led, int state)
@@ -926,9 +953,15 @@
 
 	sc = led->sc;
+	lockmgr(&sc->led_lock, LK_EXCLUSIVE);
 	if (led->busy)
-		return;
+		goto out;
 
 	led->busy = 1;
 	led->state = state;
+	sc->led_pending++;
 
-	AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
+	if (AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task,
+	    led) != AE_OK) {
+		led->busy = 0;
+		sc->led_pending--;
+	}
+out:
+	lockmgr(&sc->led_lock, LK_RELEASE);
 }