Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 26 Nov 2008 07:36:17 +0000 (UTC)
From:      Pyun YongHyeon <yongari@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r185330 - head/sys/dev/fxp
Message-ID:  <200811260736.mAQ7aHqb048004@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: yongari
Date: Wed Nov 26 07:36:17 2008
New Revision: 185330
URL: http://svn.freebsd.org/changeset/base/185330

Log:
  Implement TSO for 82550/82551 controllers.
   o Configure controller to use dynamic TBD as TSO requires that
     operation mode.
   o Add a dummy TBD to tx_cb_u as TSO can access one more TBD in TSO
     operation.
   o Increase a DMA segment size to 4096 to hold a full IP segment
     with link layer header.
   o Unlike other TSO capable controllers, 82550/82551 does not
     modify the first IP packet in TSO operation so driver should
     create an IP packet with proper header. Subsequent IP packets
     are generated from the header information in the first IP packet
     header. Likewise pseudo checksum also should be computed by
     driver for the first packet.
   o TSO requires one more TBD to hold total TCP payload. To make
     code simple for TSO/non-TSO case, increase the index of the
     first available TBD array.
   o Remove KASSERT that checks the size of a DMA segment should be
     less than or equal to MCLBYTES as it's no longer valid in TSO.
   o Tx threshold and number of TBDs field is used to store MSS in
     TSO. So don't set the Tx threshold in TSO case.

Modified:
  head/sys/dev/fxp/if_fxp.c
  head/sys/dev/fxp/if_fxpreg.h
  head/sys/dev/fxp/if_fxpvar.h

Modified: head/sys/dev/fxp/if_fxp.c
==============================================================================
--- head/sys/dev/fxp/if_fxp.c	Wed Nov 26 06:36:53 2008	(r185329)
+++ head/sys/dev/fxp/if_fxp.c	Wed Nov 26 07:36:17 2008	(r185330)
@@ -619,11 +619,15 @@ fxp_attach(device_t dev)
 	 * Allocate DMA tags and DMA safe memory.
 	 */
 	sc->maxtxseg = FXP_NTXSEG;
-	if (sc->flags & FXP_FLAG_EXT_RFA)
+	sc->maxsegsize = MCLBYTES;
+	if (sc->flags & FXP_FLAG_EXT_RFA) {
 		sc->maxtxseg--;
+		sc->maxsegsize = FXP_TSO_SEGSIZE;
+	}
 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 2, 0,
 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
-	    MCLBYTES * sc->maxtxseg, sc->maxtxseg, MCLBYTES, 0,
+	    sc->maxsegsize * sc->maxtxseg + sizeof(struct ether_vlan_header),
+	    sc->maxtxseg, sc->maxsegsize, 0,
 	    busdma_lock_mutex, &Giant, &sc->fxp_mtag);
 	if (error) {
 		device_printf(dev, "could not allocate dma tag\n");
@@ -780,11 +784,11 @@ fxp_attach(device_t dev)
 
 	ifp->if_capabilities = ifp->if_capenable = 0;
 
-	/* Enable checksum offload for 82550 or better chips */
+	/* Enable checksum offload/TSO for 82550 or better chips */
 	if (sc->flags & FXP_FLAG_EXT_RFA) {
-		ifp->if_hwassist = FXP_CSUM_FEATURES;
-		ifp->if_capabilities |= IFCAP_HWCSUM;
-		ifp->if_capenable |= IFCAP_HWCSUM;
+		ifp->if_hwassist = FXP_CSUM_FEATURES | CSUM_TSO;
+		ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_TSO4;
+		ifp->if_capenable |= IFCAP_HWCSUM | IFCAP_TSO4;
 	}
 
 	if (sc->flags & FXP_FLAG_82559_RXCSUM) {
@@ -1275,12 +1279,15 @@ fxp_encap(struct fxp_softc *sc, struct m
 	struct mbuf *m;
 	struct fxp_tx *txp;
 	struct fxp_cb_tx *cbp;
+	struct tcphdr *tcp;
 	bus_dma_segment_t segs[FXP_NTXSEG];
-	int error, i, nseg;
+	int error, i, nseg, tcp_payload;
 
 	FXP_LOCK_ASSERT(sc, MA_OWNED);
 	ifp = sc->ifp;
 
+	tcp_payload = 0;
+	tcp = NULL;
 	/*
 	 * Get pointer to next available tx desc.
 	 */
@@ -1358,6 +1365,75 @@ fxp_encap(struct fxp_softc *sc, struct m
 #endif
 	}
 
+	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
+		/*
+		 * 82550/82551 requires ethernet/IP/TCP headers must be
+		 * contained in the first active transmit buffer.
+		 */
+		struct ether_header *eh;
+		struct ip *ip;
+		uint32_t ip_off, poff;
+
+		if (M_WRITABLE(*m_head) == 0) {
+			/* Get a writable copy. */
+			m = m_dup(*m_head, M_DONTWAIT);
+			m_freem(*m_head);
+			if (m == NULL) {
+				*m_head = NULL;
+				return (ENOBUFS);
+			}
+			*m_head = m;
+		}
+		ip_off = sizeof(struct ether_header);
+		m = m_pullup(*m_head, ip_off);
+		if (m == NULL) {
+			*m_head = NULL;
+			return (ENOBUFS);
+		}
+		eh = mtod(m, struct ether_header *);
+		/* Check the existence of VLAN tag. */
+		if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
+			ip_off = sizeof(struct ether_vlan_header);
+			m = m_pullup(m, ip_off);
+			if (m == NULL) {
+				*m_head = NULL;
+				return (ENOBUFS);
+			}
+		}
+		m = m_pullup(m, ip_off + sizeof(struct ip));
+		if (m == NULL) {
+			*m_head = NULL;
+			return (ENOBUFS);
+		}
+		ip = (struct ip *)(mtod(m, char *) + ip_off);
+		poff = ip_off + (ip->ip_hl << 2);
+		m = m_pullup(m, poff + sizeof(struct tcphdr));
+		if (m == NULL) {
+			*m_head = NULL;
+			return (ENOBUFS);
+		}
+		tcp = (struct tcphdr *)(mtod(m, char *) + poff);
+		m = m_pullup(m, poff + sizeof(struct tcphdr) + tcp->th_off);
+		if (m == NULL) {
+			*m_head = NULL;
+			return (ENOBUFS);
+		}
+
+		/*
+		 * Since 82550/82551 doesn't modify IP length and pseudo
+		 * checksum in the first frame driver should compute it.
+		 */
+		ip->ip_sum = 0;
+		ip->ip_len = htons(ifp->if_mtu);
+		tcp->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+		    htons(IPPROTO_TCP + (tcp->th_off << 2) +
+		    m->m_pkthdr.tso_segsz));
+		/* Compute total TCP payload. */
+		tcp_payload = m->m_pkthdr.len - ip_off - (ip->ip_hl << 2);
+		tcp_payload -= tcp->th_off << 2;
+		*m_head = m;
+	}
+
 	error = bus_dmamap_load_mbuf_sg(sc->fxp_mtag, txp->tx_map, *m_head,
 	    segs, &nseg, 0);
 	if (error == EFBIG) {
@@ -1388,7 +1464,6 @@ fxp_encap(struct fxp_softc *sc, struct m
 
 	cbp = txp->tx_cb;
 	for (i = 0; i < nseg; i++) {
-		KASSERT(segs[i].ds_len <= MCLBYTES, ("segment size too large"));
 		/*
 		 * If this is an 82550/82551, then we're using extended
 		 * TxCBs _and_ we're using checksum offload. This means
@@ -1403,14 +1478,28 @@ fxp_encap(struct fxp_softc *sc, struct m
 		 * the chip is an 82550/82551 or not.
 		 */
 		if (sc->flags & FXP_FLAG_EXT_RFA) {
-			cbp->tbd[i + 1].tb_addr = htole32(segs[i].ds_addr);
-			cbp->tbd[i + 1].tb_size = htole32(segs[i].ds_len);
+			cbp->tbd[i + 2].tb_addr = htole32(segs[i].ds_addr);
+			cbp->tbd[i + 2].tb_size = htole32(segs[i].ds_len);
 		} else {
 			cbp->tbd[i].tb_addr = htole32(segs[i].ds_addr);
 			cbp->tbd[i].tb_size = htole32(segs[i].ds_len);
 		}
 	}
-	cbp->tbd_number = nseg;
+	if (sc->flags & FXP_FLAG_EXT_RFA) {
+		/* Configure dynamic TBD for 82550/82551. */
+		cbp->tbd_number = 0xFF;
+		cbp->tbd[nseg + 1].tb_size |= htole32(0x8000);
+	} else
+		cbp->tbd_number = nseg;
+	/* Configure TSO. */
+	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
+		cbp->tbd[-1].tb_size = htole32(m->m_pkthdr.tso_segsz << 16);
+		cbp->tbd[1].tb_size = htole32(tcp_payload << 16);
+		cbp->ipcb_ip_schedule |= FXP_IPCB_LARGESEND_ENABLE |
+		    FXP_IPCB_IP_CHECKSUM_ENABLE |
+		    FXP_IPCB_TCP_PACKET |
+		    FXP_IPCB_TCPUDP_CHECKSUM_ENABLE;
+	}
 
 	txp->tx_mbuf = m;
 	txp->tx_cb->cb_status = 0;
@@ -1423,7 +1512,8 @@ fxp_encap(struct fxp_softc *sc, struct m
 		txp->tx_cb->cb_command =
 		    htole16(sc->tx_cmd | FXP_CB_COMMAND_SF |
 		    FXP_CB_COMMAND_S | FXP_CB_COMMAND_I);
-	txp->tx_cb->tx_threshold = tx_threshold;
+	if ((m->m_pkthdr.csum_flags & CSUM_TSO) == 0)
+		txp->tx_cb->tx_threshold = tx_threshold;
 
 	/*
 	 * Advance the end of list forward.
@@ -2097,7 +2187,7 @@ fxp_init_body(struct fxp_softc *sc)
 	cbp->disc_short_rx =	!prm;	/* discard short packets */
 	cbp->underrun_retry =	1;	/* retry mode (once) on DMA underrun */
 	cbp->two_frames =	0;	/* do not limit FIFO to 2 frames */
-	cbp->dyn_tbd =		0;	/* (no) dynamic TBD mode */
+	cbp->dyn_tbd =		sc->flags & FXP_FLAG_EXT_RFA ? 1 : 0;
 	cbp->ext_rfa =		sc->flags & FXP_FLAG_EXT_RFA ? 1 : 0;
 	cbp->mediatype =	sc->flags & FXP_FLAG_SERIAL_MEDIA ? 0 : 1;
 	cbp->csma_dis =		0;	/* (don't) disable link */
@@ -2589,6 +2679,14 @@ fxp_ioctl(struct ifnet *ifp, u_long comm
 			if ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0)
 				reinit++;
 		}
+		if ((mask & IFCAP_TSO4) != 0 &&
+		    (ifp->if_capabilities & IFCAP_TSO4) != 0) {
+			ifp->if_capenable ^= IFCAP_TSO4;
+			if ((ifp->if_capenable & IFCAP_TSO4) != 0)
+				ifp->if_hwassist |= CSUM_TSO;
+			else
+				ifp->if_hwassist &= ~CSUM_TSO;
+		}
 		if ((mask & IFCAP_VLAN_MTU) != 0 &&
 		    (ifp->if_capabilities & IFCAP_VLAN_MTU) != 0) {
 			ifp->if_capenable ^= IFCAP_VLAN_MTU;

Modified: head/sys/dev/fxp/if_fxpreg.h
==============================================================================
--- head/sys/dev/fxp/if_fxpreg.h	Wed Nov 26 06:36:53 2008	(r185329)
+++ head/sys/dev/fxp/if_fxpreg.h	Wed Nov 26 07:36:17 2008	(r185330)
@@ -292,7 +292,7 @@ struct fxp_cb_tx {
 	 */
 	union {
 		struct fxp_ipcb ipcb;
-		struct fxp_tbd tbd[FXP_NTXSEG];
+		struct fxp_tbd tbd[FXP_NTXSEG + 1];
 	} tx_cb_u;
 };
 

Modified: head/sys/dev/fxp/if_fxpvar.h
==============================================================================
--- head/sys/dev/fxp/if_fxpvar.h	Wed Nov 26 06:36:53 2008	(r185329)
+++ head/sys/dev/fxp/if_fxpvar.h	Wed Nov 26 07:36:17 2008	(r185330)
@@ -41,6 +41,11 @@
 #define	FXP_NTXCB_HIWAT	((FXP_NTXCB * 7) / 10)
 
 /*
+ * Maximum size of a DMA segment.
+ */
+#define	FXP_TSO_SEGSIZE	4096
+
+/*
  * Size of the TxCB list.
  */
 #define FXP_TXCB_SZ	(FXP_NTXCB * sizeof(struct fxp_cb_tx))
@@ -157,6 +162,7 @@ struct fxp_softc {
 	bus_dmamap_t spare_map;		/* spare DMA map */
 	struct fxp_desc_list fxp_desc;	/* descriptors management struct */
 	int maxtxseg;			/* maximum # of TX segments */
+	int maxsegsize;			/* maximum size of a TX segment */
 	int tx_queued;			/* # of active TxCB's */
 	int need_mcsetup;		/* multicast filter needs programming */
 	struct fxp_stats *fxp_stats;	/* Pointer to interface stats */



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