diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index c108dd69b1e89aa5a12336a07b69a5c183c818c1..bbd0ea56635569c6de3978ad47e695701296be86 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -108,6 +108,7 @@ #include <net/pkt_sched.h> #include <linux/list.h> #include <linux/reboot.h> +#include <net/checksum.h> #include <linux/ethtool.h> #include <linux/if_vlan.h> diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 9c7b47d6db8830dff7fed3ade1457589b6fc141a..21408178a439b270affd1e8b0a7363cd9a857966 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -445,6 +445,11 @@ e1000_probe(struct pci_dev *pdev, netdev->features = NETIF_F_SG; } +#ifdef NETIF_F_TSO + if(adapter->hw.mac_type >= e1000_82544) + netdev->features |= NETIF_F_TSO; +#endif + if(pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; @@ -1332,9 +1337,62 @@ e1000_watchdog(unsigned long data) #define E1000_TX_FLAGS_CSUM 0x00000001 #define E1000_TX_FLAGS_VLAN 0x00000002 +#define E1000_TX_FLAGS_TSO 0x00000004 #define E1000_TX_FLAGS_VLAN_MASK 0xffff0000 #define E1000_TX_FLAGS_VLAN_SHIFT 16 +static inline boolean_t +e1000_tso(struct e1000_adapter *adapter, struct sk_buff *skb, int tx_flags) +{ +#ifdef NETIF_F_TSO + struct e1000_context_desc *context_desc; + int i; + uint8_t ipcss, ipcso, tucss, tucso, hdr_len; + uint16_t ipcse, tucse, mss; + + if(skb_shinfo(skb)->tso_size) { + hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + mss = skb_shinfo(skb)->tso_size; + skb->nh.iph->tot_len = 0; + skb->nh.iph->check = 0; + skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr, + skb->nh.iph->daddr, + 0, + IPPROTO_TCP, + 0); + ipcss = skb->nh.raw - skb->data; + ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data; + ipcse = skb->h.raw - skb->data - 1; + tucss = skb->h.raw - skb->data; + tucso = (void *)&(skb->h.th->check) - (void *)skb->data; + tucse = 0; + + i = adapter->tx_ring.next_to_use; + context_desc = E1000_CONTEXT_DESC(adapter->tx_ring, i); + + context_desc->lower_setup.ip_fields.ipcss = ipcss; + context_desc->lower_setup.ip_fields.ipcso = ipcso; + context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse); + context_desc->upper_setup.tcp_fields.tucss = tucss; + context_desc->upper_setup.tcp_fields.tucso = tucso; + context_desc->upper_setup.tcp_fields.tucse = cpu_to_le16(tucse); + context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss); + context_desc->tcp_seg_setup.fields.hdr_len = hdr_len; + context_desc->cmd_and_length = cpu_to_le32(adapter->txd_cmd | + E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE | + E1000_TXD_CMD_IP | E1000_TXD_CMD_TCP | + (skb->len - (hdr_len))); + + i = (i + 1) % adapter->tx_ring.count; + adapter->tx_ring.next_to_use = i; + + return TRUE; + } +#endif + + return FALSE; +} + static inline boolean_t e1000_tx_csum(struct e1000_adapter *adapter, struct sk_buff *skb) { @@ -1434,6 +1492,12 @@ e1000_tx_queue(struct e1000_adapter *adapter, int count, int tx_flags) txd_upper = 0; txd_lower = adapter->txd_cmd; + if(tx_flags & E1000_TX_FLAGS_TSO) { + txd_lower |= E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D | + E1000_TXD_CMD_TSE; + txd_upper |= (E1000_TXD_POPTS_IXSM | E1000_TXD_POPTS_TXSM) << 8; + } + if(tx_flags & E1000_TX_FLAGS_CSUM) { txd_lower |= E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D; txd_upper |= E1000_TXD_POPTS_TXSM << 8; @@ -1478,22 +1542,29 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) for(f = 0; f < skb_shinfo(skb)->nr_frags; f++) count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size, adapter->max_data_per_txd); +#ifdef NETIF_F_TSO + if((skb_shinfo(skb)->tso_size) || (skb->ip_summed == CHECKSUM_HW)) + count++; +#else if(skb->ip_summed == CHECKSUM_HW) count++; +#endif if(E1000_DESC_UNUSED(&adapter->tx_ring) < count) { netif_stop_queue(netdev); return 1; } - if(e1000_tx_csum(adapter, skb)) - tx_flags |= E1000_TX_FLAGS_CSUM; - if(adapter->vlgrp && vlan_tx_tag_present(skb)) { tx_flags |= E1000_TX_FLAGS_VLAN; tx_flags |= (vlan_tx_tag_get(skb) << E1000_TX_FLAGS_VLAN_SHIFT); } + if(e1000_tso(adapter, skb, tx_flags)) + tx_flags |= E1000_TX_FLAGS_TSO; + else if(e1000_tx_csum(adapter, skb)) + tx_flags |= E1000_TX_FLAGS_CSUM; + count = e1000_tx_map(adapter, skb); e1000_tx_queue(adapter, count, tx_flags); diff --git a/drivers/net/e1000/e1000_proc.c b/drivers/net/e1000/e1000_proc.c index 620638eb47c78e90612fe440e2c87617ae1d8ce1..9874c36ae76f3d87315d8df4ced77635a4091695 100644 --- a/drivers/net/e1000/e1000_proc.c +++ b/drivers/net/e1000/e1000_proc.c @@ -667,9 +667,12 @@ e1000_proc_list_setup(struct e1000_adapter *adapter) LIST_ADD_U("Rx_Long_Length_Errors", &adapter->stats.roc); LIST_ADD_U("Rx_Short_Length_Errors", &adapter->stats.ruc); - /* The 82542 does not have an alignment error count register */ - if(adapter->hw.mac_type >= e1000_82543) + /* The 82542 does not have some of these stats */ + if(adapter->hw.mac_type >= e1000_82543) { LIST_ADD_U("Rx_Align_Errors", &adapter->stats.algnerrc); + LIST_ADD_U("Tx_TCP_Seg_Good", &adapter->stats.tsctc); + LIST_ADD_U("Tx_TCP_Seg_Failed", &adapter->stats.tsctfc); + } LIST_ADD_U("Rx_Flow_Control_XON", &adapter->stats.xonrxc); LIST_ADD_U("Rx_Flow_Control_XOFF", &adapter->stats.xoffrxc);