igbvf: Fix checksum error when using stacked vlan

When a skb has multiple vlans and it is CHECKSUM_PARTIAL,
igbvf_tx_csum() fails to get the network protocol and checksum related
descriptor fields are not configured correctly because skb->protocol
doesn't show the L3 protocol in this case.

Use vlan_get_protocol() to get the proper network protocol.

Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Toshiaki Makita 2015-01-29 20:37:08 +09:00 committed by David S. Miller
parent d4bcef3fbe
commit 72b1405964
1 changed files with 11 additions and 8 deletions

View File

@ -1907,7 +1907,8 @@ static void igbvf_watchdog_task(struct work_struct *work)
static int igbvf_tso(struct igbvf_adapter *adapter, static int igbvf_tso(struct igbvf_adapter *adapter,
struct igbvf_ring *tx_ring, struct igbvf_ring *tx_ring,
struct sk_buff *skb, u32 tx_flags, u8 *hdr_len) struct sk_buff *skb, u32 tx_flags, u8 *hdr_len,
__be16 protocol)
{ {
struct e1000_adv_tx_context_desc *context_desc; struct e1000_adv_tx_context_desc *context_desc;
struct igbvf_buffer *buffer_info; struct igbvf_buffer *buffer_info;
@ -1927,7 +1928,7 @@ static int igbvf_tso(struct igbvf_adapter *adapter,
l4len = tcp_hdrlen(skb); l4len = tcp_hdrlen(skb);
*hdr_len += l4len; *hdr_len += l4len;
if (skb->protocol == htons(ETH_P_IP)) { if (protocol == htons(ETH_P_IP)) {
struct iphdr *iph = ip_hdr(skb); struct iphdr *iph = ip_hdr(skb);
iph->tot_len = 0; iph->tot_len = 0;
iph->check = 0; iph->check = 0;
@ -1958,7 +1959,7 @@ static int igbvf_tso(struct igbvf_adapter *adapter,
/* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */ /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT); tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT);
if (skb->protocol == htons(ETH_P_IP)) if (protocol == htons(ETH_P_IP))
tu_cmd |= E1000_ADVTXD_TUCMD_IPV4; tu_cmd |= E1000_ADVTXD_TUCMD_IPV4;
tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP; tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP;
@ -1984,7 +1985,8 @@ static int igbvf_tso(struct igbvf_adapter *adapter,
static inline bool igbvf_tx_csum(struct igbvf_adapter *adapter, static inline bool igbvf_tx_csum(struct igbvf_adapter *adapter,
struct igbvf_ring *tx_ring, struct igbvf_ring *tx_ring,
struct sk_buff *skb, u32 tx_flags) struct sk_buff *skb, u32 tx_flags,
__be16 protocol)
{ {
struct e1000_adv_tx_context_desc *context_desc; struct e1000_adv_tx_context_desc *context_desc;
unsigned int i; unsigned int i;
@ -2011,7 +2013,7 @@ static inline bool igbvf_tx_csum(struct igbvf_adapter *adapter,
tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT); tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT);
if (skb->ip_summed == CHECKSUM_PARTIAL) { if (skb->ip_summed == CHECKSUM_PARTIAL) {
switch (skb->protocol) { switch (protocol) {
case htons(ETH_P_IP): case htons(ETH_P_IP):
tu_cmd |= E1000_ADVTXD_TUCMD_IPV4; tu_cmd |= E1000_ADVTXD_TUCMD_IPV4;
if (ip_hdr(skb)->protocol == IPPROTO_TCP) if (ip_hdr(skb)->protocol == IPPROTO_TCP)
@ -2211,6 +2213,7 @@ static netdev_tx_t igbvf_xmit_frame_ring_adv(struct sk_buff *skb,
u8 hdr_len = 0; u8 hdr_len = 0;
int count = 0; int count = 0;
int tso = 0; int tso = 0;
__be16 protocol = vlan_get_protocol(skb);
if (test_bit(__IGBVF_DOWN, &adapter->state)) { if (test_bit(__IGBVF_DOWN, &adapter->state)) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
@ -2239,13 +2242,13 @@ static netdev_tx_t igbvf_xmit_frame_ring_adv(struct sk_buff *skb,
tx_flags |= (vlan_tx_tag_get(skb) << IGBVF_TX_FLAGS_VLAN_SHIFT); tx_flags |= (vlan_tx_tag_get(skb) << IGBVF_TX_FLAGS_VLAN_SHIFT);
} }
if (skb->protocol == htons(ETH_P_IP)) if (protocol == htons(ETH_P_IP))
tx_flags |= IGBVF_TX_FLAGS_IPV4; tx_flags |= IGBVF_TX_FLAGS_IPV4;
first = tx_ring->next_to_use; first = tx_ring->next_to_use;
tso = skb_is_gso(skb) ? tso = skb_is_gso(skb) ?
igbvf_tso(adapter, tx_ring, skb, tx_flags, &hdr_len) : 0; igbvf_tso(adapter, tx_ring, skb, tx_flags, &hdr_len, protocol) : 0;
if (unlikely(tso < 0)) { if (unlikely(tso < 0)) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
return NETDEV_TX_OK; return NETDEV_TX_OK;
@ -2253,7 +2256,7 @@ static netdev_tx_t igbvf_xmit_frame_ring_adv(struct sk_buff *skb,
if (tso) if (tso)
tx_flags |= IGBVF_TX_FLAGS_TSO; tx_flags |= IGBVF_TX_FLAGS_TSO;
else if (igbvf_tx_csum(adapter, tx_ring, skb, tx_flags) && else if (igbvf_tx_csum(adapter, tx_ring, skb, tx_flags, protocol) &&
(skb->ip_summed == CHECKSUM_PARTIAL)) (skb->ip_summed == CHECKSUM_PARTIAL))
tx_flags |= IGBVF_TX_FLAGS_CSUM; tx_flags |= IGBVF_TX_FLAGS_CSUM;