As previously threatened, add TCP/IP checksum offload support to
the fxp driver. This is enabled only for the 82550/82551 chips (PCI revision code 12 or 13). RX and TX checksum offload are both supported. Transmit offload is limited to TCP and UDP only right now: there seems to be a problem with IP header checksumming on transmit in some cases. This chip has hardware VLAN support as well. I hope to enable support for this eventually.
This commit is contained in:
parent
d52cf765d3
commit
c8bca6dc3d
@ -67,6 +67,13 @@
|
||||
#include <net/if_types.h>
|
||||
#include <net/if_vlan_var.h>
|
||||
|
||||
#ifdef FXP_IP_CSUM_WAR
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <machine/in_cksum.h>
|
||||
#endif
|
||||
|
||||
#include <pci/pcivar.h>
|
||||
#include <pci/pcireg.h> /* for PCIM_CMD_xxx */
|
||||
|
||||
@ -169,6 +176,12 @@ static struct fxp_ident fxp_ident_table[] = {
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
#ifdef FXP_IP_CSUM_WAR
|
||||
#define FXP_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
|
||||
#else
|
||||
#define FXP_CSUM_FEATURES (CSUM_TCP | CSUM_UDP)
|
||||
#endif
|
||||
|
||||
static int fxp_probe(device_t dev);
|
||||
static int fxp_attach(device_t dev);
|
||||
static int fxp_detach(device_t dev);
|
||||
@ -592,6 +605,22 @@ fxp_attach(device_t dev)
|
||||
sc->flags |= FXP_FLAG_LONG_PKT_EN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable use of extended RFDs and TCBs for 82550
|
||||
* and later chips. Note: we need extended TXCB support
|
||||
* too, but that's already enabled by the code above.
|
||||
* Be careful to do this only on the right devices.
|
||||
*/
|
||||
|
||||
if (sc->revision == FXP_REV_82550 || sc->revision == FXP_REV_82550_C) {
|
||||
sc->rfa_size = sizeof (struct fxp_rfa);
|
||||
sc->tx_cmd = FXP_CB_COMMAND_IPCBXMIT;
|
||||
sc->flags |= FXP_FLAG_EXT_RFA;
|
||||
} else {
|
||||
sc->rfa_size = sizeof (struct fxp_rfa) - FXP_RFAX_LEN;
|
||||
sc->tx_cmd = FXP_CB_COMMAND_XMIT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read MAC address.
|
||||
*/
|
||||
@ -644,6 +673,13 @@ fxp_attach(device_t dev)
|
||||
ifp->if_start = fxp_start;
|
||||
ifp->if_watchdog = fxp_watchdog;
|
||||
|
||||
/* Enable checksum offload for 82550 or better chips */
|
||||
|
||||
if (sc->flags & FXP_FLAG_EXT_RFA) {
|
||||
ifp->if_hwassist = FXP_CSUM_FEATURES;
|
||||
ifp->if_capabilities = IFCAP_HWCSUM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach the interface.
|
||||
*/
|
||||
@ -1013,6 +1049,7 @@ fxp_start(struct ifnet *ifp)
|
||||
{
|
||||
struct fxp_softc *sc = ifp->if_softc;
|
||||
struct fxp_cb_tx *txp;
|
||||
volatile struct fxp_tbd *bdptr;
|
||||
|
||||
/*
|
||||
* See if we need to suspend xmit until the multicast filter
|
||||
@ -1045,6 +1082,84 @@ fxp_start(struct ifnet *ifp)
|
||||
*/
|
||||
txp = sc->cbl_last->next;
|
||||
|
||||
/*
|
||||
* If this is an 82550/82551, then we're using extended
|
||||
* TxCBs _and_ we're using checksum offload. This means
|
||||
* that the TxCB is really an IPCB. One major difference
|
||||
* between the two is that with plain extended TxCBs,
|
||||
* the bottom half of the TxCB contains two entries from
|
||||
* the TBD array, whereas IPCBs contain just one entry:
|
||||
* one entry (8 bytes) has been sacrificed for the TCP/IP
|
||||
* checksum offload control bits. So to make things work
|
||||
* right, we have to start filling in the TBD array
|
||||
* starting from a different place depending on whether
|
||||
* the chip is an 82550/82551 or not.
|
||||
*/
|
||||
|
||||
bdptr = &txp->tbd[0];
|
||||
if (sc->flags & FXP_FLAG_EXT_RFA)
|
||||
bdptr++;
|
||||
|
||||
/*
|
||||
* Deal with TCP/IP checksum offload. Note that
|
||||
* in order for TCP checksum offload to work,
|
||||
* the pseudo header checksum must have already
|
||||
* been computed and stored in the checksum field
|
||||
* in the TCP header. The stack should have
|
||||
* already done this for us.
|
||||
*/
|
||||
|
||||
if (mb_head->m_pkthdr.csum_flags) {
|
||||
if (mb_head->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
|
||||
txp->ipcb_ip_activation_high =
|
||||
FXP_IPCB_HARDWAREPARSING_ENABLE;
|
||||
txp->ipcb_ip_schedule =
|
||||
FXP_IPCB_TCPUDP_CHECKSUM_ENABLE;
|
||||
if (mb_head->m_pkthdr.csum_flags & CSUM_TCP)
|
||||
txp->ipcb_ip_schedule |=
|
||||
FXP_IPCB_TCP_PACKET;
|
||||
}
|
||||
#ifdef FXP_IP_CSUM_WAR
|
||||
/*
|
||||
* XXX The 82550 chip appears to have trouble
|
||||
* dealing with IP header checksums in very small
|
||||
* datagrams, namely fragments from 1 to 3 bytes
|
||||
* in size. For example, say you want to transmit
|
||||
* a UDP packet of 1473 bytes. The packet will be
|
||||
* fragmented over two IP datagrams, the latter
|
||||
* containing only one byte of data. The 82550 will
|
||||
* botch the header checksum on the 1-byte fragment.
|
||||
* As long as the datagram contains 4 or more bytes
|
||||
* of data, you're ok.
|
||||
*
|
||||
* The following code attempts to work around this
|
||||
* problem: if the datagram is less than 38 bytes
|
||||
* in size (14 bytes ether header, 20 bytes IP header,
|
||||
* plus 4 bytes of data), we punt and compute the IP
|
||||
* header checksum by hand. This workaround doesn't
|
||||
* work very well, however, since it can be fooled
|
||||
* by things like VLAN tags and IP options that make
|
||||
* the header sizes/offsets vary.
|
||||
*/
|
||||
|
||||
if (mb_head->m_pkthdr.csum_flags & CSUM_IP) {
|
||||
if (mb_head->m_pkthdr.len < 38) {
|
||||
struct ip *ip;
|
||||
mb_head->m_data += ETHER_HDR_LEN;
|
||||
ip = mtod(mb_head, struct ip *);
|
||||
ip->ip_sum = in_cksum(mb_head,
|
||||
ip->ip_hl << 2);
|
||||
mb_head->m_data -= ETHER_HDR_LEN;
|
||||
} else {
|
||||
txp->ipcb_ip_activation_high =
|
||||
FXP_IPCB_HARDWAREPARSING_ENABLE;
|
||||
txp->ipcb_ip_schedule |=
|
||||
FXP_IPCB_IP_CHECKSUM_ENABLE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through each of the mbufs in the chain and initialize
|
||||
* the transmit buffer descriptors with the physical address
|
||||
@ -1053,11 +1168,11 @@ fxp_start(struct ifnet *ifp)
|
||||
tbdinit:
|
||||
for (m = mb_head, segment = 0; m != NULL; m = m->m_next) {
|
||||
if (m->m_len != 0) {
|
||||
if (segment == FXP_NTXSEG)
|
||||
if (segment == (FXP_NTXSEG - 1))
|
||||
break;
|
||||
txp->tbd[segment].tb_addr =
|
||||
bdptr[segment].tb_addr =
|
||||
vtophys(mtod(m, vm_offset_t));
|
||||
txp->tbd[segment].tb_size = m->m_len;
|
||||
bdptr[segment].tb_size = m->m_len;
|
||||
segment++;
|
||||
}
|
||||
}
|
||||
@ -1090,16 +1205,17 @@ fxp_start(struct ifnet *ifp)
|
||||
goto tbdinit;
|
||||
}
|
||||
|
||||
txp->byte_count = 0;
|
||||
txp->tbd_number = segment;
|
||||
txp->mb_head = mb_head;
|
||||
txp->cb_status = 0;
|
||||
if (sc->tx_queued != FXP_CXINT_THRESH - 1) {
|
||||
txp->cb_command =
|
||||
FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF |
|
||||
sc->tx_cmd | FXP_CB_COMMAND_SF |
|
||||
FXP_CB_COMMAND_S;
|
||||
} else {
|
||||
txp->cb_command =
|
||||
FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF |
|
||||
sc->tx_cmd | FXP_CB_COMMAND_SF |
|
||||
FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
|
||||
/*
|
||||
* Set a 5 second timer just in case we don't hear
|
||||
@ -1270,6 +1386,8 @@ fxp_intr_body(struct fxp_softc *sc, u_int8_t statack, int count)
|
||||
if (txp->mb_head != NULL) {
|
||||
m_freem(txp->mb_head);
|
||||
txp->mb_head = NULL;
|
||||
/* clear this to reset csum offload bits */
|
||||
txp->tbd[0].tb_addr = 0;
|
||||
}
|
||||
sc->tx_queued--;
|
||||
}
|
||||
@ -1352,6 +1470,26 @@ fxp_intr_body(struct fxp_softc *sc, u_int8_t statack, int count)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do IP checksum checking. */
|
||||
if (rfa->rfa_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;
|
||||
|
||||
@ -1426,6 +1564,8 @@ fxp_tick(void *xsc)
|
||||
if (txp->mb_head != NULL) {
|
||||
m_freem(txp->mb_head);
|
||||
txp->mb_head = NULL;
|
||||
/* clear this to reset csum offload bits */
|
||||
txp->tbd[0].tb_addr = 0;
|
||||
}
|
||||
sc->tx_queued--;
|
||||
}
|
||||
@ -1516,6 +1656,8 @@ fxp_stop(struct fxp_softc *sc)
|
||||
if (txp[i].mb_head != NULL) {
|
||||
m_freem(txp[i].mb_head);
|
||||
txp[i].mb_head = NULL;
|
||||
/* clear this to reset csum offload bits */
|
||||
txp[i].tbd[0].tb_addr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1636,7 +1778,7 @@ fxp_init(void *xsc)
|
||||
cbp->cb_status = 0;
|
||||
cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL;
|
||||
cbp->link_addr = -1; /* (no) next command */
|
||||
cbp->byte_count = 22; /* (22) bytes to config */
|
||||
cbp->byte_count = sc->flags & FXP_FLAG_EXT_RFA ? 32 : 22;
|
||||
cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */
|
||||
cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */
|
||||
cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */
|
||||
@ -1659,6 +1801,7 @@ fxp_init(void *xsc)
|
||||
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->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 */
|
||||
@ -1690,6 +1833,7 @@ fxp_init(void *xsc)
|
||||
cbp->fdx_pin_en = 1; /* (enable) FDX# pin */
|
||||
cbp->multi_ia = 0; /* (don't) accept multiple IAs */
|
||||
cbp->mc_all = sc->flags & FXP_FLAG_ALL_MCAST ? 1 : 0;
|
||||
cbp->gamla_rx = sc->flags & FXP_FLAG_EXT_RFA ? 1 : 0;
|
||||
|
||||
if (sc->revision == FXP_REV_82557) {
|
||||
/*
|
||||
@ -1892,8 +2036,9 @@ fxp_add_rfabuf(struct fxp_softc *sc, struct mbuf *oldm)
|
||||
* data start past it.
|
||||
*/
|
||||
rfa = mtod(m, struct fxp_rfa *);
|
||||
m->m_data += sizeof(struct fxp_rfa);
|
||||
rfa->size = (u_int16_t)(MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE);
|
||||
m->m_data += sc->rfa_size;
|
||||
rfa->size = (u_int16_t)(MCLBYTES - sizeof(struct fxp_rfa) -
|
||||
RFA_ALIGNMENT_FUDGE);
|
||||
|
||||
/*
|
||||
* Initialize the rest of the RFA. Note that since the RFA
|
||||
|
@ -154,7 +154,8 @@ struct fxp_cb_config {
|
||||
save_bf:1;
|
||||
volatile u_int disc_short_rx:1,
|
||||
underrun_retry:2,
|
||||
:3,
|
||||
:2,
|
||||
ext_rfa:1, /* 550 */
|
||||
two_frames:1, /* 8,9 */
|
||||
dyn_tbd:1; /* 8,9 */
|
||||
volatile u_int mediatype:1, /* 7 */
|
||||
@ -208,6 +209,8 @@ struct fxp_cb_config {
|
||||
volatile u_int :3,
|
||||
mc_all:1,
|
||||
:4;
|
||||
volatile u_int8_t gamla_rx:1; /* 550 */
|
||||
volatile u_int8_t pad[9]; /* 550 */
|
||||
};
|
||||
|
||||
#define MAXMCADDR 80
|
||||
@ -243,6 +246,27 @@ struct fxp_tbd {
|
||||
volatile u_int32_t tb_addr;
|
||||
volatile u_int32_t tb_size;
|
||||
};
|
||||
|
||||
struct fxp_ipcb {
|
||||
/*
|
||||
* The following fields are valid only when
|
||||
* using the IPCB command block for TX checksum offload
|
||||
* (and TCP large send, VLANs, and (I think) IPsec). To use
|
||||
* them, you must enable extended TxCBs (available only
|
||||
* on the 82559 and later) and use the IPCBXMIT command.
|
||||
* Note that Intel defines the IPCB to be 32 bytes long,
|
||||
* the last 8 bytes of which comprise the first entry
|
||||
* in the TBD array (see note below). This means we only
|
||||
* have to define 8 extra bytes here.
|
||||
*/
|
||||
volatile u_int16_t ipcb_schedule_low;
|
||||
volatile u_int8_t ipcb_ip_schedule;
|
||||
volatile u_int8_t ipcb_ip_activation_high;
|
||||
volatile u_int16_t ipcb_vlan_id;
|
||||
volatile u_int8_t ipcb_ip_header_offset;
|
||||
volatile u_int8_t ipcb_tcp_header_offset;
|
||||
};
|
||||
|
||||
struct fxp_cb_tx {
|
||||
struct fxp_cb_tx *next;
|
||||
struct mbuf *mb_head;
|
||||
@ -253,15 +277,37 @@ struct fxp_cb_tx {
|
||||
volatile u_int16_t byte_count;
|
||||
volatile u_int8_t tx_threshold;
|
||||
volatile u_int8_t tbd_number;
|
||||
|
||||
/*
|
||||
* The following structure isn't actually part of the TxCB,
|
||||
* unless the extended TxCB feature is being used. In this
|
||||
* case, the first two elements of the structure below are
|
||||
* fetched along with the TxCB.
|
||||
*/
|
||||
volatile struct fxp_tbd tbd[FXP_NTXSEG];
|
||||
union {
|
||||
volatile struct fxp_ipcb;
|
||||
volatile struct fxp_tbd tbd[FXP_NTXSEG];
|
||||
} tx_cb_u;
|
||||
};
|
||||
|
||||
#define tbd tx_cb_u.tbd
|
||||
#define ipcb_schedule_low tx_cb_u.ipcb_schedule_low
|
||||
#define ipcb_ip_schedule tx_cb_u.ipcb_ip_schedule
|
||||
#define ipcb_ip_activation_high tx_cb_u.ipcb_ip_activation_high
|
||||
#define ipcb_vlan_id tx_cb_u.ipcb_vlan_id
|
||||
#define ipcb_ip_header_offset tx_cb_u.ipcb_ip_header_offset
|
||||
#define ipcb_tcp_header_offset tx_cb_u.ipcb_tcp_header_offset
|
||||
|
||||
/*
|
||||
* IPCB field definitions
|
||||
*/
|
||||
#define FXP_IPCB_IP_CHECKSUM_ENABLE 0x10
|
||||
#define FXP_IPCB_TCPUDP_CHECKSUM_ENABLE 0x20
|
||||
#define FXP_IPCB_TCP_PACKET 0x40
|
||||
#define FXP_IPCB_LARGESEND_ENABLE 0x80
|
||||
#define FXP_IPCB_HARDWAREPARSING_ENABLE 0x01
|
||||
#define FXP_IPCB_INSERTVLAN_ENABLE 0x02
|
||||
|
||||
/*
|
||||
* Control Block (CB) definitions
|
||||
*/
|
||||
@ -278,6 +324,9 @@ struct fxp_cb_tx {
|
||||
#define FXP_CB_COMMAND_UCODE 0x5
|
||||
#define FXP_CB_COMMAND_DUMP 0x6
|
||||
#define FXP_CB_COMMAND_DIAG 0x7
|
||||
#define FXP_CB_COMMAND_LOADFILT 0x8
|
||||
#define FXP_CB_COMMAND_IPCBXMIT 0x9
|
||||
|
||||
/* command flags */
|
||||
#define FXP_CB_COMMAND_SF 0x0008 /* simple/flexible mode */
|
||||
#define FXP_CB_COMMAND_I 0x2000 /* generate interrupt on completion */
|
||||
@ -295,9 +344,25 @@ struct fxp_rfa {
|
||||
volatile u_int8_t rbd_addr[4];
|
||||
volatile u_int16_t actual_size;
|
||||
volatile u_int16_t size;
|
||||
|
||||
/*
|
||||
* The following fields are only available when using
|
||||
* extended receive mode on an 82550/82551 chipset.
|
||||
*/
|
||||
volatile u_int16_t rfax_vlan_id;
|
||||
volatile u_int8_t rfax_rx_parser_sts;
|
||||
volatile u_int8_t rfax_rsvd0;
|
||||
volatile u_int16_t rfax_security_sts;
|
||||
volatile u_int8_t rfax_csum_sts;
|
||||
volatile u_int8_t rfax_zerocopy_sts;
|
||||
volatile u_int8_t rfax_pad[8];
|
||||
};
|
||||
#define FXP_RFAX_LEN 16
|
||||
|
||||
#define FXP_RFA_STATUS_RCOL 0x0001 /* receive collision */
|
||||
#define FXP_RFA_STATUS_IAMATCH 0x0002 /* 0 = matches station address */
|
||||
#define FXP_RFA_STATUS_NOAMATCH 0x0004 /* 1 = doesn't match anything */
|
||||
#define FXP_RFA_STATUS_PARSE 0x0008 /* pkt parse ok (82550/1 only) */
|
||||
#define FXP_RFA_STATUS_S4 0x0010 /* receive error from PHY */
|
||||
#define FXP_RFA_STATUS_TL 0x0020 /* type/length */
|
||||
#define FXP_RFA_STATUS_FTS 0x0080 /* frame too short */
|
||||
@ -312,6 +377,19 @@ struct fxp_rfa {
|
||||
#define FXP_RFA_CONTROL_S 0x4000 /* suspend after reception */
|
||||
#define FXP_RFA_CONTROL_EL 0x8000 /* end of list */
|
||||
|
||||
/* Bits in the 'csum_sts' byte */
|
||||
#define FXP_RFDX_CS_TCPUDP_CSUM_BIT_VALID 0x10
|
||||
#define FXP_RFDX_CS_TCPUDP_CSUM_VALID 0x20
|
||||
#define FXP_RFDX_CS_IP_CSUM_BIT_VALID 0x01
|
||||
#define FXP_RFDX_CS_IP_CSUM_VALID 0x02
|
||||
|
||||
/* Bits in the 'packet parser' byte */
|
||||
#define FXP_RFDX_P_PARSE_BIT 0x08
|
||||
#define FXP_RFDX_P_CSUM_PROTOCOL_MASK 0x03
|
||||
#define FXP_RFDX_P_TCP_PACKET 0x00
|
||||
#define FXP_RFDX_P_UDP_PACKET 0x01
|
||||
#define FXP_RFDX_P_IP_PACKET 0x03
|
||||
|
||||
/*
|
||||
* Statistics dump area definitions
|
||||
*/
|
||||
|
@ -146,6 +146,8 @@ struct fxp_softc {
|
||||
u_int8_t saved_intline;
|
||||
u_int8_t saved_cachelnsz;
|
||||
u_int8_t saved_lattimer;
|
||||
u_int8_t rfa_size;
|
||||
u_int32_t tx_cmd;
|
||||
};
|
||||
|
||||
#define FXP_FLAG_MWI_ENABLE 0x0001 /* MWI enable */
|
||||
@ -158,6 +160,7 @@ struct fxp_softc {
|
||||
#define FXP_FLAG_CU_RESUME_BUG 0x0080 /* requires workaround for CU_RESUME */
|
||||
#define FXP_FLAG_UCODE 0x0100 /* ucode is loaded */
|
||||
#define FXP_FLAG_DEFERRED_RNR 0x0200 /* DEVICE_POLLING deferred RNR */
|
||||
#define FXP_FLAG_EXT_RFA 0x0400 /* extended RFDs for csum offload */
|
||||
|
||||
/* Macros to ease CSR access. */
|
||||
#define CSR_READ_1(sc, reg) \
|
||||
|
Loading…
Reference in New Issue
Block a user