summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2008-11-26 06:36:53 +0000
committeryongari <yongari@FreeBSD.org>2008-11-26 06:36:53 +0000
commitfee7af0f4b62cc4581d450254b96822bef873dcb (patch)
tree1c36faa350eb6634a84743c2a1f22607d0b5bc2f
parenta0562881c5d4edb972fb1e5d44d22fb7d65e7a18 (diff)
downloadFreeBSD-src-fee7af0f4b62cc4581d450254b96822bef873dcb.zip
FreeBSD-src-fee7af0f4b62cc4581d450254b96822bef873dcb.tar.gz
Implement Rx checksum offload for 82559 or later controllers.
82559 or later controllers added simple checksum calculation logic in RU. For backward compatibility the computed checksum is appended at the end of the data posted to Rx buffer. This type of simple checksum calculation support had been used on several vendors such as Sun HME/GEM, SysKonnect GENESIS and Marvell Yukon controllers. Because this type of checksum offload support requires parsing of received frame and pseudo checksum calculation with software routine it still consumes more CPU cycles than that of full-fledged checksum offload controller. But it's still better than software checksum calculation.
-rw-r--r--sys/dev/fxp/if_fxp.c137
-rw-r--r--sys/dev/fxp/if_fxpvar.h1
2 files changed, 110 insertions, 28 deletions
diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c
index e969cdc..05632fd 100644
--- a/sys/dev/fxp/if_fxp.c
+++ b/sys/dev/fxp/if_fxp.c
@@ -59,15 +59,15 @@ __FBSDID("$FreeBSD$");
#include <net/if_types.h>
#include <net/if_vlan_var.h>
-#include <machine/bus.h>
-#include <machine/resource.h>
-
-#ifdef FXP_IP_CSUM_WAR
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include <machine/bus.h>
#include <machine/in_cksum.h>
-#endif
+#include <machine/resource.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h> /* for PCIM_CMD_xxx */
@@ -213,6 +213,8 @@ static int fxp_suspend(device_t dev);
static int fxp_resume(device_t dev);
static void fxp_intr(void *xsc);
+static void fxp_rxcsum(struct fxp_softc *sc, struct ifnet *ifp,
+ struct mbuf *m, uint16_t status, int pos);
static void fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp,
uint8_t statack, int count);
static void fxp_init(void *xsc);
@@ -591,6 +593,9 @@ fxp_attach(device_t dev)
sc->flags |= FXP_FLAG_SAVE_BAD;
}
+ /* For 82559 or later chips, Rx checksum offload is supported. */
+ if (sc->revision >= FXP_REV_82559_A0)
+ sc->flags |= FXP_FLAG_82559_RXCSUM;
/*
* Enable use of extended RFDs and TCBs for 82550
* and later chips. Note: we need extended TXCB support
@@ -603,6 +608,8 @@ fxp_attach(device_t dev)
sc->rfa_size = sizeof (struct fxp_rfa);
sc->tx_cmd = FXP_CB_COMMAND_IPCBXMIT;
sc->flags |= FXP_FLAG_EXT_RFA;
+ /* Use extended RFA instead of 82559 checksum mode. */
+ sc->flags &= ~FXP_FLAG_82559_RXCSUM;
} else {
sc->rfa_size = sizeof (struct fxp_rfa) - FXP_RFAX_LEN;
sc->tx_cmd = FXP_CB_COMMAND_XMIT;
@@ -780,6 +787,11 @@ fxp_attach(device_t dev)
ifp->if_capenable |= IFCAP_HWCSUM;
}
+ if (sc->flags & FXP_FLAG_82559_RXCSUM) {
+ ifp->if_capabilities |= IFCAP_RXCSUM;
+ ifp->if_capenable |= IFCAP_RXCSUM;
+ }
+
#ifdef DEVICE_POLLING
/* Inform the world we support polling. */
ifp->if_capabilities |= IFCAP_POLLING;
@@ -1558,6 +1570,84 @@ fxp_txeof(struct fxp_softc *sc)
}
static void
+fxp_rxcsum(struct fxp_softc *sc, struct ifnet *ifp, struct mbuf *m,
+ uint16_t status, int pos)
+{
+ struct ether_header *eh;
+ struct ip *ip;
+ struct udphdr *uh;
+ int32_t hlen, len, pktlen, temp32;
+ uint16_t csum, *opts;
+
+ if ((sc->flags & FXP_FLAG_82559_RXCSUM) == 0) {
+ if ((status & FXP_RFA_STATUS_PARSE) != 0) {
+ if (status & FXP_RFDX_CS_IP_CSUM_BIT_VALID)
+ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
+ if (status & FXP_RFDX_CS_IP_CSUM_VALID)
+ m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+ if ((status & FXP_RFDX_CS_TCPUDP_CSUM_BIT_VALID) &&
+ (status & FXP_RFDX_CS_TCPUDP_CSUM_VALID)) {
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID |
+ CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+ return;
+ }
+
+ pktlen = m->m_pkthdr.len;
+ if (pktlen < sizeof(struct ether_header) + sizeof(struct ip))
+ return;
+ eh = mtod(m, struct ether_header *);
+ if (eh->ether_type != htons(ETHERTYPE_IP))
+ return;
+ ip = (struct ip *)(eh + 1);
+ if (ip->ip_v != IPVERSION)
+ return;
+
+ hlen = ip->ip_hl << 2;
+ pktlen -= sizeof(struct ether_header);
+ if (hlen < sizeof(struct ip))
+ return;
+ if (ntohs(ip->ip_len) < hlen)
+ return;
+ if (ntohs(ip->ip_len) != pktlen)
+ return;
+ if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
+ return; /* can't handle fragmented packet */
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ if (pktlen < (hlen + sizeof(struct tcphdr)))
+ return;
+ break;
+ case IPPROTO_UDP:
+ if (pktlen < (hlen + sizeof(struct udphdr)))
+ return;
+ uh = (struct udphdr *)((caddr_t)ip + hlen);
+ if (uh->uh_sum == 0)
+ return; /* no checksum */
+ break;
+ default:
+ return;
+ }
+ /* Extract computed checksum. */
+ csum = be16dec(mtod(m, char *) + pos);
+ /* checksum fixup for IP options */
+ len = hlen - sizeof(struct ip);
+ if (len > 0) {
+ opts = (uint16_t *)(ip + 1);
+ for (; len > 0; len -= sizeof(uint16_t), opts++) {
+ temp32 = csum - *opts;
+ temp32 = (temp32 >> 16) + (temp32 & 65535);
+ csum = temp32 & 65535;
+ }
+ }
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
+ m->m_pkthdr.csum_data = csum;
+}
+
+static void
fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack,
int count)
{
@@ -1661,6 +1751,11 @@ fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack,
* of bogus length or CRC errors.
*/
total_len = le16toh(rfa->actual_size) & 0x3fff;
+ if ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0 &&
+ (ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+ /* Adjust for appended checksum bytes. */
+ total_len -= 2;
+ }
if (total_len < sizeof(struct ether_header) ||
total_len > MCLBYTES - RFA_ALIGNMENT_FUDGE -
sc->rfa_size || status & FXP_RFA_STATUS_CRC) {
@@ -1668,30 +1763,12 @@ fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack,
continue;
}
- /* Do IP checksum checking. */
- if ((ifp->if_capenable & IFCAP_RXCSUM) != 0 &&
- (status & FXP_RFA_STATUS_PARSE)) {
- if (rfa->rfax_csum_sts &
- FXP_RFDX_CS_IP_CSUM_BIT_VALID)
- m->m_pkthdr.csum_flags |=
- CSUM_IP_CHECKED;
- if (rfa->rfax_csum_sts &
- FXP_RFDX_CS_IP_CSUM_VALID)
- m->m_pkthdr.csum_flags |=
- CSUM_IP_VALID;
- if ((rfa->rfax_csum_sts &
- FXP_RFDX_CS_TCPUDP_CSUM_BIT_VALID) &&
- (rfa->rfax_csum_sts &
- FXP_RFDX_CS_TCPUDP_CSUM_VALID)) {
- m->m_pkthdr.csum_flags |=
- CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
- m->m_pkthdr.csum_data = 0xffff;
- }
- }
-
m->m_pkthdr.len = m->m_len = total_len;
m->m_pkthdr.rcvif = ifp;
+ /* Do IP checksum checking. */
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ fxp_rxcsum(sc, ifp, m, status, total_len);
/*
* Drop locks before calling if_input() since it
* may re-enter fxp_start() in the netisr case.
@@ -2024,7 +2101,8 @@ fxp_init_body(struct fxp_softc *sc)
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 */
- cbp->tcp_udp_cksum = 0; /* (don't) enable checksum */
+ cbp->tcp_udp_cksum = ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0 &&
+ (ifp->if_capenable & IFCAP_RXCSUM) != 0) ? 1 : 0;
cbp->vlan_tco = 0; /* (don't) enable vlan wakeup */
cbp->link_wake_en = 0; /* (don't) assert PME# on link change */
cbp->arp_wake_en = 0; /* (don't) assert PME# on arp */
@@ -2506,8 +2584,11 @@ fxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
ifp->if_hwassist &= ~FXP_CSUM_FEATURES;
}
if ((mask & IFCAP_RXCSUM) != 0 &&
- (ifp->if_capabilities & IFCAP_RXCSUM) != 0)
+ (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
ifp->if_capenable ^= IFCAP_RXCSUM;
+ if ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0)
+ reinit++;
+ }
if ((mask & IFCAP_VLAN_MTU) != 0 &&
(ifp->if_capabilities & IFCAP_VLAN_MTU) != 0) {
ifp->if_capenable ^= IFCAP_VLAN_MTU;
diff --git a/sys/dev/fxp/if_fxpvar.h b/sys/dev/fxp/if_fxpvar.h
index 1bab01e..fdaff2e 100644
--- a/sys/dev/fxp/if_fxpvar.h
+++ b/sys/dev/fxp/if_fxpvar.h
@@ -194,6 +194,7 @@ struct fxp_softc {
#define FXP_FLAG_DEFERRED_RNR 0x0200 /* DEVICE_POLLING deferred RNR */
#define FXP_FLAG_EXT_RFA 0x0400 /* extended RFDs for csum offload */
#define FXP_FLAG_SAVE_BAD 0x0800 /* save bad pkts: bad size, CRC, etc */
+#define FXP_FLAG_82559_RXCSUM 0x1000 /* 82559 compatible RX checksum */
/* Macros to ease CSR access. */
#define CSR_READ_1(sc, reg) bus_read_1(sc->fxp_res[0], reg)
OpenPOWER on IntegriCloud