diff options
author | Andrew Gallatin <gallatin@myri.com> | 2007-09-17 14:37:42 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 19:47:47 -0400 |
commit | 1e6e9342d41ff80ced0ad5dfcf084926700cdfc5 (patch) | |
tree | f4890526db54f9c8821e2d3e366d116d44b1712a /drivers | |
parent | d4dc4ec9d84e0578b9bfbe56a11fafdb7cbac771 (diff) |
[MYRI10GE]: Use LRO.
Singed off by: Andrew Gallatin <gallatin@myri.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/myri10ge/myri10ge.c | 109 |
2 files changed, 109 insertions, 1 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 61dedfeed62a..40ff9a5269a2 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig | |||
@@ -2583,6 +2583,7 @@ config MYRI10GE | |||
2583 | depends on PCI | 2583 | depends on PCI |
2584 | select FW_LOADER | 2584 | select FW_LOADER |
2585 | select CRC32 | 2585 | select CRC32 |
2586 | select INET_LRO | ||
2586 | ---help--- | 2587 | ---help--- |
2587 | This driver supports Myricom Myri-10G Dual Protocol interface in | 2588 | This driver supports Myricom Myri-10G Dual Protocol interface in |
2588 | Ethernet mode. If the eeprom on your board is not recent enough, | 2589 | Ethernet mode. If the eeprom on your board is not recent enough, |
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index a30146ea51f0..48b23c592406 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #include <linux/etherdevice.h> | 48 | #include <linux/etherdevice.h> |
49 | #include <linux/if_ether.h> | 49 | #include <linux/if_ether.h> |
50 | #include <linux/if_vlan.h> | 50 | #include <linux/if_vlan.h> |
51 | #include <linux/inet_lro.h> | ||
51 | #include <linux/ip.h> | 52 | #include <linux/ip.h> |
52 | #include <linux/inet.h> | 53 | #include <linux/inet.h> |
53 | #include <linux/in.h> | 54 | #include <linux/in.h> |
@@ -62,6 +63,8 @@ | |||
62 | #include <linux/io.h> | 63 | #include <linux/io.h> |
63 | #include <linux/log2.h> | 64 | #include <linux/log2.h> |
64 | #include <net/checksum.h> | 65 | #include <net/checksum.h> |
66 | #include <net/ip.h> | ||
67 | #include <net/tcp.h> | ||
65 | #include <asm/byteorder.h> | 68 | #include <asm/byteorder.h> |
66 | #include <asm/io.h> | 69 | #include <asm/io.h> |
67 | #include <asm/processor.h> | 70 | #include <asm/processor.h> |
@@ -89,6 +92,8 @@ MODULE_LICENSE("Dual BSD/GPL"); | |||
89 | 92 | ||
90 | #define MYRI10GE_EEPROM_STRINGS_SIZE 256 | 93 | #define MYRI10GE_EEPROM_STRINGS_SIZE 256 |
91 | #define MYRI10GE_MAX_SEND_DESC_TSO ((65536 / 2048) * 2) | 94 | #define MYRI10GE_MAX_SEND_DESC_TSO ((65536 / 2048) * 2) |
95 | #define MYRI10GE_MAX_LRO_DESCRIPTORS 8 | ||
96 | #define MYRI10GE_LRO_MAX_PKTS 64 | ||
92 | 97 | ||
93 | #define MYRI10GE_NO_CONFIRM_DATA htonl(0xffffffff) | 98 | #define MYRI10GE_NO_CONFIRM_DATA htonl(0xffffffff) |
94 | #define MYRI10GE_NO_RESPONSE_RESULT 0xffffffff | 99 | #define MYRI10GE_NO_RESPONSE_RESULT 0xffffffff |
@@ -151,6 +156,8 @@ struct myri10ge_rx_done { | |||
151 | dma_addr_t bus; | 156 | dma_addr_t bus; |
152 | int cnt; | 157 | int cnt; |
153 | int idx; | 158 | int idx; |
159 | struct net_lro_mgr lro_mgr; | ||
160 | struct net_lro_desc lro_desc[MYRI10GE_MAX_LRO_DESCRIPTORS]; | ||
154 | }; | 161 | }; |
155 | 162 | ||
156 | struct myri10ge_priv { | 163 | struct myri10ge_priv { |
@@ -278,6 +285,14 @@ static int myri10ge_debug = -1; /* defaults above */ | |||
278 | module_param(myri10ge_debug, int, 0); | 285 | module_param(myri10ge_debug, int, 0); |
279 | MODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)"); | 286 | MODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)"); |
280 | 287 | ||
288 | static int myri10ge_lro = 1; | ||
289 | module_param(myri10ge_lro, int, S_IRUGO); | ||
290 | MODULE_PARM_DESC(myri10ge_lro, "Enable large receive offload\n"); | ||
291 | |||
292 | static int myri10ge_lro_max_pkts = MYRI10GE_LRO_MAX_PKTS; | ||
293 | module_param(myri10ge_lro_max_pkts, int, S_IRUGO); | ||
294 | MODULE_PARM_DESC(myri10ge_lro, "Number of LRO packets to be aggregated\n"); | ||
295 | |||
281 | static int myri10ge_fill_thresh = 256; | 296 | static int myri10ge_fill_thresh = 256; |
282 | module_param(myri10ge_fill_thresh, int, S_IRUGO | S_IWUSR); | 297 | module_param(myri10ge_fill_thresh, int, S_IRUGO | S_IWUSR); |
283 | MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed\n"); | 298 | MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed\n"); |
@@ -1021,6 +1036,15 @@ myri10ge_rx_done(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx, | |||
1021 | remainder -= MYRI10GE_ALLOC_SIZE; | 1036 | remainder -= MYRI10GE_ALLOC_SIZE; |
1022 | } | 1037 | } |
1023 | 1038 | ||
1039 | if (mgp->csum_flag && myri10ge_lro) { | ||
1040 | rx_frags[0].page_offset += MXGEFW_PAD; | ||
1041 | rx_frags[0].size -= MXGEFW_PAD; | ||
1042 | len -= MXGEFW_PAD; | ||
1043 | lro_receive_frags(&mgp->rx_done.lro_mgr, rx_frags, | ||
1044 | len, len, (void *)(unsigned long)csum, csum); | ||
1045 | return 1; | ||
1046 | } | ||
1047 | |||
1024 | hlen = MYRI10GE_HLEN > len ? len : MYRI10GE_HLEN; | 1048 | hlen = MYRI10GE_HLEN > len ? len : MYRI10GE_HLEN; |
1025 | 1049 | ||
1026 | /* allocate an skb to attach the page(s) to. */ | 1050 | /* allocate an skb to attach the page(s) to. */ |
@@ -1136,6 +1160,9 @@ static inline int myri10ge_clean_rx_done(struct myri10ge_priv *mgp, int budget) | |||
1136 | mgp->stats.rx_packets += rx_packets; | 1160 | mgp->stats.rx_packets += rx_packets; |
1137 | mgp->stats.rx_bytes += rx_bytes; | 1161 | mgp->stats.rx_bytes += rx_bytes; |
1138 | 1162 | ||
1163 | if (myri10ge_lro) | ||
1164 | lro_flush_all(&rx_done->lro_mgr); | ||
1165 | |||
1139 | /* restock receive rings if needed */ | 1166 | /* restock receive rings if needed */ |
1140 | if (mgp->rx_small.fill_cnt - mgp->rx_small.cnt < myri10ge_fill_thresh) | 1167 | if (mgp->rx_small.fill_cnt - mgp->rx_small.cnt < myri10ge_fill_thresh) |
1141 | myri10ge_alloc_rx_pages(mgp, &mgp->rx_small, | 1168 | myri10ge_alloc_rx_pages(mgp, &mgp->rx_small, |
@@ -1373,7 +1400,8 @@ static const char myri10ge_gstrings_stats[][ETH_GSTRING_LEN] = { | |||
1373 | "dropped_pause", "dropped_bad_phy", "dropped_bad_crc32", | 1400 | "dropped_pause", "dropped_bad_phy", "dropped_bad_crc32", |
1374 | "dropped_unicast_filtered", "dropped_multicast_filtered", | 1401 | "dropped_unicast_filtered", "dropped_multicast_filtered", |
1375 | "dropped_runt", "dropped_overrun", "dropped_no_small_buffer", | 1402 | "dropped_runt", "dropped_overrun", "dropped_no_small_buffer", |
1376 | "dropped_no_big_buffer" | 1403 | "dropped_no_big_buffer", "LRO aggregated", "LRO flushed", |
1404 | "LRO avg aggr", "LRO no_desc" | ||
1377 | }; | 1405 | }; |
1378 | 1406 | ||
1379 | #define MYRI10GE_NET_STATS_LEN 21 | 1407 | #define MYRI10GE_NET_STATS_LEN 21 |
@@ -1439,6 +1467,14 @@ myri10ge_get_ethtool_stats(struct net_device *netdev, | |||
1439 | data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_overrun); | 1467 | data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_overrun); |
1440 | data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_no_small_buffer); | 1468 | data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_no_small_buffer); |
1441 | data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_no_big_buffer); | 1469 | data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_no_big_buffer); |
1470 | data[i++] = mgp->rx_done.lro_mgr.stats.aggregated; | ||
1471 | data[i++] = mgp->rx_done.lro_mgr.stats.flushed; | ||
1472 | if (mgp->rx_done.lro_mgr.stats.flushed) | ||
1473 | data[i++] = mgp->rx_done.lro_mgr.stats.aggregated / | ||
1474 | mgp->rx_done.lro_mgr.stats.flushed; | ||
1475 | else | ||
1476 | data[i++] = 0; | ||
1477 | data[i++] = mgp->rx_done.lro_mgr.stats.no_desc; | ||
1442 | } | 1478 | } |
1443 | 1479 | ||
1444 | static void myri10ge_set_msglevel(struct net_device *netdev, u32 value) | 1480 | static void myri10ge_set_msglevel(struct net_device *netdev, u32 value) |
@@ -1712,10 +1748,69 @@ static void myri10ge_free_irq(struct myri10ge_priv *mgp) | |||
1712 | pci_disable_msi(pdev); | 1748 | pci_disable_msi(pdev); |
1713 | } | 1749 | } |
1714 | 1750 | ||
1751 | static int | ||
1752 | myri10ge_get_frag_header(struct skb_frag_struct *frag, void **mac_hdr, | ||
1753 | void **ip_hdr, void **tcpudp_hdr, | ||
1754 | u64 * hdr_flags, void *priv) | ||
1755 | { | ||
1756 | struct ethhdr *eh; | ||
1757 | struct vlan_ethhdr *veh; | ||
1758 | struct iphdr *iph; | ||
1759 | u8 *va = page_address(frag->page) + frag->page_offset; | ||
1760 | unsigned long ll_hlen; | ||
1761 | __wsum csum = (__wsum) (unsigned long)priv; | ||
1762 | |||
1763 | /* find the mac header, aborting if not IPv4 */ | ||
1764 | |||
1765 | eh = (struct ethhdr *)va; | ||
1766 | *mac_hdr = eh; | ||
1767 | ll_hlen = ETH_HLEN; | ||
1768 | if (eh->h_proto != htons(ETH_P_IP)) { | ||
1769 | if (eh->h_proto == htons(ETH_P_8021Q)) { | ||
1770 | veh = (struct vlan_ethhdr *)va; | ||
1771 | if (veh->h_vlan_encapsulated_proto != htons(ETH_P_IP)) | ||
1772 | return -1; | ||
1773 | |||
1774 | ll_hlen += VLAN_HLEN; | ||
1775 | |||
1776 | /* | ||
1777 | * HW checksum starts ETH_HLEN bytes into | ||
1778 | * frame, so we must subtract off the VLAN | ||
1779 | * header's checksum before csum can be used | ||
1780 | */ | ||
1781 | csum = csum_sub(csum, csum_partial(va + ETH_HLEN, | ||
1782 | VLAN_HLEN, 0)); | ||
1783 | } else { | ||
1784 | return -1; | ||
1785 | } | ||
1786 | } | ||
1787 | *hdr_flags = LRO_IPV4; | ||
1788 | |||
1789 | iph = (struct iphdr *)(va + ll_hlen); | ||
1790 | *ip_hdr = iph; | ||
1791 | if (iph->protocol != IPPROTO_TCP) | ||
1792 | return -1; | ||
1793 | *hdr_flags |= LRO_TCP; | ||
1794 | *tcpudp_hdr = (u8 *) (*ip_hdr) + (iph->ihl << 2); | ||
1795 | |||
1796 | /* verify the IP checksum */ | ||
1797 | if (unlikely(ip_fast_csum((u8 *) iph, iph->ihl))) | ||
1798 | return -1; | ||
1799 | |||
1800 | /* verify the checksum */ | ||
1801 | if (unlikely(csum_tcpudp_magic(iph->saddr, iph->daddr, | ||
1802 | ntohs(iph->tot_len) - (iph->ihl << 2), | ||
1803 | IPPROTO_TCP, csum))) | ||
1804 | return -1; | ||
1805 | |||
1806 | return 0; | ||
1807 | } | ||
1808 | |||
1715 | static int myri10ge_open(struct net_device *dev) | 1809 | static int myri10ge_open(struct net_device *dev) |
1716 | { | 1810 | { |
1717 | struct myri10ge_priv *mgp; | 1811 | struct myri10ge_priv *mgp; |
1718 | struct myri10ge_cmd cmd; | 1812 | struct myri10ge_cmd cmd; |
1813 | struct net_lro_mgr *lro_mgr; | ||
1719 | int status, big_pow2; | 1814 | int status, big_pow2; |
1720 | 1815 | ||
1721 | mgp = netdev_priv(dev); | 1816 | mgp = netdev_priv(dev); |
@@ -1847,6 +1942,18 @@ static int myri10ge_open(struct net_device *dev) | |||
1847 | mgp->link_state = htonl(~0U); | 1942 | mgp->link_state = htonl(~0U); |
1848 | mgp->rdma_tags_available = 15; | 1943 | mgp->rdma_tags_available = 15; |
1849 | 1944 | ||
1945 | lro_mgr = &mgp->rx_done.lro_mgr; | ||
1946 | lro_mgr->dev = dev; | ||
1947 | lro_mgr->features = LRO_F_NAPI; | ||
1948 | lro_mgr->ip_summed = CHECKSUM_COMPLETE; | ||
1949 | lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY; | ||
1950 | lro_mgr->max_desc = MYRI10GE_MAX_LRO_DESCRIPTORS; | ||
1951 | lro_mgr->lro_arr = mgp->rx_done.lro_desc; | ||
1952 | lro_mgr->get_frag_header = myri10ge_get_frag_header; | ||
1953 | lro_mgr->max_aggr = myri10ge_lro_max_pkts; | ||
1954 | if (lro_mgr->max_aggr > MAX_SKB_FRAGS) | ||
1955 | lro_mgr->max_aggr = MAX_SKB_FRAGS; | ||
1956 | |||
1850 | napi_enable(&mgp->napi); /* must happen prior to any irq */ | 1957 | napi_enable(&mgp->napi); /* must happen prior to any irq */ |
1851 | 1958 | ||
1852 | status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_UP, &cmd, 0); | 1959 | status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_UP, &cmd, 0); |