BSD WiFi Buffer Management
FreeBSD (and all systems using net80211) utilize memory buffers to receive, manipulate and process network traffic. These buffers
are pre-allocated during attachment, due to the performance cost associated with dynamic allocation, and utilize a single-linked
queue(3)
structure.
Devices utilize two buffer queues, Rx and Tx. The underlying structure is often the same.
Receive Buffers
There are two types of Rx buffers: Inactive and Active, included in the softc as sc_rx_inactive
and sc_rx_active
, respectively.
Inactive Rx buffers are those that are not currently being utilized by the driver and are available for future use. Active buffers are used to store and process new data from “the air”.
When a new frame is received, the Bus-specific handler will execute the driver-specific Rx handler (ie, otus_rxeof
). This will run
the following sequence.
/* Lock the driver, to prevent two queue operations at once */
DRIVER_LOCK(sc);
/* Identify the first available inactive buffer */
data = STAILQ_FIRST(&sc->sc_rx_inactive);
/* If no buffers are available, return an ENOMEM error condition */
if (data == NULL) {
printf("Driver is out of space\n");
return (ENOMEM);
}
/* Remove the head, now located in `data` from the queue */
STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next)
/* Insert the item into the active list */
STAILQ_INSERT_TAIL(&usc->usc_rx_active, data, next);
/* Unlock the device */
DRIVER_UNLOCK(sc);
From here, the buffer is used to store, manipulate and eventually submit data via a ieee80211_input_*
function. Upon completion,
the buffer is returned to the inactive list as follows:
/* Lock the driver, as previous */
DRIVER_LOCK(sc);
/* Remove the buffer from the active queue */
STAILQ_REMOVE(&sc->sc_tx_active, data, driver_data_type, next);
/* Put it back into the inactive queue */
STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
/* Unlock the device */
DRIVER_UNLOCK(sc);
Transmission Buffers
Tx buffers are very similar, except there are three types: Inactive, Pending and Active, included in the softc as sc_tx_inactive
, sc_tx_pending
and sc_tx_active
, respectively.
- Inactive - As previous, these are unused buffers.
- Pending - These are buffers that are queued for transmission but not currently being transmitted.
- Active - These are buffers that are in the active stage of transmission.
This distinction between Pending and Active allows for things like:
- Prioritization in submission, such as due to QoS
- Batch submission, which aids in performance
- Delaying transmission for frames that are not ready.
Notes
- The names I referenced above are oft-used, but some devices use the name
tx_free_list
- The same logic applies for IRQ or interrupt pipes for PCI and USB, respectively.
- Some OpenBSD devices not maintain an active queue list, but instead simply remove and add the buffer from the inactve list.
Hope this helps someone going forward!