diff options
author | Divy Le Ray <divy@chelsio.com> | 2008-05-21 21:56:26 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-05-22 06:34:13 -0400 |
commit | b47385bd4f67481a7dbfcf1b4b82e9a67ecb846c (patch) | |
tree | 7357a330b671dc2526fd7c467f1799cc612914ce /drivers | |
parent | 7385ecf339c504933a98581c2056d83b69b2a82b (diff) |
cxgb3 - Add LRO support
Add LRO support.
Signed-off-by: Divy Le Ray <divy@chelsio.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/cxgb3/adapter.h | 14 | ||||
-rw-r--r-- | drivers/net/cxgb3/common.h | 1 | ||||
-rw-r--r-- | drivers/net/cxgb3/cxgb3_ioctl.h | 1 | ||||
-rw-r--r-- | drivers/net/cxgb3/cxgb3_main.c | 19 | ||||
-rw-r--r-- | drivers/net/cxgb3/sge.c | 233 | ||||
-rw-r--r-- | drivers/net/cxgb3/t3_cpl.h | 11 |
7 files changed, 268 insertions, 12 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9f6cc8a56073..daeb23d7fec1 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig | |||
@@ -2409,6 +2409,7 @@ config CHELSIO_T3 | |||
2409 | tristate "Chelsio Communications T3 10Gb Ethernet support" | 2409 | tristate "Chelsio Communications T3 10Gb Ethernet support" |
2410 | depends on PCI | 2410 | depends on PCI |
2411 | select FW_LOADER | 2411 | select FW_LOADER |
2412 | select INET_LRO | ||
2412 | help | 2413 | help |
2413 | This driver supports Chelsio T3-based gigabit and 10Gb Ethernet | 2414 | This driver supports Chelsio T3-based gigabit and 10Gb Ethernet |
2414 | adapters. | 2415 | adapters. |
diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h index 263e4faf45e5..271140433b09 100644 --- a/drivers/net/cxgb3/adapter.h +++ b/drivers/net/cxgb3/adapter.h | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <linux/cache.h> | 42 | #include <linux/cache.h> |
43 | #include <linux/mutex.h> | 43 | #include <linux/mutex.h> |
44 | #include <linux/bitops.h> | 44 | #include <linux/bitops.h> |
45 | #include <linux/inet_lro.h> | ||
45 | #include "t3cdev.h" | 46 | #include "t3cdev.h" |
46 | #include <asm/io.h> | 47 | #include <asm/io.h> |
47 | 48 | ||
@@ -173,16 +174,29 @@ enum { /* per port SGE statistics */ | |||
173 | SGE_PSTAT_TX_CSUM, /* # of TX checksum offloads */ | 174 | SGE_PSTAT_TX_CSUM, /* # of TX checksum offloads */ |
174 | SGE_PSTAT_VLANEX, /* # of VLAN tag extractions */ | 175 | SGE_PSTAT_VLANEX, /* # of VLAN tag extractions */ |
175 | SGE_PSTAT_VLANINS, /* # of VLAN tag insertions */ | 176 | SGE_PSTAT_VLANINS, /* # of VLAN tag insertions */ |
177 | SGE_PSTAT_LRO_AGGR, /* # of page chunks added to LRO sessions */ | ||
178 | SGE_PSTAT_LRO_FLUSHED, /* # of flushed LRO sessions */ | ||
179 | SGE_PSTAT_LRO_NO_DESC, /* # of overflown LRO sessions */ | ||
176 | 180 | ||
177 | SGE_PSTAT_MAX /* must be last */ | 181 | SGE_PSTAT_MAX /* must be last */ |
178 | }; | 182 | }; |
179 | 183 | ||
184 | #define T3_MAX_LRO_SES 8 | ||
185 | #define T3_MAX_LRO_MAX_PKTS 64 | ||
186 | |||
180 | struct sge_qset { /* an SGE queue set */ | 187 | struct sge_qset { /* an SGE queue set */ |
181 | struct adapter *adap; | 188 | struct adapter *adap; |
182 | struct napi_struct napi; | 189 | struct napi_struct napi; |
183 | struct sge_rspq rspq; | 190 | struct sge_rspq rspq; |
184 | struct sge_fl fl[SGE_RXQ_PER_SET]; | 191 | struct sge_fl fl[SGE_RXQ_PER_SET]; |
185 | struct sge_txq txq[SGE_TXQ_PER_SET]; | 192 | struct sge_txq txq[SGE_TXQ_PER_SET]; |
193 | struct net_lro_mgr lro_mgr; | ||
194 | struct net_lro_desc lro_desc[T3_MAX_LRO_SES]; | ||
195 | struct skb_frag_struct *lro_frag_tbl; | ||
196 | int lro_nfrags; | ||
197 | int lro_enabled; | ||
198 | int lro_frag_len; | ||
199 | void *lro_va; | ||
186 | struct net_device *netdev; | 200 | struct net_device *netdev; |
187 | unsigned long txq_stopped; /* which Tx queues are stopped */ | 201 | unsigned long txq_stopped; /* which Tx queues are stopped */ |
188 | struct timer_list tx_reclaim_timer; /* reclaims TX buffers */ | 202 | struct timer_list tx_reclaim_timer; /* reclaims TX buffers */ |
diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h index 579bee42a5cb..d444f5881f56 100644 --- a/drivers/net/cxgb3/common.h +++ b/drivers/net/cxgb3/common.h | |||
@@ -351,6 +351,7 @@ struct tp_params { | |||
351 | 351 | ||
352 | struct qset_params { /* SGE queue set parameters */ | 352 | struct qset_params { /* SGE queue set parameters */ |
353 | unsigned int polling; /* polling/interrupt service for rspq */ | 353 | unsigned int polling; /* polling/interrupt service for rspq */ |
354 | unsigned int lro; /* large receive offload */ | ||
354 | unsigned int coalesce_usecs; /* irq coalescing timer */ | 355 | unsigned int coalesce_usecs; /* irq coalescing timer */ |
355 | unsigned int rspq_size; /* # of entries in response queue */ | 356 | unsigned int rspq_size; /* # of entries in response queue */ |
356 | unsigned int fl_size; /* # of entries in regular free list */ | 357 | unsigned int fl_size; /* # of entries in regular free list */ |
diff --git a/drivers/net/cxgb3/cxgb3_ioctl.h b/drivers/net/cxgb3/cxgb3_ioctl.h index 0a82fcddf2d8..68200a14065e 100644 --- a/drivers/net/cxgb3/cxgb3_ioctl.h +++ b/drivers/net/cxgb3/cxgb3_ioctl.h | |||
@@ -90,6 +90,7 @@ struct ch_qset_params { | |||
90 | int32_t fl_size[2]; | 90 | int32_t fl_size[2]; |
91 | int32_t intr_lat; | 91 | int32_t intr_lat; |
92 | int32_t polling; | 92 | int32_t polling; |
93 | int32_t lro; | ||
93 | int32_t cong_thres; | 94 | int32_t cong_thres; |
94 | }; | 95 | }; |
95 | 96 | ||
diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 3a3127216791..5447f3e60f07 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c | |||
@@ -1212,6 +1212,9 @@ static char stats_strings[][ETH_GSTRING_LEN] = { | |||
1212 | "VLANinsertions ", | 1212 | "VLANinsertions ", |
1213 | "TxCsumOffload ", | 1213 | "TxCsumOffload ", |
1214 | "RxCsumGood ", | 1214 | "RxCsumGood ", |
1215 | "LroAggregated ", | ||
1216 | "LroFlushed ", | ||
1217 | "LroNoDesc ", | ||
1215 | "RxDrops ", | 1218 | "RxDrops ", |
1216 | 1219 | ||
1217 | "CheckTXEnToggled ", | 1220 | "CheckTXEnToggled ", |
@@ -1340,6 +1343,9 @@ static void get_stats(struct net_device *dev, struct ethtool_stats *stats, | |||
1340 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANINS); | 1343 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANINS); |
1341 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TX_CSUM); | 1344 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TX_CSUM); |
1342 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_RX_CSUM_GOOD); | 1345 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_RX_CSUM_GOOD); |
1346 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_LRO_AGGR); | ||
1347 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_LRO_FLUSHED); | ||
1348 | *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_LRO_NO_DESC); | ||
1343 | *data++ = s->rx_cong_drops; | 1349 | *data++ = s->rx_cong_drops; |
1344 | 1350 | ||
1345 | *data++ = s->num_toggled; | 1351 | *data++ = s->num_toggled; |
@@ -1558,6 +1564,13 @@ static int set_rx_csum(struct net_device *dev, u32 data) | |||
1558 | struct port_info *p = netdev_priv(dev); | 1564 | struct port_info *p = netdev_priv(dev); |
1559 | 1565 | ||
1560 | p->rx_csum_offload = data; | 1566 | p->rx_csum_offload = data; |
1567 | if (!data) { | ||
1568 | struct adapter *adap = p->adapter; | ||
1569 | int i; | ||
1570 | |||
1571 | for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) | ||
1572 | adap->sge.qs[i].lro_enabled = 0; | ||
1573 | } | ||
1561 | return 0; | 1574 | return 0; |
1562 | } | 1575 | } |
1563 | 1576 | ||
@@ -1830,6 +1843,11 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) | |||
1830 | } | 1843 | } |
1831 | } | 1844 | } |
1832 | } | 1845 | } |
1846 | if (t.lro >= 0) { | ||
1847 | struct sge_qset *qs = &adapter->sge.qs[t.qset_idx]; | ||
1848 | q->lro = t.lro; | ||
1849 | qs->lro_enabled = t.lro; | ||
1850 | } | ||
1833 | break; | 1851 | break; |
1834 | } | 1852 | } |
1835 | case CHELSIO_GET_QSET_PARAMS:{ | 1853 | case CHELSIO_GET_QSET_PARAMS:{ |
@@ -1849,6 +1867,7 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) | |||
1849 | t.fl_size[0] = q->fl_size; | 1867 | t.fl_size[0] = q->fl_size; |
1850 | t.fl_size[1] = q->jumbo_size; | 1868 | t.fl_size[1] = q->jumbo_size; |
1851 | t.polling = q->polling; | 1869 | t.polling = q->polling; |
1870 | t.lro = q->lro; | ||
1852 | t.intr_lat = q->coalesce_usecs; | 1871 | t.intr_lat = q->coalesce_usecs; |
1853 | t.cong_thres = q->cong_thres; | 1872 | t.cong_thres = q->cong_thres; |
1854 | 1873 | ||
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 3e91be55e19e..a96331c875e6 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c | |||
@@ -584,6 +584,8 @@ static void t3_reset_qset(struct sge_qset *q) | |||
584 | memset(q->txq, 0, sizeof(struct sge_txq) * SGE_TXQ_PER_SET); | 584 | memset(q->txq, 0, sizeof(struct sge_txq) * SGE_TXQ_PER_SET); |
585 | q->txq_stopped = 0; | 585 | q->txq_stopped = 0; |
586 | memset(&q->tx_reclaim_timer, 0, sizeof(q->tx_reclaim_timer)); | 586 | memset(&q->tx_reclaim_timer, 0, sizeof(q->tx_reclaim_timer)); |
587 | kfree(q->lro_frag_tbl); | ||
588 | q->lro_nfrags = q->lro_frag_len = 0; | ||
587 | } | 589 | } |
588 | 590 | ||
589 | 591 | ||
@@ -796,7 +798,7 @@ recycle: | |||
796 | goto recycle; | 798 | goto recycle; |
797 | 799 | ||
798 | if (!skb) | 800 | if (!skb) |
799 | newskb = alloc_skb(SGE_RX_PULL_LEN, GFP_ATOMIC); | 801 | newskb = alloc_skb(SGE_RX_PULL_LEN, GFP_ATOMIC); |
800 | if (unlikely(!newskb)) { | 802 | if (unlikely(!newskb)) { |
801 | if (!drop_thres) | 803 | if (!drop_thres) |
802 | return NULL; | 804 | return NULL; |
@@ -1868,9 +1870,10 @@ static void restart_tx(struct sge_qset *qs) | |||
1868 | * if it was immediate data in a response. | 1870 | * if it was immediate data in a response. |
1869 | */ | 1871 | */ |
1870 | static void rx_eth(struct adapter *adap, struct sge_rspq *rq, | 1872 | static void rx_eth(struct adapter *adap, struct sge_rspq *rq, |
1871 | struct sk_buff *skb, int pad) | 1873 | struct sk_buff *skb, int pad, int lro) |
1872 | { | 1874 | { |
1873 | struct cpl_rx_pkt *p = (struct cpl_rx_pkt *)(skb->data + pad); | 1875 | struct cpl_rx_pkt *p = (struct cpl_rx_pkt *)(skb->data + pad); |
1876 | struct sge_qset *qs = rspq_to_qset(rq); | ||
1874 | struct port_info *pi; | 1877 | struct port_info *pi; |
1875 | 1878 | ||
1876 | skb_pull(skb, sizeof(*p) + pad); | 1879 | skb_pull(skb, sizeof(*p) + pad); |
@@ -1887,18 +1890,202 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq, | |||
1887 | if (unlikely(p->vlan_valid)) { | 1890 | if (unlikely(p->vlan_valid)) { |
1888 | struct vlan_group *grp = pi->vlan_grp; | 1891 | struct vlan_group *grp = pi->vlan_grp; |
1889 | 1892 | ||
1890 | rspq_to_qset(rq)->port_stats[SGE_PSTAT_VLANEX]++; | 1893 | qs->port_stats[SGE_PSTAT_VLANEX]++; |
1891 | if (likely(grp)) | 1894 | if (likely(grp)) |
1892 | __vlan_hwaccel_rx(skb, grp, ntohs(p->vlan), | 1895 | if (lro) |
1893 | rq->polling); | 1896 | lro_vlan_hwaccel_receive_skb(&qs->lro_mgr, skb, |
1897 | grp, | ||
1898 | ntohs(p->vlan), | ||
1899 | p); | ||
1900 | else | ||
1901 | __vlan_hwaccel_rx(skb, grp, ntohs(p->vlan), | ||
1902 | rq->polling); | ||
1894 | else | 1903 | else |
1895 | dev_kfree_skb_any(skb); | 1904 | dev_kfree_skb_any(skb); |
1896 | } else if (rq->polling) | 1905 | } else if (rq->polling) { |
1897 | netif_receive_skb(skb); | 1906 | if (lro) |
1898 | else | 1907 | lro_receive_skb(&qs->lro_mgr, skb, p); |
1908 | else | ||
1909 | netif_receive_skb(skb); | ||
1910 | } else | ||
1899 | netif_rx(skb); | 1911 | netif_rx(skb); |
1900 | } | 1912 | } |
1901 | 1913 | ||
1914 | static inline int is_eth_tcp(u32 rss) | ||
1915 | { | ||
1916 | return G_HASHTYPE(ntohl(rss)) == RSS_HASH_4_TUPLE; | ||
1917 | } | ||
1918 | |||
1919 | /** | ||
1920 | * lro_frame_ok - check if an ingress packet is eligible for LRO | ||
1921 | * @p: the CPL header of the packet | ||
1922 | * | ||
1923 | * Returns true if a received packet is eligible for LRO. | ||
1924 | * The following conditions must be true: | ||
1925 | * - packet is TCP/IP Ethernet II (checked elsewhere) | ||
1926 | * - not an IP fragment | ||
1927 | * - no IP options | ||
1928 | * - TCP/IP checksums are correct | ||
1929 | * - the packet is for this host | ||
1930 | */ | ||
1931 | static inline int lro_frame_ok(const struct cpl_rx_pkt *p) | ||
1932 | { | ||
1933 | const struct ethhdr *eh = (struct ethhdr *)(p + 1); | ||
1934 | const struct iphdr *ih = (struct iphdr *)(eh + 1); | ||
1935 | |||
1936 | return (*((u8 *)p + 1) & 0x90) == 0x10 && p->csum == htons(0xffff) && | ||
1937 | eh->h_proto == htons(ETH_P_IP) && ih->ihl == (sizeof(*ih) >> 2); | ||
1938 | } | ||
1939 | |||
1940 | #define TCP_FLAG_MASK (TCP_FLAG_CWR | TCP_FLAG_ECE | TCP_FLAG_URG |\ | ||
1941 | TCP_FLAG_ACK | TCP_FLAG_PSH | TCP_FLAG_RST |\ | ||
1942 | TCP_FLAG_SYN | TCP_FLAG_FIN) | ||
1943 | #define TSTAMP_WORD ((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |\ | ||
1944 | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP) | ||
1945 | |||
1946 | /** | ||
1947 | * lro_segment_ok - check if a TCP segment is eligible for LRO | ||
1948 | * @tcph: the TCP header of the packet | ||
1949 | * | ||
1950 | * Returns true if a TCP packet is eligible for LRO. This requires that | ||
1951 | * the packet have only the ACK flag set and no TCP options besides | ||
1952 | * time stamps. | ||
1953 | */ | ||
1954 | static inline int lro_segment_ok(const struct tcphdr *tcph) | ||
1955 | { | ||
1956 | int optlen; | ||
1957 | |||
1958 | if (unlikely((tcp_flag_word(tcph) & TCP_FLAG_MASK) != TCP_FLAG_ACK)) | ||
1959 | return 0; | ||
1960 | |||
1961 | optlen = (tcph->doff << 2) - sizeof(*tcph); | ||
1962 | if (optlen) { | ||
1963 | const u32 *opt = (const u32 *)(tcph + 1); | ||
1964 | |||
1965 | if (optlen != TCPOLEN_TSTAMP_ALIGNED || | ||
1966 | *opt != htonl(TSTAMP_WORD) || !opt[2]) | ||
1967 | return 0; | ||
1968 | } | ||
1969 | return 1; | ||
1970 | } | ||
1971 | |||
1972 | static int t3_get_lro_header(void **eh, void **iph, void **tcph, | ||
1973 | u64 *hdr_flags, void *priv) | ||
1974 | { | ||
1975 | const struct cpl_rx_pkt *cpl = priv; | ||
1976 | |||
1977 | if (!lro_frame_ok(cpl)) | ||
1978 | return -1; | ||
1979 | |||
1980 | *eh = (struct ethhdr *)(cpl + 1); | ||
1981 | *iph = (struct iphdr *)((struct ethhdr *)*eh + 1); | ||
1982 | *tcph = (struct tcphdr *)((struct iphdr *)*iph + 1); | ||
1983 | |||
1984 | if (!lro_segment_ok(*tcph)) | ||
1985 | return -1; | ||
1986 | |||
1987 | *hdr_flags = LRO_IPV4 | LRO_TCP; | ||
1988 | return 0; | ||
1989 | } | ||
1990 | |||
1991 | static int t3_get_skb_header(struct sk_buff *skb, | ||
1992 | void **iph, void **tcph, u64 *hdr_flags, | ||
1993 | void *priv) | ||
1994 | { | ||
1995 | void *eh; | ||
1996 | |||
1997 | return t3_get_lro_header(&eh, iph, tcph, hdr_flags, priv); | ||
1998 | } | ||
1999 | |||
2000 | static int t3_get_frag_header(struct skb_frag_struct *frag, void **eh, | ||
2001 | void **iph, void **tcph, u64 *hdr_flags, | ||
2002 | void *priv) | ||
2003 | { | ||
2004 | return t3_get_lro_header(eh, iph, tcph, hdr_flags, priv); | ||
2005 | } | ||
2006 | |||
2007 | /** | ||
2008 | * lro_add_page - add a page chunk to an LRO session | ||
2009 | * @adap: the adapter | ||
2010 | * @qs: the associated queue set | ||
2011 | * @fl: the free list containing the page chunk to add | ||
2012 | * @len: packet length | ||
2013 | * @complete: Indicates the last fragment of a frame | ||
2014 | * | ||
2015 | * Add a received packet contained in a page chunk to an existing LRO | ||
2016 | * session. | ||
2017 | */ | ||
2018 | static void lro_add_page(struct adapter *adap, struct sge_qset *qs, | ||
2019 | struct sge_fl *fl, int len, int complete) | ||
2020 | { | ||
2021 | struct rx_sw_desc *sd = &fl->sdesc[fl->cidx]; | ||
2022 | struct cpl_rx_pkt *cpl; | ||
2023 | struct skb_frag_struct *rx_frag = qs->lro_frag_tbl; | ||
2024 | int nr_frags = qs->lro_nfrags, frag_len = qs->lro_frag_len; | ||
2025 | int offset = 0; | ||
2026 | |||
2027 | if (!nr_frags) { | ||
2028 | offset = 2 + sizeof(struct cpl_rx_pkt); | ||
2029 | qs->lro_va = cpl = sd->pg_chunk.va + 2; | ||
2030 | } | ||
2031 | |||
2032 | fl->credits--; | ||
2033 | |||
2034 | len -= offset; | ||
2035 | pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr), | ||
2036 | fl->buf_size, PCI_DMA_FROMDEVICE); | ||
2037 | |||
2038 | rx_frag += nr_frags; | ||
2039 | rx_frag->page = sd->pg_chunk.page; | ||
2040 | rx_frag->page_offset = sd->pg_chunk.offset + offset; | ||
2041 | rx_frag->size = len; | ||
2042 | frag_len += len; | ||
2043 | qs->lro_nfrags++; | ||
2044 | qs->lro_frag_len = frag_len; | ||
2045 | |||
2046 | if (!complete) | ||
2047 | return; | ||
2048 | |||
2049 | qs->lro_nfrags = qs->lro_frag_len = 0; | ||
2050 | cpl = qs->lro_va; | ||
2051 | |||
2052 | if (unlikely(cpl->vlan_valid)) { | ||
2053 | struct net_device *dev = qs->netdev; | ||
2054 | struct port_info *pi = netdev_priv(dev); | ||
2055 | struct vlan_group *grp = pi->vlan_grp; | ||
2056 | |||
2057 | if (likely(grp != NULL)) { | ||
2058 | lro_vlan_hwaccel_receive_frags(&qs->lro_mgr, | ||
2059 | qs->lro_frag_tbl, | ||
2060 | frag_len, frag_len, | ||
2061 | grp, ntohs(cpl->vlan), | ||
2062 | cpl, 0); | ||
2063 | return; | ||
2064 | } | ||
2065 | } | ||
2066 | lro_receive_frags(&qs->lro_mgr, qs->lro_frag_tbl, | ||
2067 | frag_len, frag_len, cpl, 0); | ||
2068 | } | ||
2069 | |||
2070 | /** | ||
2071 | * init_lro_mgr - initialize a LRO manager object | ||
2072 | * @lro_mgr: the LRO manager object | ||
2073 | */ | ||
2074 | static void init_lro_mgr(struct sge_qset *qs, struct net_lro_mgr *lro_mgr) | ||
2075 | { | ||
2076 | lro_mgr->dev = qs->netdev; | ||
2077 | lro_mgr->features = LRO_F_NAPI; | ||
2078 | lro_mgr->ip_summed = CHECKSUM_UNNECESSARY; | ||
2079 | lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY; | ||
2080 | lro_mgr->max_desc = T3_MAX_LRO_SES; | ||
2081 | lro_mgr->lro_arr = qs->lro_desc; | ||
2082 | lro_mgr->get_frag_header = t3_get_frag_header; | ||
2083 | lro_mgr->get_skb_header = t3_get_skb_header; | ||
2084 | lro_mgr->max_aggr = T3_MAX_LRO_MAX_PKTS; | ||
2085 | if (lro_mgr->max_aggr > MAX_SKB_FRAGS) | ||
2086 | lro_mgr->max_aggr = MAX_SKB_FRAGS; | ||
2087 | } | ||
2088 | |||
1902 | /** | 2089 | /** |
1903 | * handle_rsp_cntrl_info - handles control information in a response | 2090 | * handle_rsp_cntrl_info - handles control information in a response |
1904 | * @qs: the queue set corresponding to the response | 2091 | * @qs: the queue set corresponding to the response |
@@ -2027,7 +2214,7 @@ static int process_responses(struct adapter *adap, struct sge_qset *qs, | |||
2027 | q->next_holdoff = q->holdoff_tmr; | 2214 | q->next_holdoff = q->holdoff_tmr; |
2028 | 2215 | ||
2029 | while (likely(budget_left && is_new_response(r, q))) { | 2216 | while (likely(budget_left && is_new_response(r, q))) { |
2030 | int packet_complete, eth, ethpad = 2; | 2217 | int packet_complete, eth, ethpad = 2, lro = qs->lro_enabled; |
2031 | struct sk_buff *skb = NULL; | 2218 | struct sk_buff *skb = NULL; |
2032 | u32 len, flags = ntohl(r->flags); | 2219 | u32 len, flags = ntohl(r->flags); |
2033 | __be32 rss_hi = *(const __be32 *)r, | 2220 | __be32 rss_hi = *(const __be32 *)r, |
@@ -2059,6 +2246,9 @@ no_mem: | |||
2059 | } else if ((len = ntohl(r->len_cq)) != 0) { | 2246 | } else if ((len = ntohl(r->len_cq)) != 0) { |
2060 | struct sge_fl *fl; | 2247 | struct sge_fl *fl; |
2061 | 2248 | ||
2249 | if (eth) | ||
2250 | lro = qs->lro_enabled && is_eth_tcp(rss_hi); | ||
2251 | |||
2062 | fl = (len & F_RSPD_FLQ) ? &qs->fl[1] : &qs->fl[0]; | 2252 | fl = (len & F_RSPD_FLQ) ? &qs->fl[1] : &qs->fl[0]; |
2063 | if (fl->use_pages) { | 2253 | if (fl->use_pages) { |
2064 | void *addr = fl->sdesc[fl->cidx].pg_chunk.va; | 2254 | void *addr = fl->sdesc[fl->cidx].pg_chunk.va; |
@@ -2068,6 +2258,12 @@ no_mem: | |||
2068 | prefetch(addr + L1_CACHE_BYTES); | 2258 | prefetch(addr + L1_CACHE_BYTES); |
2069 | #endif | 2259 | #endif |
2070 | __refill_fl(adap, fl); | 2260 | __refill_fl(adap, fl); |
2261 | if (lro > 0) { | ||
2262 | lro_add_page(adap, qs, fl, | ||
2263 | G_RSPD_LEN(len), | ||
2264 | flags & F_RSPD_EOP); | ||
2265 | goto next_fl; | ||
2266 | } | ||
2071 | 2267 | ||
2072 | skb = get_packet_pg(adap, fl, q, | 2268 | skb = get_packet_pg(adap, fl, q, |
2073 | G_RSPD_LEN(len), | 2269 | G_RSPD_LEN(len), |
@@ -2083,7 +2279,7 @@ no_mem: | |||
2083 | q->rx_drops++; | 2279 | q->rx_drops++; |
2084 | } else if (unlikely(r->rss_hdr.opcode == CPL_TRACE_PKT)) | 2280 | } else if (unlikely(r->rss_hdr.opcode == CPL_TRACE_PKT)) |
2085 | __skb_pull(skb, 2); | 2281 | __skb_pull(skb, 2); |
2086 | 2282 | next_fl: | |
2087 | if (++fl->cidx == fl->size) | 2283 | if (++fl->cidx == fl->size) |
2088 | fl->cidx = 0; | 2284 | fl->cidx = 0; |
2089 | } else | 2285 | } else |
@@ -2113,7 +2309,7 @@ no_mem: | |||
2113 | 2309 | ||
2114 | if (skb != NULL && packet_complete) { | 2310 | if (skb != NULL && packet_complete) { |
2115 | if (eth) | 2311 | if (eth) |
2116 | rx_eth(adap, q, skb, ethpad); | 2312 | rx_eth(adap, q, skb, ethpad, lro); |
2117 | else { | 2313 | else { |
2118 | q->offload_pkts++; | 2314 | q->offload_pkts++; |
2119 | /* Preserve the RSS info in csum & priority */ | 2315 | /* Preserve the RSS info in csum & priority */ |
@@ -2125,12 +2321,17 @@ no_mem: | |||
2125 | } | 2321 | } |
2126 | 2322 | ||
2127 | if (flags & F_RSPD_EOP) | 2323 | if (flags & F_RSPD_EOP) |
2128 | clear_rspq_bufstate(q); | 2324 | clear_rspq_bufstate(q); |
2129 | } | 2325 | } |
2130 | --budget_left; | 2326 | --budget_left; |
2131 | } | 2327 | } |
2132 | 2328 | ||
2133 | deliver_partial_bundle(&adap->tdev, q, offload_skbs, ngathered); | 2329 | deliver_partial_bundle(&adap->tdev, q, offload_skbs, ngathered); |
2330 | lro_flush_all(&qs->lro_mgr); | ||
2331 | qs->port_stats[SGE_PSTAT_LRO_AGGR] = qs->lro_mgr.stats.aggregated; | ||
2332 | qs->port_stats[SGE_PSTAT_LRO_FLUSHED] = qs->lro_mgr.stats.flushed; | ||
2333 | qs->port_stats[SGE_PSTAT_LRO_NO_DESC] = qs->lro_mgr.stats.no_desc; | ||
2334 | |||
2134 | if (sleeping) | 2335 | if (sleeping) |
2135 | check_ring_db(adap, qs, sleeping); | 2336 | check_ring_db(adap, qs, sleeping); |
2136 | 2337 | ||
@@ -2674,6 +2875,7 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, | |||
2674 | { | 2875 | { |
2675 | int i, avail, ret = -ENOMEM; | 2876 | int i, avail, ret = -ENOMEM; |
2676 | struct sge_qset *q = &adapter->sge.qs[id]; | 2877 | struct sge_qset *q = &adapter->sge.qs[id]; |
2878 | struct net_lro_mgr *lro_mgr = &q->lro_mgr; | ||
2677 | 2879 | ||
2678 | init_qset_cntxt(q, id); | 2880 | init_qset_cntxt(q, id); |
2679 | init_timer(&q->tx_reclaim_timer); | 2881 | init_timer(&q->tx_reclaim_timer); |
@@ -2754,6 +2956,10 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, | |||
2754 | q->fl[0].order = FL0_PG_ORDER; | 2956 | q->fl[0].order = FL0_PG_ORDER; |
2755 | q->fl[1].order = FL1_PG_ORDER; | 2957 | q->fl[1].order = FL1_PG_ORDER; |
2756 | 2958 | ||
2959 | q->lro_frag_tbl = kcalloc(MAX_FRAME_SIZE / FL1_PG_CHUNK_SIZE + 1, | ||
2960 | sizeof(struct skb_frag_struct), | ||
2961 | GFP_KERNEL); | ||
2962 | q->lro_nfrags = q->lro_frag_len = 0; | ||
2757 | spin_lock_irq(&adapter->sge.reg_lock); | 2963 | spin_lock_irq(&adapter->sge.reg_lock); |
2758 | 2964 | ||
2759 | /* FL threshold comparison uses < */ | 2965 | /* FL threshold comparison uses < */ |
@@ -2803,6 +3009,9 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, | |||
2803 | q->adap = adapter; | 3009 | q->adap = adapter; |
2804 | q->netdev = dev; | 3010 | q->netdev = dev; |
2805 | t3_update_qset_coalesce(q, p); | 3011 | t3_update_qset_coalesce(q, p); |
3012 | |||
3013 | init_lro_mgr(q, lro_mgr); | ||
3014 | |||
2806 | avail = refill_fl(adapter, &q->fl[0], q->fl[0].size, | 3015 | avail = refill_fl(adapter, &q->fl[0], q->fl[0].size, |
2807 | GFP_KERNEL | __GFP_COMP); | 3016 | GFP_KERNEL | __GFP_COMP); |
2808 | if (!avail) { | 3017 | if (!avail) { |
diff --git a/drivers/net/cxgb3/t3_cpl.h b/drivers/net/cxgb3/t3_cpl.h index b7a1a310dfd4..a666c5d51cc0 100644 --- a/drivers/net/cxgb3/t3_cpl.h +++ b/drivers/net/cxgb3/t3_cpl.h | |||
@@ -174,6 +174,13 @@ enum { /* TCP congestion control algorithms */ | |||
174 | CONG_ALG_HIGHSPEED | 174 | CONG_ALG_HIGHSPEED |
175 | }; | 175 | }; |
176 | 176 | ||
177 | enum { /* RSS hash type */ | ||
178 | RSS_HASH_NONE = 0, | ||
179 | RSS_HASH_2_TUPLE = 1, | ||
180 | RSS_HASH_4_TUPLE = 2, | ||
181 | RSS_HASH_TCPV6 = 3 | ||
182 | }; | ||
183 | |||
177 | union opcode_tid { | 184 | union opcode_tid { |
178 | __be32 opcode_tid; | 185 | __be32 opcode_tid; |
179 | __u8 opcode; | 186 | __u8 opcode; |
@@ -184,6 +191,10 @@ union opcode_tid { | |||
184 | #define G_OPCODE(x) (((x) >> S_OPCODE) & 0xFF) | 191 | #define G_OPCODE(x) (((x) >> S_OPCODE) & 0xFF) |
185 | #define G_TID(x) ((x) & 0xFFFFFF) | 192 | #define G_TID(x) ((x) & 0xFFFFFF) |
186 | 193 | ||
194 | #define S_HASHTYPE 22 | ||
195 | #define M_HASHTYPE 0x3 | ||
196 | #define G_HASHTYPE(x) (((x) >> S_HASHTYPE) & M_HASHTYPE) | ||
197 | |||
187 | /* tid is assumed to be 24-bits */ | 198 | /* tid is assumed to be 24-bits */ |
188 | #define MK_OPCODE_TID(opcode, tid) (V_OPCODE(opcode) | (tid)) | 199 | #define MK_OPCODE_TID(opcode, tid) (V_OPCODE(opcode) | (tid)) |
189 | 200 | ||