From: Herbert Xu <herbert@gondor.apana.org.au> Date: Thu, 28 May 2009 18:12:24 +1000 Subject: [net] cxgb3: add GRO suppport Message-id: E1M9aiu-0007Ri-BE@gondolin.me.apana.org.au O-Subject: [PATCH 1/3] cxgb3: Add GRO suppport Bugzilla: 499347 RH-Acked-by: David Miller <davem@redhat.com> RH-Acked-by: Thomas Graf <tgraf@redhat.com> RH-Acked-by: Mike Christie <mchristi@redhat.com> RHEL5 bugzilla 499347 cxgb3: Add GRO suppport Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 33dd9b5..468c7f1 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2380,7 +2380,6 @@ config CHELSIO_T1 config CHELSIO_T3 tristate "Chelsio Communications T3 10Gb Ethernet support" depends on PCI - select INET_LRO ---help--- This driver supports Chelsio T3-based gigabit and 10Gb Ethernet adapters. diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h index 277c5f9..01e497a 100644 --- a/drivers/net/cxgb3/adapter.h +++ b/drivers/net/cxgb3/adapter.h @@ -42,7 +42,6 @@ #include <linux/cache.h> #include <linux/mutex.h> #include <linux/bitops.h> -#include <linux/inet_lro.h> #include "t3cdev.h" #include <asm/io.h> @@ -182,27 +181,20 @@ enum { /* per port SGE statistics */ SGE_PSTAT_TX_CSUM, /* # of TX checksum offloads */ SGE_PSTAT_VLANEX, /* # of VLAN tag extractions */ SGE_PSTAT_VLANINS, /* # of VLAN tag insertions */ - SGE_PSTAT_LRO_AGGR, /* # of page chunks added to LRO sessions */ - SGE_PSTAT_LRO_FLUSHED, /* # of flushed LRO sessions */ - SGE_PSTAT_LRO_NO_DESC, /* # of overflown LRO sessions */ SGE_PSTAT_MAX /* must be last */ }; -#define T3_MAX_LRO_SES 8 -#define T3_MAX_LRO_MAX_PKTS 64 +struct napi_gro_fraginfo; struct sge_qset { /* an SGE queue set */ struct adapter *adap; + struct napi_struct napi; struct sge_rspq rspq; struct sge_fl fl[SGE_RXQ_PER_SET]; struct sge_txq txq[SGE_TXQ_PER_SET]; - struct net_lro_mgr lro_mgr; - struct net_lro_desc lro_desc[T3_MAX_LRO_SES]; - struct skb_frag_struct *lro_frag_tbl; - int lro_nfrags; + int nomem; int lro_enabled; - int lro_frag_len; void *lro_va; struct net_device *netdev; /* associated net device */ unsigned long txq_stopped; /* which Tx queues are stopped */ diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index d93a563..6021ef6 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -610,8 +610,11 @@ static int setup_sge_qsets(struct adapter *adap) for (j = pi->first_qset; j < pi->first_qset + pi->nqsets; ++j, ++qset_idx) { - if (!(pi->rx_offload & T3_LRO)) - adap->params.sge.qset[qset_idx].lro = 0; + adap->sge.qs[qset_idx].napi.dev = dev; + adap->params.sge.qset[qset_idx].lro = + !!(pi->rx_offload & T3_LRO); + adap->sge.qs[qset_idx].lro_enabled = + !!(pi->rx_offload & T3_LRO); err = t3_sge_alloc_qset(adap, qset_idx, 1, (adap->flags & USING_MSIX) ? qset_idx + 1 : irq_idx, @@ -1522,9 +1525,9 @@ static void get_stats(struct net_device *dev, struct ethtool_stats *stats, *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANINS); *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TX_CSUM); *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_RX_CSUM_GOOD); - *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_LRO_AGGR); - *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_LRO_FLUSHED); - *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_LRO_NO_DESC); + *data++ = 0; + *data++ = 0; + *data++ = 0; *data++ = s->rx_cong_drops; *data++ = s->num_toggled; @@ -3131,7 +3134,7 @@ static int __devinit init_one(struct pci_dev *pdev, netdev->mem_end = mmio_start + mmio_len - 1; netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; netdev->features |= NETIF_F_LLTX; - netdev->features |= NETIF_F_LRO; + netdev->features |= NETIF_F_GRO; if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 0fb6351..f7abd2a 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -609,8 +609,8 @@ static void t3_reset_qset(struct sge_qset *q) q->txq_stopped = 0; q->tx_reclaim_timer.function = NULL; /* for t3_stop_sge_timers() */ q->rx_reclaim_timer.function = NULL; - kfree(q->lro_frag_tbl); - q->lro_nfrags = q->lro_frag_len = 0; + q->nomem = 0; + napi_free_frags(&q->napi); } @@ -1977,10 +1977,8 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq, qs->port_stats[SGE_PSTAT_VLANEX]++; if (likely(grp)) if (lro) - lro_vlan_hwaccel_receive_skb(&qs->lro_mgr, skb, - grp, - ntohs(p->vlan), - p); + vlan_gro_receive(&qs->napi, grp, + ntohs(p->vlan), skb); else { if (unlikely(pi->iscsi_ipv4addr && is_arp(skb))) { @@ -1997,7 +1995,7 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq, dev_kfree_skb_any(skb); } else if (rq->polling) { if (lro) - lro_receive_skb(&qs->lro_mgr, skb, p); + napi_gro_receive(&qs->napi, skb); else { if (unlikely(pi->iscsi_ipv4addr && is_arp(skb))) cxgb3_arp_process(adap, skb); @@ -2013,59 +2011,6 @@ static inline int is_eth_tcp(u32 rss) } /** - * lro_frame_ok - check if an ingress packet is eligible for LRO - * @p: the CPL header of the packet - * - * Returns true if a received packet is eligible for LRO. - * The following conditions must be true: - * - packet is TCP/IP Ethernet II (checked elsewhere) - * - not an IP fragment - * - no IP options - * - TCP/IP checksums are correct - * - the packet is for this host - */ -static inline int lro_frame_ok(const struct cpl_rx_pkt *p) -{ - const struct ethhdr *eh = (struct ethhdr *)(p + 1); - const struct iphdr *ih = (struct iphdr *)(eh + 1); - - return (*((u8 *)p + 1) & 0x90) == 0x10 && p->csum == htons(0xffff) && - eh->h_proto == htons(ETH_P_IP) && ih->ihl == (sizeof(*ih) >> 2); -} - -static int t3_get_lro_header(void **eh, void **iph, void **tcph, - u64 *hdr_flags, void *priv) -{ - const struct cpl_rx_pkt *cpl = priv; - - if (!lro_frame_ok(cpl)) - return -1; - - *eh = (struct ethhdr *)(cpl + 1); - *iph = (struct iphdr *)((struct ethhdr *)*eh + 1); - *tcph = (struct tcphdr *)((struct iphdr *)*iph + 1); - - *hdr_flags = LRO_IPV4 | LRO_TCP; - return 0; -} - -static int t3_get_skb_header(struct sk_buff *skb, - void **iph, void **tcph, u64 *hdr_flags, - void *priv) -{ - void *eh; - - return t3_get_lro_header(&eh, iph, tcph, hdr_flags, priv); -} - -static int t3_get_frag_header(struct skb_frag_struct *frag, void **eh, - void **iph, void **tcph, u64 *hdr_flags, - void *priv) -{ - return t3_get_lro_header(eh, iph, tcph, hdr_flags, priv); -} - -/** * lro_add_page - add a page chunk to an LRO session * @adap: the adapter * @qs: the associated queue set @@ -2080,34 +2025,54 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs, struct sge_fl *fl, int len, int complete) { struct rx_sw_desc *sd = &fl->sdesc[fl->cidx]; + struct sk_buff *skb = NULL; struct cpl_rx_pkt *cpl; - struct skb_frag_struct *rx_frag = qs->lro_frag_tbl; - int nr_frags = qs->lro_nfrags, frag_len = qs->lro_frag_len; + struct skb_frag_struct *rx_frag; + int nr_frags; int offset = 0; - if (!nr_frags) { - offset = 2 + sizeof(struct cpl_rx_pkt); - qs->lro_va = cpl = sd->pg_chunk.va + 2; + if (!qs->nomem) { + skb = napi_get_frags(&qs->napi); + qs->nomem = !skb; } fl->credits--; - len -= offset; pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr), fl->buf_size, PCI_DMA_FROMDEVICE); + if (!skb) { + put_page(sd->pg_chunk.page); + if (complete) + qs->nomem = 0; + return; + } + + rx_frag = skb_shinfo(skb)->frags; + nr_frags = skb_shinfo(skb)->nr_frags; + + if (!nr_frags) { + offset = 2 + sizeof(struct cpl_rx_pkt); + qs->lro_va = sd->pg_chunk.va + 2; + } + len -= offset; + + prefetch(qs->lro_va); + rx_frag += nr_frags; rx_frag->page = sd->pg_chunk.page; rx_frag->page_offset = sd->pg_chunk.offset + offset; rx_frag->size = len; - frag_len += len; - qs->lro_nfrags++; - qs->lro_frag_len = frag_len; + + skb->len += len; + skb->data_len += len; + skb->truesize += len; + skb_shinfo(skb)->nr_frags++; if (!complete) return; - qs->lro_nfrags = qs->lro_frag_len = 0; + skb->ip_summed = CHECKSUM_UNNECESSARY; cpl = qs->lro_va; if (unlikely(cpl->vlan_valid)) { @@ -2116,40 +2081,11 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs, struct vlan_group *grp = pi->vlan_grp; if (likely(grp != NULL)) { - lro_vlan_hwaccel_receive_frags(&qs->lro_mgr, - qs->lro_frag_tbl, - frag_len, frag_len, - grp, ntohs(cpl->vlan), - cpl, 0); + vlan_gro_frags(&qs->napi, grp, ntohs(cpl->vlan)); return; } } - lro_receive_frags(&qs->lro_mgr, qs->lro_frag_tbl, - frag_len, frag_len, cpl, 0); -} - -/** - * init_lro_mgr - initialize a LRO manager object - * @lro_mgr: the LRO manager object - */ -static void init_lro_mgr(struct sge_qset *qs, struct net_lro_mgr *lro_mgr) -{ - struct adapter *adapter = qs->adap; - struct net_device *dev = qs->netdev; - struct port_info *pi = netdev_priv(dev); - - lro_mgr->dev = adapter->port[pi->port_id]; - lro_mgr->features = LRO_F_NAPI; - lro_mgr->frag_align_pad = NET_IP_ALIGN; - lro_mgr->ip_summed = CHECKSUM_UNNECESSARY; - lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY; - lro_mgr->max_desc = T3_MAX_LRO_SES; - lro_mgr->lro_arr = qs->lro_desc; - lro_mgr->get_frag_header = t3_get_frag_header; - lro_mgr->get_skb_header = t3_get_skb_header; - lro_mgr->max_aggr = T3_MAX_LRO_MAX_PKTS; - if (lro_mgr->max_aggr > MAX_SKB_FRAGS) - lro_mgr->max_aggr = MAX_SKB_FRAGS; + napi_gro_frags(&qs->napi); } /** @@ -2392,10 +2328,7 @@ next_fl: } deliver_partial_bundle(&adap->tdev, q, offload_skbs, ngathered); - lro_flush_all(&qs->lro_mgr); - qs->port_stats[SGE_PSTAT_LRO_AGGR] = qs->lro_mgr.stats.aggregated; - qs->port_stats[SGE_PSTAT_LRO_FLUSHED] = qs->lro_mgr.stats.flushed; - qs->port_stats[SGE_PSTAT_LRO_NO_DESC] = qs->lro_mgr.stats.no_desc; + napi_gro_flush(&qs->napi); if (sleeping) check_ring_db(adap, qs, sleeping); @@ -2992,7 +2925,6 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, { int i, avail, ret = -ENOMEM; struct sge_qset *q = &adapter->sge.qs[id]; - struct net_lro_mgr *lro_mgr = &q->lro_mgr; init_qset_cntxt(q, id); setup_timer(&q->tx_reclaim_timer, sge_timer_tx, (unsigned long)q); @@ -3072,10 +3004,6 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, q->fl[0].order = FL0_PG_ORDER; q->fl[1].order = FL1_PG_ORDER; - q->lro_frag_tbl = kcalloc(MAX_FRAME_SIZE / FL1_PG_CHUNK_SIZE + 1, - sizeof(struct skb_frag_struct), - GFP_KERNEL); - q->lro_nfrags = q->lro_frag_len = 0; spin_lock_irq(&adapter->sge.reg_lock); /* FL threshold comparison uses < */ @@ -3134,8 +3062,6 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, if (netdev->atalk_ptr == NULL) netdev->atalk_ptr = q; - init_lro_mgr(q, lro_mgr); - avail = refill_fl(adapter, &q->fl[0], q->fl[0].size, GFP_KERNEL | __GFP_COMP); if (!avail) { diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c index b39bdfc..ac0b8ab 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c +++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c @@ -14,6 +14,7 @@ #include <linux/inet.h> #include <linux/err.h> #include <linux/crypto.h> +#include <net/dst.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_eh.h>