Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 30 Jul 2017 23:35:21 +0000 (UTC)
From:      Andriy Voskoboinyk <avos@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r321735 - in head/sys/dev/rtwn: . usb
Message-ID:  <201707302335.v6UNZL7x007411@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avos
Date: Sun Jul 30 23:35:21 2017
New Revision: 321735
URL: https://svnweb.freebsd.org/changeset/base/321735

Log:
  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

Modified:
  head/sys/dev/rtwn/if_rtwnreg.h
  head/sys/dev/rtwn/usb/rtwn_usb_attach.c
  head/sys/dev/rtwn/usb/rtwn_usb_ep.c
  head/sys/dev/rtwn/usb/rtwn_usb_rx.c
  head/sys/dev/rtwn/usb/rtwn_usb_var.h

Modified: head/sys/dev/rtwn/if_rtwnreg.h
==============================================================================
--- head/sys/dev/rtwn/if_rtwnreg.h	Sun Jul 30 23:15:30 2017	(r321734)
+++ head/sys/dev/rtwn/if_rtwnreg.h	Sun Jul 30 23:35:21 2017	(r321735)
@@ -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 */

Modified: head/sys/dev/rtwn/usb/rtwn_usb_attach.c
==============================================================================
--- head/sys/dev/rtwn/usb/rtwn_usb_attach.c	Sun Jul 30 23:15:30 2017	(r321734)
+++ head/sys/dev/rtwn/usb/rtwn_usb_attach.c	Sun Jul 30 23:35:21 2017	(r321735)
@@ -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 iee
 
 	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
@@ -259,6 +265,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)
 {
 	struct rtwn_usb_softc *uc = RTWN_USB_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);

Modified: head/sys/dev/rtwn/usb/rtwn_usb_ep.c
==============================================================================
--- head/sys/dev/rtwn/usb/rtwn_usb_ep.c	Sun Jul 30 23:15:30 2017	(r321734)
+++ head/sys/dev/rtwn/usb/rtwn_usb_ep.c	Sun Jul 30 23:35:21 2017	(r321735)
@@ -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);

Modified: head/sys/dev/rtwn/usb/rtwn_usb_rx.c
==============================================================================
--- head/sys/dev/rtwn/usb/rtwn_usb_rx.c	Sun Jul 30 23:15:30 2017	(r321734)
+++ head/sys/dev/rtwn/usb/rtwn_usb_rx.c	Sun Jul 30 23:35:21 2017	(r321735)
@@ -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 rtw
 		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 rtw
 		 */
 		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,14 +149,108 @@ 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;
 	struct mbuf *m, *m0 = NULL;
 	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 le
 		/* 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 le
 		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 le
 		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
 
 	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;

Modified: head/sys/dev/rtwn/usb/rtwn_usb_var.h
==============================================================================
--- head/sys/dev/rtwn/usb/rtwn_usb_var.h	Sun Jul 30 23:15:30 2017	(r321734)
+++ head/sys/dev/rtwn/usb/rtwn_usb_var.h	Sun Jul 30 23:35:21 2017	(r321735)
@@ -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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201707302335.v6UNZL7x007411>