⬢ DragonFlyBSD Kernel Audit
DF-3601 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/sound/pci/hda/hdaa.h	2026-09-15 07:35:52.361501130 +0000
+++ b/sys/dev/sound/pci/hda/hdaa.h	2026-09-15 07:36:36.900936666 +0000
@@ -212,6 +212,7 @@
 	struct hdaa_pcm_devinfo	*devs;
 	struct hdaa_chan	*chans;
 	struct callout		poll_jack;
+	struct task		reconfig_task;
 	int			poll_ival;
 };
 
--- a/sys/dev/sound/pci/hda/hdaa.c	2026-09-15 07:35:52.361501130 +0000
+++ b/sys/dev/sound/pci/hda/hdaa.c	2026-09-15 07:36:36.880936920 +0000
@@ -6489,8 +6489,33 @@
 	HDA_BOOTHVERBOSE(
 		device_printf(dev, "Reconfiguration...\n");
 	);
-	if ((error = device_delete_children(dev)) != 0)
-		return (error);
+	/*
+	 * Defer the whole sequence out of the sysctl handler context:
+	 * userland_sysctl() holds the per-CPU global sysctllock SHARED
+	 * across handler execution, while device_delete_children() /
+	 * bus_generic_attach() (un)register the pcm children sysctl OIDs
+	 * via sysctl_unregister_oid()/sysctl_add_oid() -> _sysctl_xlock(),
+	 * which takes EVERY CPU's gd_sysctllock EXCLUSIVE.  Running inline
+	 * the same thread would wait for its own shared hold forever.
+	 */
+	taskqueue_enqueue(taskqueue_thread[mycpuid], &devinfo->reconfig_task);
+
+	return (0);
+}
+
+/*
+ * Deferred reconfiguration body (see comment in hdaa_sysctl_reconfig).
+ */
+static void
+hdaa_reconfig_task(void *context, int pending)
+{
+	device_t dev = context;
+	struct hdaa_devinfo *devinfo = device_get_softc(dev);
+
+	if (device_is_attached(dev) == 0)
+		return;
+	if (device_delete_children(dev) != 0)
+		return;
 	hdaa_lock(devinfo);
 	hdaa_unconfigure(dev);
 	hdaa_configure(dev);
@@ -6499,7 +6524,6 @@
 	HDA_BOOTHVERBOSE(
 		device_printf(dev, "Reconfiguration done\n");
 	);
-	return (0);
 }
 
 static int
@@ -6624,6 +6648,7 @@
 	devinfo->newgpo = -1;
 	callout_init_mp(&devinfo->poll_jack);
 	devinfo->poll_ival = hz;
+	TASK_INIT(&devinfo->reconfig_task, 0, hdaa_reconfig_task, dev);
 
 	hdaa_lock(devinfo);
 	res = hda_command(dev,
@@ -6713,6 +6738,9 @@
 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
 	int error;
 
+	/* Drain any pending deferred reconfiguration before teardown. */
+	taskqueue_drain(taskqueue_thread[0], &devinfo->reconfig_task);
+
 	if ((error = device_delete_children(dev)) != 0)
 		return (error);