提交 47fd23fe 编写于 作者: R Roland Dreier 提交者: David S. Miller

cxgb3: Keep LRO off if disabled when interface is down

I have a system with a Chelsio adapter (driven by cxgb3) whose ports are
part of a Linux bridge.  Recently I updated the kernel and discovered
that things stopped working because cxgb3 was doing LRO on packets that
were passed into the bridge code for forwarding.  (Incidentally, this
problem manifested itself in a strange way that made debugging a bit
interesting -- for some reason, the skb_warn_if_lro() check in bridge
didn't trigger and these LROed packets were forwarded out a forcedeth
interface, and caused the forcedeth transmit path to get stuck)

This is because cxgb3 has no way of keeping state for the LRO flag until
the interface is brought up, so if the bridging code disables LRO while
the interface is down, then cxgb3_up() will just reenable LRO, and on my
Debian system at least, the init scripts add interfaces to a bridge
before bringing the interfaces up.

Fix this by keeping track of each interface's LRO state in cxgb3 so that
when bridge disables LRO, it stays disabled in cxgb3_up() when the
interface is brought up.  I did this by changing the rx_csum_offload
flag into a pair of bit flags; the effect of this on the rx_eth() fast
path is miniscule enough that it should be fine (eg on x86, a cmpb
instruction becomes a testb instruction).
Signed-off-by: NRoland Dreier <rolandd@cisco.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 2a7e637d
...@@ -50,12 +50,17 @@ struct vlan_group; ...@@ -50,12 +50,17 @@ struct vlan_group;
struct adapter; struct adapter;
struct sge_qset; struct sge_qset;
enum { /* rx_offload flags */
T3_RX_CSUM = 1 << 0,
T3_LRO = 1 << 1,
};
struct port_info { struct port_info {
struct adapter *adapter; struct adapter *adapter;
struct vlan_group *vlan_grp; struct vlan_group *vlan_grp;
struct sge_qset *qs; struct sge_qset *qs;
u8 port_id; u8 port_id;
u8 rx_csum_offload; u8 rx_offload;
u8 nqsets; u8 nqsets;
u8 first_qset; u8 first_qset;
struct cphy phy; struct cphy phy;
......
...@@ -546,7 +546,7 @@ static int setup_sge_qsets(struct adapter *adap) ...@@ -546,7 +546,7 @@ static int setup_sge_qsets(struct adapter *adap)
pi->qs = &adap->sge.qs[pi->first_qset]; pi->qs = &adap->sge.qs[pi->first_qset];
for (j = pi->first_qset; j < pi->first_qset + pi->nqsets; for (j = pi->first_qset; j < pi->first_qset + pi->nqsets;
++j, ++qset_idx) { ++j, ++qset_idx) {
set_qset_lro(dev, qset_idx, pi->rx_csum_offload); set_qset_lro(dev, qset_idx, pi->rx_offload & T3_LRO);
err = t3_sge_alloc_qset(adap, qset_idx, 1, err = t3_sge_alloc_qset(adap, qset_idx, 1,
(adap->flags & USING_MSIX) ? qset_idx + 1 : (adap->flags & USING_MSIX) ? qset_idx + 1 :
irq_idx, irq_idx,
...@@ -1657,17 +1657,19 @@ static u32 get_rx_csum(struct net_device *dev) ...@@ -1657,17 +1657,19 @@ static u32 get_rx_csum(struct net_device *dev)
{ {
struct port_info *p = netdev_priv(dev); struct port_info *p = netdev_priv(dev);
return p->rx_csum_offload; return p->rx_offload & T3_RX_CSUM;
} }
static int set_rx_csum(struct net_device *dev, u32 data) static int set_rx_csum(struct net_device *dev, u32 data)
{ {
struct port_info *p = netdev_priv(dev); struct port_info *p = netdev_priv(dev);
p->rx_csum_offload = data; if (data) {
if (!data) { p->rx_offload |= T3_RX_CSUM;
} else {
int i; int i;
p->rx_offload &= ~(T3_RX_CSUM | T3_LRO);
for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) for (i = p->first_qset; i < p->first_qset + p->nqsets; i++)
set_qset_lro(dev, i, 0); set_qset_lro(dev, i, 0);
} }
...@@ -1830,15 +1832,18 @@ static int cxgb3_set_flags(struct net_device *dev, u32 data) ...@@ -1830,15 +1832,18 @@ static int cxgb3_set_flags(struct net_device *dev, u32 data)
int i; int i;
if (data & ETH_FLAG_LRO) { if (data & ETH_FLAG_LRO) {
if (!pi->rx_csum_offload) if (!(pi->rx_offload & T3_RX_CSUM))
return -EINVAL; return -EINVAL;
pi->rx_offload |= T3_LRO;
for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++) for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++)
set_qset_lro(dev, i, 1); set_qset_lro(dev, i, 1);
} else } else {
pi->rx_offload &= ~T3_LRO;
for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++) for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++)
set_qset_lro(dev, i, 0); set_qset_lro(dev, i, 0);
}
return 0; return 0;
} }
...@@ -1926,7 +1931,7 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) ...@@ -1926,7 +1931,7 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr)
pi = adap2pinfo(adapter, i); pi = adap2pinfo(adapter, i);
if (t.qset_idx >= pi->first_qset && if (t.qset_idx >= pi->first_qset &&
t.qset_idx < pi->first_qset + pi->nqsets && t.qset_idx < pi->first_qset + pi->nqsets &&
!pi->rx_csum_offload) !(pi->rx_offload & T3_RX_CSUM))
return -EINVAL; return -EINVAL;
} }
...@@ -2946,7 +2951,7 @@ static int __devinit init_one(struct pci_dev *pdev, ...@@ -2946,7 +2951,7 @@ static int __devinit init_one(struct pci_dev *pdev,
adapter->port[i] = netdev; adapter->port[i] = netdev;
pi = netdev_priv(netdev); pi = netdev_priv(netdev);
pi->adapter = adapter; pi->adapter = adapter;
pi->rx_csum_offload = 1; pi->rx_offload = T3_RX_CSUM | T3_LRO;
pi->port_id = i; pi->port_id = i;
netif_carrier_off(netdev); netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev); netif_tx_stop_all_queues(netdev);
...@@ -2955,6 +2960,7 @@ static int __devinit init_one(struct pci_dev *pdev, ...@@ -2955,6 +2960,7 @@ static int __devinit init_one(struct pci_dev *pdev,
netdev->mem_end = mmio_start + mmio_len - 1; netdev->mem_end = mmio_start + mmio_len - 1;
netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
netdev->features |= NETIF_F_LLTX; netdev->features |= NETIF_F_LLTX;
netdev->features |= NETIF_F_LRO;
if (pci_using_dac) if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA; netdev->features |= NETIF_F_HIGHDMA;
......
...@@ -1932,7 +1932,7 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq, ...@@ -1932,7 +1932,7 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
skb_pull(skb, sizeof(*p) + pad); skb_pull(skb, sizeof(*p) + pad);
skb->protocol = eth_type_trans(skb, adap->port[p->iff]); skb->protocol = eth_type_trans(skb, adap->port[p->iff]);
pi = netdev_priv(skb->dev); pi = netdev_priv(skb->dev);
if (pi->rx_csum_offload && p->csum_valid && p->csum == htons(0xffff) && if ((pi->rx_offload & T3_RX_CSUM) && p->csum_valid && p->csum == htons(0xffff) &&
!p->fragment) { !p->fragment) {
qs->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++; qs->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++;
skb->ip_summed = CHECKSUM_UNNECESSARY; skb->ip_summed = CHECKSUM_UNNECESSARY;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册