--- 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);