# DF-5546 — xhci_pci polling mode leaks the MSI allocation on every detach

## What

`xhci_pci_attach()` calls `pci_alloc_1intr(self, xhci_use_msi, &rid,
&irq_flags)` **unconditionally** (`sys/bus/u4b/controller/xhci_pci.c:234`)
— when the controller exposes classic MSI this allocates MSI vectors,
sets `rid=1` and adds the rid→irq resource-list entries. In forced
polling mode (`hw.usb.xhci.use_polling=1` boot tunable) the IRQ resource
is then **never created** (:253-268 skips `bus_alloc_resource_any`), and
`xhci_pci_detach()` calls `pci_release_msi()` only under
`if (sc->sc_irq_res)` (:366-371). Result: on every polling-mode
attach/detach cycle (kldunload/kldload, hot-(un)plug) the MSI message
allocation, its resource-list entries and `sc_irq_rid` state leak —
there is no IRQ resource to gate the release on. Repeated cycles drain
the finite MSI vector pool, degrading future device bring-up.

Also note the dead comment at :266 — the "FALLTHROUGH - use polling"
after IRQ-allocation failure actually `goto error`s at :326 unless the
polling tunable was set; only the tunable path reaches the leak.

## Reproduce (baseline, unpatched kernel)

Guest needs a classic-MSI xHCI and the tunable:

```
/boot/loader.conf.local: hw.usb.xhci.use_polling=1
QEMU_EXTRA="-device nec-usb-xhci,msi=on,msix=off" dfbsd-qemu/vm.sh up
# boot log shows: xhci0: Interrupt polling at 100Hz
./build.sh
./run.sh     # kldload msileak.ko; sysctl hw.df5546=1
```

Expected decisive output (run.log):

1. `xhci0 pre-detach: sc_irq_rid=1 sc_irq_res=0 sc_intr_hdl=0` — MSI
   allocated by the glue, NO IRQ resource (polling mode)
2. `device_detach(xhci0) returned 0` — clean detach, children fine
3. `pci_release_msi(xhci0) probe returned 0 (LEAK: driver left MSI
   vectors allocated)` — the harness had to release what the driver
   leaked (keeps the guest usable)

(`-device qemu-xhci` does NOT work for this: that emulated device is
MSI-X only and DF's `pci_alloc_msi` is classic-MSI only, so nothing is
allocated — see run.hw-precondition.log.)

## Fix validation

`fix.diff` (unconditional `pci_release_msi(self)` after the IRQ block in
detach — a no-op returning ENODEV when nothing is allocated). Applied in
guest `/usr/src` together with the DF-5545 fix in one rebuild; rebooted
with the same tunable + NEC controller; identical run
(`run.fix.log`):

`pci_release_msi(xhci0) probe returned 19 (clean: nothing left to release)`
— the driver itself released the vectors during detach.

## Honest scope

Requires the root-set boot tunable (polling mode is a diagnostic
fallback) and MSI-capable hardware; the trigger context (module
unload / hot-unplug) is privileged. Impact is resource exhaustion /
device-init degradation, not memory disclosure. Low severity,
mechanics certain and reproduced end-to-end.
