rtwn_usb: add support for fragmented Rx.

Since device can pass multiple frames in a single payload temporary
Rx buffer was big enough to hold all of them; now the driver can
concatenate a single frame from multiple payloads.

The Rx buffer size may be configured via tunable (dev.rtwn.%d.rx_buf_size).

Tested with:
 - rtl8188cus, rtl8188eu and rtl8821au (STA mode).
 - (by kevlo) rtl8192cu and rtl8188eu.

PR:		218527
Reviewed by:	kevlo
Differential Revision:	https://reviews.freebsd.org/D11705
This commit is contained in:
Andriy Voskoboinyk 2017-07-30 23:35:21 +00:00
parent e39a96781e
commit 9dba612805
5 changed files with 214 additions and 29 deletions

View File

@ -18,6 +18,9 @@
* $FreeBSD$
*/
#ifndef IF_RTWNREG_H
#define IF_RTWNREG_H
#define R92C_MIN_TX_PWR 0x00
#define R92C_MAX_TX_PWR 0x3f
@ -165,3 +168,5 @@ rtwn_chan2centieee(const struct ieee80211_channel *c)
return (chan);
}
#endif /* IF_RTWNREG_H */

View File

@ -77,12 +77,14 @@ static void rtwn_usb_reset_lists(struct rtwn_softc *,
struct ieee80211vap *);
static void rtwn_usb_reset_tx_list(struct rtwn_usb_softc *,
rtwn_datahead *, struct ieee80211vap *);
static void rtwn_usb_reset_rx_list(struct rtwn_usb_softc *);
static void rtwn_usb_start_xfers(struct rtwn_softc *);
static void rtwn_usb_abort_xfers(struct rtwn_softc *);
static int rtwn_usb_fw_write_block(struct rtwn_softc *,
const uint8_t *, uint16_t, int);
static void rtwn_usb_drop_incorrect_tx(struct rtwn_softc *);
static void rtwn_usb_attach_methods(struct rtwn_softc *);
static void rtwn_usb_sysctlattach(struct rtwn_softc *);
#define RTWN_CONFIG_INDEX 0
@ -133,9 +135,8 @@ rtwn_usb_alloc_rx_list(struct rtwn_softc *sc)
struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
int error, i;
/* XXX recheck */
error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT,
sc->rx_dma_size + 1024);
uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT);
if (error != 0)
return (error);
@ -199,6 +200,9 @@ rtwn_usb_free_rx_list(struct rtwn_softc *sc)
rtwn_usb_free_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT);
uc->uc_rx_stat_len = 0;
uc->uc_rx_off = 0;
STAILQ_INIT(&uc->uc_rx_active);
STAILQ_INIT(&uc->uc_rx_inactive);
}
@ -224,8 +228,10 @@ rtwn_usb_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap)
rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active, vap);
rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending, vap);
if (vap == NULL)
if (vap == NULL) {
rtwn_usb_reset_rx_list(uc);
sc->qfullmsk = 0;
}
}
static void
@ -258,6 +264,23 @@ rtwn_usb_reset_tx_list(struct rtwn_usb_softc *uc,
}
}
static void
rtwn_usb_reset_rx_list(struct rtwn_usb_softc *uc)
{
int i;
for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++) {
struct rtwn_data *dp = &uc->uc_rx[i];
if (dp->m != NULL) {
m_freem(dp->m);
dp->m = NULL;
}
}
uc->uc_rx_stat_len = 0;
uc->uc_rx_off = 0;
}
static void
rtwn_usb_start_xfers(struct rtwn_softc *sc)
{
@ -327,6 +350,31 @@ rtwn_usb_attach_methods(struct rtwn_softc *sc)
sc->bcn_check_interval = 100;
}
static void
rtwn_usb_sysctlattach(struct rtwn_softc *sc)
{
struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
char str[64];
int ret;
ret = snprintf(str, sizeof(str),
"Rx buffer size, 512-byte units [%d...%d]",
RTWN_USB_RXBUFSZ_MIN, RTWN_USB_RXBUFSZ_MAX);
KASSERT(ret > 0, ("ret (%d) <= 0!\n", ret));
(void) ret;
uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_DEF;
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"rx_buf_size", CTLFLAG_RDTUN, &uc->uc_rx_buf_size,
uc->uc_rx_buf_size, str);
if (uc->uc_rx_buf_size < RTWN_USB_RXBUFSZ_MIN)
uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MIN;
if (uc->uc_rx_buf_size > RTWN_USB_RXBUFSZ_MAX)
uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MAX;
}
static int
rtwn_usb_attach(device_t self)
{
@ -343,6 +391,7 @@ rtwn_usb_attach(device_t self)
/* Need to be initialized early. */
rtwn_sysctlattach(sc);
rtwn_usb_sysctlattach(sc);
mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF);
rtwn_usb_attach_methods(sc);

View File

@ -225,7 +225,8 @@ rtwn_usb_setup_endpoints(struct rtwn_usb_softc *uc)
break;
}
rtwn_config[RTWN_BULK_RX].bufsize = sc->rx_dma_size + 1024;
rtwn_config[RTWN_BULK_RX].bufsize =
uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT;
error = usbd_transfer_setup(uc->uc_udev, &iface_index,
uc->uc_xfer, rtwn_config, RTWN_N_TRANSFER, uc, &sc->sc_mtx);
free(rtwn_config, M_TEMP);

View File

@ -63,31 +63,24 @@ __FBSDID("$FreeBSD$");
#include <dev/rtwn/usb/rtwn_usb_var.h>
#include <dev/rtwn/usb/rtwn_usb_rx.h>
static struct mbuf * rtwn_rxeof(struct rtwn_softc *, struct rtwn_data *,
uint8_t *, int);
static struct mbuf *
rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat,
int totlen)
static int
rtwn_rx_check_pre_alloc(struct rtwn_softc *sc,
struct rtwn_rx_stat_common *stat)
{
struct ieee80211com *ic = &sc->sc_ic;
struct mbuf *m;
uint32_t rxdw0;
int pktlen;
RTWN_ASSERT_LOCKED(sc);
/* Dump Rx descriptor. */
RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC,
"%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X\n",
__func__, le32toh(stat->rxdw0), le32toh(stat->rxdw1),
le32toh(stat->rxdw2), le32toh(stat->rxdw3), le32toh(stat->rxdw4),
le32toh(stat->tsf_low));
/*
* don't pass packets to the ieee80211 framework if the driver isn't
* RUNNING.
*/
if (!(sc->sc_flags & RTWN_RUNNING))
return (NULL);
return (-1);
rxdw0 = le32toh(stat->rxdw0);
if (__predict_false(rxdw0 & (RTWN_RXDW0_CRCERR | RTWN_RXDW0_ICVERR))) {
@ -98,7 +91,7 @@ rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat,
RTWN_DPRINTF(sc, RTWN_DEBUG_RECV,
"%s: RX flags error (%s)\n", __func__,
rxdw0 & RTWN_RXDW0_CRCERR ? "CRC" : "ICV");
goto fail;
return (-1);
}
pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN);
@ -108,9 +101,31 @@ rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat,
*/
RTWN_DPRINTF(sc, RTWN_DEBUG_RECV,
"%s: frame is too short: %d\n", __func__, pktlen);
goto fail;
return (-1);
}
return (0);
}
static struct mbuf *
rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat,
int totlen)
{
struct ieee80211com *ic = &sc->sc_ic;
struct mbuf *m;
RTWN_ASSERT_LOCKED(sc);
/* Dump Rx descriptor. */
RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC,
"%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X\n",
__func__, le32toh(stat->rxdw0), le32toh(stat->rxdw1),
le32toh(stat->rxdw2), le32toh(stat->rxdw3), le32toh(stat->rxdw4),
le32toh(stat->tsf_low));
if (rtwn_rx_check_pre_alloc(sc, stat) != 0)
goto fail;
m = m_get2(totlen, M_NOWAIT, MT_DATA, M_PKTHDR);
if (__predict_false(m == NULL)) {
device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n",
@ -134,7 +149,95 @@ fail:
}
static struct mbuf *
rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len)
rtwn_rxeof_fragmented(struct rtwn_usb_softc *uc, struct rtwn_data *data,
uint8_t *buf, int len)
{
struct rtwn_softc *sc = &uc->uc_sc;
struct ieee80211com *ic = &sc->sc_ic;
struct rtwn_rx_stat_common *stat = &uc->uc_rx_stat;
uint32_t rxdw0;
int totlen, pktlen, infosz, min_len;
int orig_len = len;
int alloc_mbuf = 0;
/* Check if Rx descriptor is not truncated. */
if (uc->uc_rx_stat_len < sizeof(*stat)) {
min_len = min(sizeof(*stat) - uc->uc_rx_stat_len, len);
memcpy((uint8_t *)stat + uc->uc_rx_stat_len, buf, min_len);
uc->uc_rx_stat_len += min_len;
buf += min_len;
len -= min_len;
if (uc->uc_rx_stat_len < sizeof(*stat))
goto end;
KASSERT(data->m == NULL, ("%s: data->m != NULL!\n", __func__));
alloc_mbuf = 1;
/* Dump Rx descriptor. */
RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC,
"%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, "
"tsfl %08X\n", __func__, le32toh(stat->rxdw0),
le32toh(stat->rxdw1), le32toh(stat->rxdw2),
le32toh(stat->rxdw3), le32toh(stat->rxdw4),
le32toh(stat->tsf_low));
}
rxdw0 = le32toh(stat->rxdw0);
pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN);
infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8;
totlen = sizeof(*stat) + infosz + pktlen;
if (alloc_mbuf) {
if (rtwn_rx_check_pre_alloc(sc, stat) == 0) {
data->m = m_getm(NULL, totlen, M_NOWAIT, MT_DATA);
if (data->m != NULL) {
m_copyback(data->m, 0, uc->uc_rx_stat_len,
(caddr_t)stat);
if (rtwn_check_frame(sc, data->m) != 0) {
m_freem(data->m);
data->m = NULL;
counter_u64_add(ic->ic_ierrors, 1);
}
} else
counter_u64_add(ic->ic_ierrors, 1);
} else
counter_u64_add(ic->ic_ierrors, 1);
uc->uc_rx_off = sizeof(*stat);
}
/* If mbuf allocation fails just discard the data. */
min_len = min(totlen - uc->uc_rx_off, len);
if (data->m != NULL)
m_copyback(data->m, uc->uc_rx_off, min_len, buf);
uc->uc_rx_off += min_len;
if (uc->uc_rx_off == totlen) {
/* Align next frame. */
min_len = rtwn_usb_align_rx(uc,
orig_len - len + min_len, orig_len);
min_len -= (orig_len - len);
KASSERT(len >= min_len, ("%s: len (%d) < min_len (%d)!\n",
__func__, len, min_len));
/* Clear mbuf stats. */
uc->uc_rx_stat_len = 0;
uc->uc_rx_off = 0;
}
len -= min_len;
buf += min_len;
end:
if (uc->uc_rx_stat_len == 0)
return (rtwn_rxeof(sc, data, buf, len));
else
return (NULL);
}
static struct mbuf *
rtwn_rxeof(struct rtwn_softc *sc, struct rtwn_data *data, uint8_t *buf,
int len)
{
struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
struct rtwn_rx_stat_common *stat;
@ -142,6 +245,12 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len)
uint32_t rxdw0;
int totlen, pktlen, infosz;
/* Prepend defragmented frame (if any). */
if (data->m != NULL) {
m0 = m = data->m;
data->m = NULL;
}
/* Process packets. */
while (len >= sizeof(*stat)) {
stat = (struct rtwn_rx_stat_common *)buf;
@ -156,8 +265,8 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len)
/* Make sure everything fits in xfer. */
totlen = sizeof(*stat) + infosz + pktlen;
if (totlen > len) {
device_printf(sc->sc_dev,
"%s: totlen (%d) > len (%d)!\n",
RTWN_DPRINTF(sc, RTWN_DEBUG_RECV,
"%s: frame is fragmented (totlen %d len %d)\n",
__func__, totlen, len);
break;
}
@ -165,9 +274,9 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len)
if (m0 == NULL)
m0 = m = rtwn_rx_copy_to_mbuf(sc, stat, totlen);
else {
m->m_next = rtwn_rx_copy_to_mbuf(sc, stat, totlen);
if (m->m_next != NULL)
m = m->m_next;
m->m_nextpkt = rtwn_rx_copy_to_mbuf(sc, stat, totlen);
if (m->m_nextpkt != NULL)
m = m->m_nextpkt;
}
/* Align next frame. */
@ -176,6 +285,9 @@ rtwn_rxeof(struct rtwn_softc *sc, uint8_t *buf, int len)
len -= totlen;
}
if (len > 0)
(void)rtwn_rxeof_fragmented(uc, data, buf, len);
return (m0);
}
@ -190,15 +302,19 @@ rtwn_report_intr(struct rtwn_usb_softc *uc, struct usb_xfer *xfer,
usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
if (__predict_false(len < sizeof(struct rtwn_rx_stat_common))) {
if (__predict_false(len < sizeof(struct rtwn_rx_stat_common) &&
uc->uc_rx_stat_len == 0)) {
counter_u64_add(ic->ic_ierrors, 1);
return (NULL);
}
buf = data->buf;
if (uc->uc_rx_stat_len > 0)
return (rtwn_rxeof_fragmented(uc, data, data->buf, len));
switch (rtwn_classify_intr(sc, buf, len)) {
case RTWN_RX_DATA:
return (rtwn_rxeof(sc, buf, len));
return (rtwn_rxeof(sc, data, buf, len));
case RTWN_RX_TX_REPORT:
if (sc->sc_ratectl != RTWN_RATECTL_NET80211) {
/* shouldn't happen */
@ -284,8 +400,8 @@ tr_setup:
* callback and safe to unlock.
*/
while (m != NULL) {
next = m->m_next;
m->m_next = NULL;
next = m->m_nextpkt;
m->m_nextpkt = NULL;
ni = rtwn_rx_frame(sc, m);
@ -309,6 +425,8 @@ tr_setup:
STAILQ_INSERT_TAIL(&uc->uc_rx_inactive, data, next);
}
if (error != USB_ERR_CANCELLED) {
/* XXX restart device if frame was fragmented? */
usbd_xfer_set_stall(xfer);
counter_u64_add(ic->ic_ierrors, 1);
goto tr_setup;

View File

@ -21,6 +21,12 @@
#ifndef RTWN_USBVAR_H
#define RTWN_USBVAR_H
#include <dev/rtwn/if_rtwnreg.h> /* for struct rtwn_rx_stat_common */
#define RTWN_USB_RXBUFSZ_UNIT (512)
#define RTWN_USB_RXBUFSZ_MIN ( 4)
#define RTWN_USB_RXBUFSZ_DEF (24)
#define RTWN_USB_RXBUFSZ_MAX (64)
#define RTWN_USB_TXBUFSZ (16 * 1024)
#define RTWN_IFACE_INDEX 0
@ -58,6 +64,12 @@ struct rtwn_usb_softc {
struct rtwn_data uc_rx[RTWN_USB_RX_LIST_COUNT];
rtwn_datahead uc_rx_active;
rtwn_datahead uc_rx_inactive;
int uc_rx_buf_size;
struct rtwn_rx_stat_common uc_rx_stat;
int uc_rx_stat_len;
int uc_rx_off;
struct rtwn_data uc_tx[RTWN_USB_TX_LIST_COUNT];
rtwn_datahead uc_tx_active;
rtwn_datahead uc_tx_inactive;