aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
Commit message (Collapse)AuthorAgeFilesLines
* uvideo: fix step=0 infinite loop and int overflow in fbuf_sizeHEADmainBaptiste Daroussin2026-07-241-1/+3
| | | | | | | | | Prevent infinite loop in uvideo_vs_negotiation() when a USB camera reports step=0 in its continuous frame interval descriptor. Cast fbuf_size calculation to uint64_t to avoid int overflow for large width/height/bpp combinations. Reported by: emaste
* sound: Scale PCM secondary buffers by byte rateKevin Bowling2026-07-243-34/+96
| | | | | | | | | | | | | | | | | | | The fixed 128 KiB secondary buffer cap dates from stereo-sized streams. High channel-count or high sample-width OSS streams can consume most of that budget in one graph quantum, leaving too little room for capture catch-up or playback headroom. Keep 128 KiB as the low-rate floor, but derive the effective soft-ring cap from the channel byte rate, clamped to 4 MiB. Use that per-channel cap when resizing the soft buffer and when clamping SNDCTL_DSP_SETFRAGMENT requests. Also clamp SNDCTL_DSP_LOW_WATER to the current soft-buffer size so an impossible readiness threshold cannot make poll/select wait forever. MFC after: 3 weeks Reviewed by: christos Differential Revision: https://reviews.freebsd.org/D58064
* snd_uaudio: Don't let an idle stream reprogram a shared UAC2 clockgiacomo2026-07-241-8/+95
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Some UAC2 devices expose a single Clock Source entity that is shared between their playback and capture interfaces (it appears in both the output and input clock bitmaps). On such a device uaudio(4) programs the sample rate for both directions when a stream starts. If playback runs at a 44.1 kHz-family rate while the idle capture channel is left at its 48 kHz-family default, the capture SET_CUR(UA20_CS_SAM_FREQ_CONTROL) is issued after the playback one and overwrites the rate on the shared clock. The device then runs at ~48 kHz while the playback stream carries 44.1 kHz data. Consuming samples faster than they arrive, the device repeatedly runs out of data, loses sync with the playback stream, and re-locks onto it (audible dropouts, front-panel play/idle flicker). The 48 kHz family is unaffected because both directions then agree on the rate. Fix it in three parts: - Add a shared-clock guard: before issuing SET_CUR to a clock id, if that clock is shared between playback and capture and the other direction is already streaming at a different rate, skip it. The first active stream owns the clock; a later one follows it. - When the recording channel is auto-started only as a source of jitter information for asynchronous playback, align its nominal rate to the playback rate before starting it, so it neither reprograms the shared clock to a conflicting rate nor produces mismatched frame sizes. - Always submit the explicit-feedback SYNC transfer so dev.pcm.%d.feedback_rate stays live as a diagnostic even when a capture stream is present. Reproduced on an OKTO RESEARCH DAC8 STEREO (0x152a:0x88c5), whose vestigial capture interface never streams; the same device plays the 44.1 kHz family correctly under Linux's snd-usb-audio. As a side effect, this patch also fixes the sample rate bug mentioned in the BUGS section of sound(4)'s man page, where a device needs to have the same sample rate set for both playback and recording in order to work properly. PR: 295933 Assisted-By: Claude Opus 4.8 (claude-opus-4-8) Signed-off-by: giacomo <delleceste@gmail.com> MFC after: 2 weeks Reviewed by: christos Pull-Request: https://github.com/freebsd/freebsd-src/pull/2323
* uvideo: fix printf typeBaptiste Daroussin2026-07-221-1/+1
| | | | Reported by: vishwin
* uvideo: validate frame descriptors and fix integer overflows in size computationBaptiste Daroussin2026-07-221-44/+70
|
* uvideo: use size_t for sc_mmap_count and loop index in reqbufsBaptiste Daroussin2026-07-221-6/+5
|
* uvideo: fix close/detach race on streaming teardownBaptiste Daroussin2026-07-221-11/+11
| | | | | | | | | | | | detach() stopped streaming and called uvideo_vs_close() before destroy_dev(), so a concurrent close() could race the teardown and call uvideo_vs_close() a second time (double usbd_transfer_unsetup), and mtx_destroy() could race a close still holding sc_mtx. sc_streaming was also read without the lock in both paths. Reorder detach() to call destroy_dev() first so all in-flight cdev methods drain before any teardown. Read sc_streaming under sc_mtx in both detach() and the last-close safety net.
* uvideo: track streaming ownership per-fd and free buffers on STREAMOFFBaptiste Daroussin2026-07-221-3/+76
| | | | | | | | | | | | | The driver shared a single streaming state and buffer pool across all open file descriptors, so a second client (e.g. another browser tab) could disrupt the first: its cleanup STREAMOFF would tear down the active stream, and stale buffers prevented re-acquisition. Add per-fd state via devfs cdevpriv tracking whether this fd started streaming. STREAMOFF and close from a non-streaming fd are no-ops. STREAMOFF from the streaming fd stops the stream and frees the buffers so that a new fd can re-acquire the camera. DQBUF returns EPIPE immediately when buffers are freed instead of waiting for a timeout.
* uvideo: bounds-check frame interval reads against bLengthBaptiste Daroussin2026-07-221-7/+9
| | | | | | | | | | | | | | | Frame interval data is read from device-supplied frame descriptors whose bLength may be shorter than the number of intervals declared by bFrameIntervalType. The continuous branch of uvideo_enum_fivals() read three intervals unconditionally, and the discrete branch checked the pointer but not the four bytes that UGETDW() reads, so a short or malformed descriptor could read past bLength and leak adjacent kernel memory to userspace. uvideo_vs_parse_desc_frame_max_rate() had the same class of off-by-up-to-three-bytes read. Compute the available bytes from bLength and validate before each read. Reported by: emaste
* uvideo: lock the mmap queue and read pathBaptiste Daroussin2026-07-221-13/+59
| | | | | | | | | | | | | | | | qbuf(), dqbuf() and read() manipulated sc_mmap_q / sc_mmap_cur / sc_frames_ready without sc_mtx, racing with the USB transfer callbacks (producer) that run under the mutex. This could corrupt the queue or trigger use-after-free. Take sc_mtx around qbuf(), use mtx_sleep() and protect the queue operations in dqbuf(), and use mtx_sleep() with a snapshot of sc_fsize in read(). Also reject S_FMT and S_PARM with EBUSY while streaming: both re-negotiate the probe/commit controls with the device, which disrupts the active USB transfers (a second client opening the device would otherwise freeze the first one's stream).
* uvideo: validate frame size before mmap buffer allocationBaptiste Daroussin2026-07-221-4/+12
| | | | | | | | | | | | | dwMaxVideoFrameSize comes from the USB probe/commit response and is not validated. reqbufs() computed buf_size_total with signed int arithmetic and no bound, so a bogus value could wrap the product to a small size and yield a too-small buffer with a huge sc_mmap_buffer_size, causing out-of-bounds writes from the USB transfer callbacks. Bound the frame size against sc_max_fbuf_size and use overflow-checked size_t arithmetic for the total and per-buffer offsets. Reported by: emaste
* uvideo: replace contigmalloc with OBJT_PHYS vm_object for mmap bufferBaptiste Daroussin2026-07-221-141/+51
| | | | | | | | | | | | Allocate the mmap buffer via phys_pager_allocate() and map it into kernel space with vm_map_find()/vm_map_wire(), instead of a custom cdev_pager backed by contigmalloc. phys_pager_allocate() is required over a bare vm_object_allocate(OBJT_PHYS) to initialise un_pager.phys.ops, otherwise phys_pager_getpages() NULL-derefs during vm_map_wire(). Reviewed by: markj Reported by: markj Differential Revision: https://reviews.freebsd.org/D58394
* vtnet: Accept VIRTIO_NET_F_CTRL_RX_EXTRAFaraz Vahedi2026-07-211-0/+1
| | | | | | | | | | | Although the driver does not issue the extra receive-mode commands accepting the feature is harmless and some devices, notably Apple's Virtualization.framework, offer their control-queue features as a group and refuse FEATURES_OK unless the whole set is acknowledged. Signed-off-by: Faraz Vahedi <kfv@kfv.io> Reviewed by: adrian Pull Request: https://github.com/freebsd/freebsd-src/pull/2322
* vtnet: Implement VIRTIO_NET_F_GUEST_ANNOUNCEFaraz Vahedi2026-07-212-0/+90
| | | | | | | | | | | | | | | | | | | | | | | When the device sets VIRTIO_NET_S_ANNOUNCE in the config status field, for example after a VM migrates to a new host, announce the interface's presence on the network so peers and switches learn the new attachment point, then acknowledge the request with the VIRTIO_NET_CTRL_ANNOUNCE_ACK control command, as per VirtIO v1.3, 5.1.6.5.4. The announcement raises iflladdr_event: the stack sends gratuitous ARPs and unsolicited neighbor advertisements for the interface's addresses, and stacked interfaces such as vlan(4) propagate the event and announce theirs as well. The event handlers may sleep, so the work is deferred from the config change interrupt to a task on taskqueue_thread; that context also allows the acknowledgement to be skipped safely if the interface was stopped in the meantime, in which case the device keeps the bit set and the request is re-delivered with the next config change interrupt. Signed-off-by: Faraz Vahedi <kfv@kfv.io> Reviewed by: adrian Pull Request: https://github.com/freebsd/freebsd-src/pull/2322
* vtnet: Retry feature negotiation without offloadsFaraz Vahedi2026-07-212-2/+30
| | | | | | | | | | | | | | | | | | | | | | | | A device is permitted to reject an otherwise valid subset of its offered features by refusing to accept FEATURES_OK (VirtIO v1.3, 2.2.2). Apple's Virtualization.framework does this in practice; it treats the offered CSUM/TSO offloads as all-or-nothing, while vtnet's default request contains only part of that group because of hw.vtnet.lro_disable that would drop the guest TSO bits, thus negotiation fails and the device does not attach. If FEATURES_OK is rejected, retry the negotiation once with every offload-related feature stripped. Changing the feature set after a failed FEATURES_OK requires re-initialising from device reset (VirtIO v1.3, 3.1.1), so the retry goes through virtio_reinit(). A NIC without offloads is preferable to no NIC at all. Devices that accept the initial feature set are unaffected, while those that also reject the reduced set continue to fail attachment as before. Signed-off-by: Faraz Vahedi <kfv@kfv.io> Reviewed by: adrian Pull Request: https://github.com/freebsd/freebsd-src/pull/2322
* hwpmc: Add EXTERROR diagnostics to the hwpmc syscall pathAndre Silva2026-07-211-31/+79
| | | | | | | | | | | | | Annotate validation failures in the PMC syscall handlers (allocate, attach, read/write) with EXTERROR(), so pmc(3) callers see which precondition failed, not a bare errno. Register HWPMC_MOD in exterr_cat.h and the generated filenames.h. Signed-off-by: Andre Silva <andasilv@amd.com> Reviewed by: Ali Mashtizadeh <ali@mashtizadeh.com>, mhorne Sponsored by: AMD Pull Request: https://github.com/freebsd/freebsd-src/pull/2180
* hwpmc: Add EXTERROR diagnostics to the AMD and IBS allocatorsAndre Silva2026-07-212-24/+45
| | | | | | | | | | | | Replace bare EINVAL in AMD/IBS allocation and config-validation with EXTERROR(), so a failed pmc(3) allocation names the check and value. Register HWPMC_AMD in exterr_cat.h and the generated filenames.h. Signed-off-by: Andre Silva <andasilv@amd.com> Reviewed by: Ali Mashtizadeh <ali@mashtizadeh.com>, mhorne Sponsored by: AMD Pull Request: https://github.com/freebsd/freebsd-src/pull/2180
* hid: u2f: stop interrupts on last-closeKyle Evans2026-07-211-3/+0
| | | | | | | | | | | | | | | | | | | | This fixes an issue with the Solo2 (and likely some of the Nitrokey family) where hangs would occur with OpenSSH- it issues a CANCEL prior to closing the device unconditionally, and without draining the read endpoint we end up seeing the response to that CANCEL the next time OpenSSH tries to connect. This throws the entire command/response sequence out of whack. This call used to break Yubikeys in some situations, but the fix that landed in 28d85db46b48 ("xhci: Do not drop and add bits in xhci") seems to have addressed that- presumably we sometimes end up stopping the command and desyncing at the controller level. This probably implies that we need a SYNCWRITE HID quirk, but that requires a little more work in usbhid_sync_xfer() and this doesn't seem to cause any problems in normal usage. Reviewed by: aokblast, wulf Differential Revision: https://reviews.freebsd.org/D58199
* uvideo: fix use-after-free in mmap buffer lifetime managementBaptiste Daroussin2026-07-211-14/+162
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The uvideo driver freed the mmap buffer (contigmalloc'd) in several paths (VIDIOC_STREAMOFF, last close, detach) without coordinating with the lifetime of existing user-space mmap mappings. This could lead to use-after-free when user-space continued to access the mapped memory after the backing pages had been freed. Fix this by switching from the simple d_mmap callback to d_mmap_single with custom cdev_pager_ops, and by attaching the contig buffer to a single shared vm_object created at REQBUFS time: - uvideo_reqbufs() allocates a uvideo_mmap_state (independent of the softc) and a shared vm_object via cdev_pager_allocate() that spans the whole buffer; the softc holds one reference to it. - uvideo_cdev_mmap_single() simply hands out additional references to that shared object; the requested offset selects which buffer is mapped. The VM system tracks mapping lifetime through the object reference count, so no per-mapping bookkeeping is needed. - uvideo_pg_ctor/uvideo_pg_dtor validate the mapping and free the contig buffer together with the state when the last reference (softc's own or a user mapping) is dropped. - uvideo_pg_fault installs a fictitious page for the backing physical address, following the canonical device-pager pattern: update the passed-in page in place when it is already fictitious, otherwise allocate a fake page and vm_page_replace() the busy placeholder, so that dev_pager_dealloc() does not deadlock. - uvideo_vs_free_frame() drops the softc's reference instead of contigfree()'ing directly; if mappings still exist the buffer stays alive until the last uvideo_pg_dtor(). - VIDIOC_STREAMOFF no longer frees the buffer (per V4L2 spec). - Last close always releases the buffer (deferred if mappings exist). - The mmap_state outlives the softc, so the pager dtor can safely free the buffer even after device detach. Reported by: 章鱼哥 (@aipyapp) (www.aipyaipy.com) Reported by: Chris Jarrett-Davies of the OpenAI Codex Security Team
* nvme: Explicitly cast caddr_t valuesDag-Erling Smørgrav2026-07-211-1/+1
| | | | | | | | | Sponsored by: Klara, Inc. Sponsored by: NetApp, Inc. MFC after: 1 week Fixes: 6d0001d44490 ("nvme: add support for DIOCGIDENT") Reviewed by: bnovkov, imp Differential Revision: https://reviews.freebsd.org/D58357
* firewire: replace magic numbers with named constantsAbdelkader Boudih2026-07-2012-58/+112
| | | | | | | No functional change. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58311
* firewire: remove dead code across the subsystemAbdelkader Boudih2026-07-209-428/+3
| | | | | Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58310
* firewire: force root change when root node is not cycle master capableAbdelkader Boudih2026-07-201-3/+4
| | | | | | | | | When a FireWire bus resets, all devices negotiate who is the new boss. when we detect the root node can't be cycle master, we send a PHY config packet that forces a reelection. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58309
* firewire: drain pending xfers after callout stop in detachAbdelkader Boudih2026-07-201-1/+3
| | | | | | | Removes a TODO that predates the existing drain call. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58308
* firewire: add warn-only CRC validation for CSR ROM directoriesAbdelkader Boudih2026-07-205-1/+32
| | | | | | | | | | | | Implemented crom_crc_valid() helper to validate IEEE 1394 config ROM CRC-16 checksums. Skipped root header CRC validation since csrhdr.crc_len cover the entire ROM body which is not fully read at header parse time. Per-directory CRC checks below catch corruption where it needed. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58307
* net80211: update drivers to allocate sequence numbers in the raw pathAdrian Chadd2026-07-205-2/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | A bunch of drivers weren't properly converted. I mistakenly put a call to ieee80211_output_seqno_assign() wherever the crypto header was added, which isn't exactly correct. There are plenty of drivers which don't share enough of their raw and normal transmit path code for that to hold true. So after some manual review, it looks like I've captured the places (outside of iwn(4) which I committed earlier) where I missed ieee80211_output_seqno_assign() calls. * For bwi(4) and bwn(4) I refactored it out into a place that is common enough and happens in the same lock hold window, so it's serialised. * For the rest, it's just plain missing from the raw path. Locally tested: * ural(4) * ral(4) * bwi(4) Differential Revision: https://reviews.freebsd.org/D58098
* amd64: FRED supportKonstantin Belousov2026-07-201-1/+2
| | | | | | | | | | | | | | | | | FRED support as defined starting from the SDM rev. 90, requires a new 'events' entry point to receive user and kernel mode exceptions and interrupts notifications from the hardware. A minimal asm trampoline is enough, rest can be implemented in C due to the clean FRED organization of the event reporting. The syscall entry is handled by a microptimized assembly path, directly calling into the amd64_syscall() handler, instead of the generic events entry point. Tested by: emaste Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D55829
* uvideo: import quirks infrastructure from OpenBSDBaptiste Daroussin2026-07-201-2/+177
| | | | | | | | | | | | | | | | Import the device quirk system from OpenBSD to handle UVC devices that need special handling. This includes: - UVIDEO_FLAG_ISIGHT_STREAM_HEADER: non-standard streaming header - UVIDEO_FLAG_REATTACH: needs reattach after firmware upload - UVIDEO_FLAG_VENDOR_CLASS: incorrectly reports as vendor class - UVIDEO_FLAG_NOATTACH: device not supported - UVIDEO_FLAG_FORMAT_INDEX_IN_BMHINT: format index in bmHint Add quirks table with known devices and lookup function. Add iSight stream header decoder for Apple iSight cameras. Obtained from: OpenBSD
* uvideo: increase UVIDEO_MAX_PU and UVIDEO_MAX_CT to 32Baptiste Daroussin2026-07-201-2/+2
| | | | | | Some UVC devices (e.g. Logitech C920) expose more than 8 Processing Unit descriptors, causing "too many PU descriptors found!" errors. Increase both limits from 8 to 32 to accommodate such devices.
* hwpmc: drain a process-mode PMC's runcount when a live target detachesAlexander Leidinger2026-07-201-0/+88
| | | | | | | | | | | | | | | | | | | | | | | | | | A process-mode PMC's runcount tracks how many CPUs currently have it loaded in hardware. It is decremented only by the context-switch-out and process-exit reclaim paths, both of which the scheduler invokes only for processes flagged P_HWPMC. Detaching a target that still has the PMC live in hardware dropped the target and cleared P_HWPMC without taking the PMC off the hardware or dropping the runcount reference, so the reference leaked. A subsequent release then spun in pmc_wait_for_pmc_idle() forever waiting for the runcount to reach zero: on an INVARIANTS kernel this panics ("waiting too long for pmc to be free"), otherwise it is an unkillable loop holding the hwpmc lock. Any process able to allocate a PMC can trigger this by attaching a counting PMC to itself and detaching it before releasing. Take the PMC off the hardware and drop the runcount reference as part of detaching, before P_HWPMC is cleared: reclaim it from the detaching thread's own CPU directly, and, when the detach removes the PMC's last target, wait for any references held by the target's other threads to drain while P_HWPMC is still set (they can no longer reload it). Reviewed by: adrian MFC after: 2 weeks Assisted-by: Claude Code (Fable 5) Differential Revision: https://reviews.freebsd.org/D58342
* hwpmc: handle counter wraparound for process-mode counting PMCsAlexander Leidinger2026-07-201-16/+30
| | | | | | | | | | | | | | | | | | | | | | | | The accumulated count of a process-mode counting PMC is kept in a 64-bit software counter and seeded into the hardware counter at every context switch in. Hardware counters are narrower than that - each PMC class discovers and records its own counter width, e.g. 48 bits on current x86 (queried from CPUID on Intel, architectural on AMD) - so once the accumulated count approaches the end of the hardware counter range, the counter wraps during a time slice and the value read back at switch out is smaller than the value seeded. The increment was computed assuming a full 64-bit counter: on INVARIANTS kernels a long enough counting run panics with "negative increment" the moment the accumulated count first crosses the hardware counter range, and on other kernels the totals silently lose a full counter range per wrap. Compute the increment modulo the per-class hardware counter width instead, in both places that accumulate switch-out deltas. Reviewed by: adrian MFC after: 2 weeks Assisted-by: Claude Code (Fable 5) Differential Revision: https://reviews.freebsd.org/D58340
* apei: Fix i386 build over bus read, write functionShengYi Hung2026-07-201-6/+7
| | | | | | | | | | | bus_{read,write}_8 are macro wrappers around the corresponding bus_space functions in sys/bus.h, so implementing bus_{read,write}_8 won't work. Implement the underlying bus_space function instead. Reviewed by: jrtc27, rlibby Fixes: 9313f6b01485 Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D58301
* re(4): add hw.re.aspm_disable loader tunableJérémie Jourdin2026-07-191-4/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | re(4) has unconditionally disabled ASPM L0s/L1 and CLKREQ at attach for years; on laptops this costs 200mW+ (requested by adrian@ in the PR). Make it a tunable following the existing hw.re.* pattern: * default 1 keeps today's behavior; * 0 preserves the firmware-configured ASPM state at attach and skips the watchdog re-assert from the previous revision. Documented in re.4. * Verified on RTL8168H (XID 0x541): with hw.re.aspm_disable=0, attach no longer logs "ASPM disabled" and pciconf -lcb shows the firmware Link Control state preserved -- including Clock PM, which the unconditional code previously cleared. * Default (1) is behaviorally identical to the current driver. * Note the tunable also stops the driver clearing CLKREQ, a small power win even where firmware leaves L0s/L1 off. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58280 PR: kern/166724
* re(4): harden re_watchdog() recovery and log controller stateJérémie Jourdin2026-07-191-3/+58
| | | | | | | | | | | | | | | | | | | | | | | Distinguish the two failure classes from the PR in a single log line (ring indices, ISR/IMR, TXCFG, interrupt mode): lost interrupt vs genuine DMA stall. Bail out instead of re-initializing when the controller reads back all-ones (fallen off the bus; reinit cannot help). Re-assert the driver's existing ASPM-disabled policy before reinit, since firmware/power transitions re-arming L0s/L1 is a documented stall trigger. Diagnostics-only on the recovered path; no fast-path change. * Field diagnostics running on an RTL8168H production fleet; the log format distinguishes lost-doorbell / DMA-stall / dead-controller without a debug build. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58279 PR: kern/166724
* re(4): recover Tx completions whose MSI was swallowed in re_intr_msi()Jérémie Jourdin2026-07-191-0/+27
| | | | | | | | | | | | | | | | | | | | | A Tx completion that raises a status bit between the ISR ack at the top of re_intr_msi() and the IMR re-enable at the bottom is never re-signalled: these controllers do not re-assert MSI for an already-set status bit (this is why hw.re.msi_disable is a known workaround in the PR). Re-read ISR before re-enabling; if a Tx bit is pending, ack just that bit, reap the ring and restart the queue. Rx bits are deliberately left set so they re-arm the interrupt normally and Rx moderation state is untouched. Also flush the posted IMR write. Mirrors what the INTx path already achieves via the loop in re_intr(). * MSI interrupt mode on RTL8168H under load; "missed Tx interrupts" watchdog recoveries no longer occur. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58278 PR: kern/166724
* re(4): re-arm the Tx doorbell when re_txeof() leaves a non-empty ringJérémie Jourdin2026-07-191-0/+11
| | | | | | | | | | | | | | | | | | | On PCIe parts a TxPoll request can be lost when packets are queued in quick succession, leaving owned descriptors with no transfer in progress until the watchdog fires. re_txeof() runs from the interrupt handlers, re_tick() and re_watchdog(), so re-writing TXSTART whenever the ring is still non-empty turns a potential 5-second stall into at most one tick. One register write on a path that already took an interrupt; fast path untouched. * Sustained bidirectional load on RTL8168H; no Tx stalls, no throughput regression at 941 Mbps line rate. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58277 PR: kern/166724
* re(4): quiesce RTL8168G+ and reset before freeing buffers in re_stop()Jérémie Jourdin2026-07-192-1/+48
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The STOPREQ command written by re_stop() is not defined for RTL8168G and later; issuing it can wedge the MAC. Replace it on those parts with the vendor-documented sequence: * settle delay * bounded poll for Tx queue empty * clear TE/RE * then bounded poll of the MCU command register (0xD3) FIFO-empty bits. Also reset the controller before the Rx/Tx buffer free: a controller that has not quiesced keeps DMAing stale, still-owned descriptors pointing at freed mbufs (use-after-free under INVARIANTS, cross-NIC mbuf corruption reported in the PR). Adds the RL_MCU_* register definitions. All waits are bounded; error paths only. * iperf3 --bidir at line rate against RTL8168H (XID 0x541); previously wedged the controller until power cycle, with the quiesce the reset path recovers. * Deployed in production on an RTL8168H fleet since 2026-07-01. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58276 PR: kern/166724
* aq(4): correct Atlantic 2 register accessNick Price2026-07-194-18/+31
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Four Atlantic 2 register-access corrections found in bring-up. B0 aggregate octet counters: the B0 firmware statistics interface reports only aggregate rx/tx good octets, not the per-cast breakdown A0 and Atlantic 1 provide, so every octet sysctl read a permanent zero while frame counters advanced. Populate the aggregate octet fields from the B0 buffer; aq_update_hw_stats() accumulates them directly when the per-cast octets are absent. Drop the duplicate attach-time MCP reboot: aq_hw_mpi_create() already reboots the A2 firmware to read its version and caps, then aq_hw_reset() immediately rebooted it again -- a full MCP restart plus several transaction-id-bracketed window reads, adding attach latency and a duplicate banner. Give aq_hw_reset() a reboot flag and pass reboot=false for A2 at attach; the load-bearing down/stop reboot (which resyncs A2 RX DMA across ifconfig down/up) keeps reboot=true. Skip Atlantic 1 register accesses on Atlantic 2: gate out the 0x7040 Atlantic 1 TPO write (which A2 lacks; already a no-op via the unset TPO2 feature, but Linux hw_atl2 omits it), and guard the aq_hw_mpi_read_stats() direct reads of reg_rx_dma_stat_counter7 (dpc) and the LRO counter (cprc) with !ATLANTIC2 -- those are Atlantic 1 codegen offsets that on Atlantic 2 land on unrelated registers and can report bogus input-drop / LRO counts. HW-validated on AQC107 <-> AQC113C: A1 stats unchanged, A2 IQDROPS stays 0, attach consumes one MCP reboot instead of two, bidirectional iperf3 clean. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58143
* aq(4): program the Atlantic 2 multiqueue datapathNick Price2026-07-194-28/+210
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Wire up the Atlantic 2 receive datapath: the action-resolver table (ART), multiqueue RSS, QoS, and interrupt moderation. RX action-resolver table: Atlantic 2 replaces Atlantic 1's discrete RX filter registers with an ART -- hardware computes a per-packet classification tag, then walks {tag, mask, action} rows to drop, assign a queue, or assign a TC. aq_hw_art_filter_set() installs one row under the ART semaphore; aq_hw_init_rx_path() enables the resolver, tags L2 unicast/broadcast, installs the unicast/all-multicast and VLAN drop rows, and assigns every 802.1p priority to TC 0 (mirroring the Atlantic 1 user-priority map, since our RX side is a single 8-ring group in TC 0). Tag every enabled VLAN filter in the per-filter resolver-tag field -- a register the BSD ports never write -- because the VLAN drop row matches resolver tag 0, so without it all tagged receive was dead under VLAN filtering. Promiscuous mode disables the drop rows rather than toggling the Atlantic 1 promiscuous bits; all ART callers surface a semaphore timeout consistently. The Atlantic 1 RX_TCP_RSS_HASH and TPO2 programming is gated to Atlantic 1. Multiqueue RSS and QoS: fill Atlantic 2's own per-TC redirection table (AQ2_RPF_RSS_REDIR), skipping the Atlantic 1 table and its write-enable handshake. Program Atlantic 2's smaller packet-buffer sizes, its wider data-TC credit/weight fields, and its ring-to-TC map, using aq_hw_active_tcs() for the TC loops. RSS hash types: the Atlantic 2 resolver has per-protocol hash-type enables in REDIR2, so build the mask from aq_rss_hashconfig() instead of hardcoding every protocol -- UDP 4-tuple hashing now follows the kernel policy (off by default) with no L3L4 flow-filter workaround, and aq_hw_udp_rss_enable() is skipped on Atlantic 2. The kernel-to-hardware hash-type mapping is a small static lookup table rather than a nine-branch chain, since the two bit spaces do not share a simple shift. Tx interrupt moderation: Atlantic 2's per-ring Tx moderation control register lives at a different address, but its field layout matches the value the driver already builds, so write that value straight to it; Rx moderation is shared. HW-validated on AQC107 <-> AQC113C: TCP RSS spreads across 7/8 RX queues under 16 parallel flows, rx_err=0. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58142
* aq(4): add Atlantic 2 (AQC113) device supportNick Price2026-07-198-84/+879
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Add support for the Marvell Atlantic 2 (AQC113/114/115/116) controllers, a new chip generation that is not register-compatible with the Atlantic 1 parts aq(4) supports today. Adapted from the OpenBSD/NetBSD if_aq driver. Register and device definitions (aq2_hw.h): the firmware handshake (MIF_BOOT / MCP_HOST_REQ_INT / MIF_HOST_FINISHED), the 0x12000/0x13000 firmware interface windows, and the action-resolver table (ART) that replaces Atlantic 1's discrete RX filters, plus the Atlantic 2 PCI device ids and the aq_is_atlantic2() helper. Reserve a chip-feature bit (AQ_HW_CHIP_ATLANTIC2) and add the aq_hw fields the firmware fills at boot (ART base index, statistics interface version A0/B0). The per-VLAN-filter resolver-tag field comes from the Linux driver; the BSD sources never write it. Firmware operations (aq_fwa2.c): Atlantic 2 talks to the management CPU through the 0x12000/0x13000 register windows plus the boot handshake, rather than Atlantic 1's mailbox in shared RAM. Implement that as a third aq_firmware_ops vtable (reset, set_mode, get_mode, get_mac_addr, get_stats); aq_fwa2_reboot() boots the firmware, selects the A2 ops, and reads the version and ART base index, failing fast on the crash-init / boot-failed bits. fwa2_set_mode advertises full duplex only (the media model exposes no half-duplex types) and writes and acks the link options before raising ACTIVE mode, so a forced media change does not begin negotiation with a stale rate mask. enum aq_fw_link_speed gains aq_fw_10M, which Atlantic 2 supports and Atlantic 1 does not. Probe and attach: list the device ids with their media types and link speeds (all copper; AQC113* up to 10G, AQC116C to 1G), populate hw->device_id, and tag the generation with AQ_HW_CHIP_ATLANTIC2 so IS_CHIP_FEATURE() recognises it uniformly. Branch firmware bring-up and reset on the generation: aq_hw_init_ucp() and aq_hw_reset() reboot the MCP instead of the Atlantic 1 RBL/FLB reset -- without a real datapath reset every stop/init cycle reprograms the rings on a live, desynced RX DMA engine and the receive path stays dead. aq_hw_init() programs the Atlantic 2 launch-time clock ratio in place of the Atlantic 1 MRRS / TX-DMA request-limit clamp. Add an AQ_LINK_10M capability bit (Atlantic 2 links at 10M, Atlantic 1 cannot), offer 10baseT media, and map IFM_10_T to aq_fw_10M. With every supported media type now present, replace the per-speed switch statements in aq_media.c with a single {link bit, fw rate, IFM_* subtype, Mbit/s} table -- one source of truth for the supported link speeds. With this an Atlantic 2 card probes, brings up its firmware, reads its MAC, and negotiates link; the RX action-resolver datapath comes next. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58141
* aq(4): remove dead code and tidy macros, diagnostics, and namingNick Price2026-07-1912-211/+136
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Non-functional cleanup, with two diagnostic corrections. Dead code: delete leftover commented-out AQ_DBG_ENTER/EXIT/PRINT calls (aq_hw.c, aq_fw2x.c, aq_irq.c, aq_main.c), a commented-out aq_nic_cfg local, the stale old-signature parameter blocks between the ring-init declarations and their bodies (aq_ring.c), a trailing note on a live statement, and the unused DumpHex() vendor debug helper (no callers; its body only compiled under AQ_CFG_DEBUG_LVL > 3). Register-write macros: parenthesize AQ_WRITE_REG_BIT's msk/shift/value arguments so a compound argument cannot mis-bind, give AQ_HW_FLUSH() an explicit hw parameter instead of capturing it from caller scope, and drop the duplicate lowercase aq_hw_write_reg[_bit] aliases (converting the 43 call sites to the uppercase spelling) so there is a single form. Diagnostics: the aq_log* family expanded through the base log macro, which ignored its level and printed unconditionally, while the error traces gated on a debug level that defaulted below LOG_ERR and so were suppressed -- backwards. Gate the base log macro the way the trace one does and default the level to lvl_error, so the once-per-event firmware reset / capability errors are visible by default while the verbose info/dump output stays opt-in. Naming: rename identifiers carried verbatim from the vendor import that do not match style -- names mixing an ALL-CAPS macro-style prefix with a lowercase tail, and a trailing underscore the vendor used as a "file-local" marker in place of static. - dbg_level_ / dbg_categories_ -> aq_dbg_level / aq_dbg_categories: these are real globals (the log/trace macros reference them from every translation unit), so the trailing underscore was never a stand-in for static; give them the aq_ namespace so the driver stops exporting generically-named global symbols. - log_base_ / trace_base_ -> aq_log_base / aq_trace_base: the internal macros behind the aq_log*/trace* families. - bootExitCode / flbStatus -> boot_exit_code / flb_status (aq_fw.c); flb_status now matches the identically-purposed variable already spelled that way in the sibling FLB-reset path. Cosmetic: terminate the ring/HW-init, MSI-X admin-handler, and media-change error messages with a newline so they are not garbled into adjacent dmesg output, and label the per-queue rx_bytes sysctl "RX Octets" (it was copy-pasted "TX Octets"). Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58140
* aq(4): harden the interrupt and MAC-statistics pathsNick Price2026-07-194-44/+90
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Firmware-statistics accounting and interrupt-routing fixes. Stats delta underflow: guard the MAC statistics delta accumulation against counter wrap or a firmware counter reset, so a snapshot smaller than the previous one does not underflow into a huge spurious delta. Skip stats on a failed read: aq_update_hw_stats() ignored aq_hw_mpi_read_stats()'s return and committed the on-stack mbox into last_stats unconditionally. On a failed read that snapshot is garbage or zero and poisons the delta baseline (a zeroed snapshot wipes last_stats, so the next good read double-counts). Check the return and skip the accumulation and the last_stats commit on failure. Mailbox/stats separation: struct aq_hw_stats served both as the raw fw1x MCP mailbox layout and as the driver's canonical stats snapshot, so any field added to it would silently shift the fw1x mailbox read. Give the fw1x mailbox its own raw layout in struct aq_hw_fw_mbox and let aq_hw_stats become purely driver-owned; with the coupling gone, add first-class aggregate octet fields (brc/btc) that Atlantic 2 B0 firmware can populate directly. No A1 behavior change. The raw block is a named struct (aq_fw1x_mbox_stats) with a _Static_assert tying its size to aq_hw_stats' matching prefix, so the fw1x memcpy cannot silently misalign if either field list drifts. Also drop the unused FW1X_MPI_STATE_ADR / FW1X_MPI_CONTROL_ADR macros and the redundant fw1x_get_stats() dpc assignment that the caller immediately overwrites. Per-speed interrupt moderation: aq_hw_interrupt_moderation_set() hardcoded speed_index = 0, so every link speed got the 10G timer pair and the other rows were dead. Record the negotiated rate and index the tables by ffs(speed) - 1, reordering the rows to match the enum aq_fw_link_speed bit positions so the index cannot drift from the enum. Rename the two per-speed timer tables (AQ_HW_NIC_timers_table_ {rx,tx}_ -> aq_itr_timers_{rx,tx}), function-local static arrays whose SCREAMING_CASE vendor names read like macros. Hardware error interrupts: route both hardware error causes (interrupt map register 0) to the admin vector so they are actually delivered. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58139
* aq(4): harden the attach, detach, and reset error pathsNick Price2026-07-194-13/+30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Correct several attach/detach/reset paths that either swallowed failures or acted on undefined state. MSI-X attach-failure double-free: aq_if_msix_intr_assign() freed the per-RX-ring interrupts in its failure path and then returned an error, so iflib's IFDI_DETACH freed the same irq structures again -- bus_teardown_intr() on a dangling tag and bus_release_resource() on an already-released IRQ, panicking a box that should have simply failed to attach. Let iflib own the teardown; drop the failure-path loop and the now-dead index bookkeeping. Detach loop bound: aq_if_detach() freed the per-ring interrupts looping to isc_nrxqsets while indexing rx_rings[], which is sized by rx_rings_count; index by rx_rings_count to match every other RX-ring loop. AQ_HW_WAIT_FOR final poll: the macro derived its result from the loop counter rather than the condition, so a condition that became true on the last iteration reported ETIMEDOUT. Worst for the acquire-on-read firmware RAM semaphore, which was acquired in hardware but reported as a timeout. Return based on the last evaluation of the condition. RBL MAC reset SPI cleanup: mac_soft_reset_rbl() fired the global reset without first tearing down the SPI/flash interface, so a flash burst in flight left the SPI bus wedged, the RBL could not re-read flash, and the reset returned EBUSY -- fatal at attach ("MAC reset failed: 16"). Set bit 4 of the SPI control register (0x53c) before the global reset, as the sibling FLB path and the Linux driver do. Reset failure propagation: aq_hw_reset() discarded fw_ops->reset()'s return, so a failed attach-time fw2x capability read left fw_caps == 0 permanently and stats silently froze. Propagate the error so the reset fails and is retried. aq_hw_init failure propagation: aq_hw_init() discarded aq_hw_init_tx_path()/aq_hw_init_rx_path() returns and reported success, bringing the interface up half-initialized; capture both and goto err_exit (mainly the Atlantic 2 RX action-resolver path, which returns EBUSY on ART semaphore timeout). Link-state outputs: aq_hw_get_link_state() left *link_speed and *fc_neg unwritten on early-return paths, and the caller acts on them uninitialized, so a transient firmware get_mode() failure could fabricate a phantom link-up at a garbage speed and program a garbage RX-pause bit. Initialize both to safe link-down values before calling get_mode(). Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58138
* aq(4): honor the kernel RSS policy and add a TX traffic-class helperNick Price2026-07-193-15/+36
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Align RX steering with the kernel RSS framework and factor out the active-traffic-class count. RSS key and indirection table: on an options RSS kernel the stack owns a canonical hash key and a hash-to-bucket indirection table binding each bucket to a CPU. aq programmed a random arc4rand() key and a plain i % rss_qs table, so the hash it stamped in iri_flowid and the queue it steered a flow to did not match the CPU the stack chose -- defeating RSS affinity. Under #ifdef RSS take the key from rss_getkey() and each entry from rss_get_indirection_to_bucket(), as e1000/ixgbe/ixl do; the non-RSS build keeps the random key and round-robin table. RSS hash-type policy: drop the private hw.aq.enable_rss_udp knob (RDTUN, default on) and add aq_rss_hashconfig(), which under options RSS returns rss_gethashconfig() and otherwise the same UDP-off default. UDP 4-tuple hashing scatters a fragmented datagram's pieces across queues because only the first fragment carries the L4 ports, so it is now off by default and re-enabled the standard way, via net.inet.rss.udp_4tuple, matching ix/ixl/mlx5. On Atlantic 1 the UDP-off action stays the existing L3L4 flow-filter workaround; only its policy source changes. TX traffic-class helper: factor the active-TC count (one per active 8-ring group, capped at HW_ATL_B0_TCS_MAX) out of aq_hw_qos_set() into aq_hw_active_tcs(), so there is a single definition of the policy; the Atlantic 2 RSS redirection table reuses it. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58137
* aq(4): drop errored RX frames instead of resetting the interfaceNick Price2026-07-191-5/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | aq_isc_rxd_pkt_get() returned EBADMSG when a receive descriptor's MAC/receive-error bit (rx_stat bit 0) was set. iflib treats any error from isc_rxd_pkt_get() as a fatal ring fault and answers with IFC_DO_RESET -- a full interface reinitialization. A per-frame receive error is not a ring fault: on a marginal link or cable the Atlantic delivers errored frames continuously, so each one triggered another reset and the interface reset-stormed itself into carrying no traffic instead of merely dropping the bad frames. The Atlantic delivers errored frames to the host by design (Linux drops them in software via buff->is_error), and iflib offers no per-frame error return that isn't a reset. Follow the vmxnet3 model: on a receive error zero the fragment lengths and return success. iflib then discards the packet (assemble_segments() excludes zero-length fragments) while still recycling the descriptors through the refill path -- no reset. Also drop frames flagged with an RX-DMA fault (rdm_err), not just the MAC-error bit; and keep iri_len non-zero on that drop path, since iflib asserts iri_len != 0. The genuinely structural errors -- more segments than isc_rx_nsegments, or a pkt_len inconsistent with the descriptor count -- still return EBADMSG, since those indicate a confused ring where a reset is the right recovery. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58136
* aq(4): expand and correct offloads, fix VLAN/multicast filteringNick Price2026-07-196-19/+62
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Advertise the offloads the hardware already performs, correct the TX descriptor's L3 family selection, and correct the VLAN and multicast receive-filter paths. Offloads: advertise IFCAP_HWCSUM_IPV6 (adding CSUM_IP6_TCP/UDP/TSO to isc_tx_csum_flags) and IFCAP_VLAN_HWTSO, and enable the RX outer (S-VLAN) tag parse mode in aq_hw_offload_set(). TX descriptor L3 family: aq_setup_offloads() derived tx_desc_cmd_ipv4 from CSUM_IP|CSUM_TSO, but CSUM_TSO is (CSUM_IP_TSO|CSUM_IP6_TSO) and tcp_output() sets both bits without regard to address family, so an IPv6 TSO frame matched on CSUM_IP_TSO and went out with the IPv4 header-checksum command set on a frame that carries no IPv4 header. The checksum flags cannot distinguish the family; key the bit off IPI_TX_IPV4 instead, which iflib derives from the parsed ethertype, as the IPI_TX_INTR test below it already does. Plain IPv6 checksum offload was unaffected, as CSUM_IP6_TCP alone never matched the mask. RX VLAN tag stripping: ring init hardwired hardware tag stripping off while the RX path still set M_VLANTAG and the writeback tag for every tagged frame, so a tagged frame arrived with the tag in line while the mbuf claimed it stripped and ether_demux() parsed four bytes short of the payload. Program per-ring stripping from IFCAP_VLAN_HWTAGGING and set M_VLANTAG only under the same capability, so the two states stay coherent. VLAN filter and promiscuous edge cases: filter only when 1..16 VLANs are registered -- with none (or more than the 16 the table holds) fall back to VLAN-promiscuous and pass all tags, rather than dropping every tagged frame against an empty filter table; and keep VLAN-promiscuous set whenever the interface is IFF_PROMISC, so adding or removing a VLAN under promisc does not clear it and start dropping tagged frames. Multicast reconcile: ifdi_multi_set is declarative, but aq_if_multi_set() only added -- shrinking the list left accept-all-multicast latched or stale exact slots enabled, defeating hardware multicast filtering until a reinit. Clear the exact slots before reprogramming the current list, and always drive accept-all-multicast from the current state so a shrink clears it. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58145
* dpaa2: Apply if_flags and MAC filters in dpaa2_ni_init()Nick Price2026-07-191-0/+11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | dpaa2_ni_init() only enabled the DPNI object; it never pushed the promiscuous/allmulti state or the multicast filter table to the MC firmware. The SIOCSIFFLAGS handler ignores flag changes that arrive while the interface is down, yet still latches them into sc->if_flags, so a promiscuous mode request made before the first up was silently lost and could never be applied afterwards: the up path runs dpaa2_ni_init(), which did not read the flags, and every later SIOCSIFFLAGS compares against the already-latched value and sees no change. This is exactly what happens when if_bridge adds a dpni member while the dpni is still down, e.g. rc.conf's create_args_bridge0="... addm dpni0" running at bridge clone time, before ifconfig_dpni0="up" is processed. bridge_ioctl_add() puts the member into promiscuous mode at addm time; the request never reaches the firmware, so the DPNI continues to hardware-filter unicast destined to other MACs. ifconfig still reports PROMISC (a stack-level flag), which makes the failure invisible: the host stays reachable only via the DPNI's own MAC address (e.g. with net.link.bridge.inherit_mac=1), while bridged epair/vnet jail traffic is silently dropped on RX. Reapply both pieces of administrative state after enabling the DPNI, as other NIC drivers do in their init path. This also restores multicast memberships joined while the interface was down. PR: 292006 Reported by: jhibbits Signed-off-by: Nick Price <nick@spun.io> Reviewed by: jhibbits Differential Revision: https://reviews.freebsd.org/D58330
* firewire: remove SPL callsAbdelkader Boudih2026-07-178-107/+14
| | | | | | | | SPL is a no-op on amd64. Real locking is already handled by fc_mtx and per-driver mutexes. Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D58210
* fwdv: match unit directories instead of IDENTIFYAbdelkader Boudih2026-07-171-87/+26
| | | | | | | Migrated fwdv to use per-unit-directory child device Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58204
* fwisound: match unit directories instead of IDENTIFYAbdelkader Boudih2026-07-171-116/+50
| | | | | | | Migrated fwisound to use per-unit-directory child device Differential Revision: https://reviews.freebsd.org/D58203 Reviewed by: adrian