Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'mv643xx-fixes'

Philipp Kirchhofer says:

====================
net: mv643xx_eth: TSO TX data corruption fixes

as previously discussed [1] the mv643xx_eth driver has some
issues with data corruption when using TCP segmentation offload (TSO).

The following patch set improves this situation by fixing two data
corruption bugs in the TSO TX path.

Before applying the patches repeatedly accessing large files located on
a SMB share on my NSA325 NAS with TSO enabled resulted in different
hash sums, which confirmed that data corruption is happening during
file transfer. After applying the patches the hash sums were the same.

As this is my first patch submission please feel free to point out any
issues with the patch set.

[1] http://thread.gmane.org/gmane.linux.network/336530
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+40 -8
+40 -8
drivers/net/ethernet/marvell/mv643xx_eth.c
··· 759 759 760 760 desc->l4i_chk = 0; 761 761 desc->byte_cnt = length; 762 - desc->buf_ptr = dma_map_single(dev->dev.parent, data, 763 - length, DMA_TO_DEVICE); 764 - if (unlikely(dma_mapping_error(dev->dev.parent, desc->buf_ptr))) { 765 - WARN(1, "dma_map_single failed!\n"); 766 - return -ENOMEM; 762 + 763 + if (length <= 8 && (uintptr_t)data & 0x7) { 764 + /* Copy unaligned small data fragment to TSO header data area */ 765 + memcpy(txq->tso_hdrs + txq->tx_curr_desc * TSO_HEADER_SIZE, 766 + data, length); 767 + desc->buf_ptr = txq->tso_hdrs_dma 768 + + txq->tx_curr_desc * TSO_HEADER_SIZE; 769 + } else { 770 + /* Alignment is okay, map buffer and hand off to hardware */ 771 + txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_SINGLE; 772 + desc->buf_ptr = dma_map_single(dev->dev.parent, data, 773 + length, DMA_TO_DEVICE); 774 + if (unlikely(dma_mapping_error(dev->dev.parent, 775 + desc->buf_ptr))) { 776 + WARN(1, "dma_map_single failed!\n"); 777 + return -ENOMEM; 778 + } 767 779 } 768 780 769 781 cmd_sts = BUFFER_OWNED_BY_DMA; ··· 791 779 } 792 780 793 781 static inline void 794 - txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length) 782 + txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length, 783 + u32 *first_cmd_sts, bool first_desc) 795 784 { 796 785 struct mv643xx_eth_private *mp = txq_to_mp(txq); 797 786 int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); ··· 801 788 int ret; 802 789 u32 cmd_csum = 0; 803 790 u16 l4i_chk = 0; 791 + u32 cmd_sts; 804 792 805 793 tx_index = txq->tx_curr_desc; 806 794 desc = &txq->tx_desc_area[tx_index]; ··· 817 803 desc->byte_cnt = hdr_len; 818 804 desc->buf_ptr = txq->tso_hdrs_dma + 819 805 txq->tx_curr_desc * TSO_HEADER_SIZE; 820 - desc->cmd_sts = cmd_csum | BUFFER_OWNED_BY_DMA | TX_FIRST_DESC | 806 + cmd_sts = cmd_csum | BUFFER_OWNED_BY_DMA | TX_FIRST_DESC | 821 807 GEN_CRC; 808 + 809 + /* Defer updating the first command descriptor until all 810 + * following descriptors have been written. 811 + */ 812 + if (first_desc) 813 + *first_cmd_sts = cmd_sts; 814 + else 815 + desc->cmd_sts = cmd_sts; 822 816 823 817 txq->tx_curr_desc++; 824 818 if (txq->tx_curr_desc == txq->tx_ring_size) ··· 841 819 int desc_count = 0; 842 820 struct tso_t tso; 843 821 int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 822 + struct tx_desc *first_tx_desc; 823 + u32 first_cmd_sts = 0; 844 824 845 825 /* Count needed descriptors */ 846 826 if ((txq->tx_desc_count + tso_count_descs(skb)) >= txq->tx_ring_size) { ··· 850 826 return -EBUSY; 851 827 } 852 828 829 + first_tx_desc = &txq->tx_desc_area[txq->tx_curr_desc]; 830 + 853 831 /* Initialize the TSO handler, and prepare the first payload */ 854 832 tso_start(skb, &tso); 855 833 856 834 total_len = skb->len - hdr_len; 857 835 while (total_len > 0) { 836 + bool first_desc = (desc_count == 0); 858 837 char *hdr; 859 838 860 839 data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); ··· 867 840 /* prepare packet headers: MAC + IP + TCP */ 868 841 hdr = txq->tso_hdrs + txq->tx_curr_desc * TSO_HEADER_SIZE; 869 842 tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 870 - txq_put_hdr_tso(skb, txq, data_left); 843 + txq_put_hdr_tso(skb, txq, data_left, &first_cmd_sts, 844 + first_desc); 871 845 872 846 while (data_left > 0) { 873 847 int size; ··· 887 859 888 860 __skb_queue_tail(&txq->tx_skb, skb); 889 861 skb_tx_timestamp(skb); 862 + 863 + /* ensure all other descriptors are written before first cmd_sts */ 864 + wmb(); 865 + first_tx_desc->cmd_sts = first_cmd_sts; 890 866 891 867 /* clear TX_END status */ 892 868 mp->work_tx_end &= ~(1 << txq->index);