diff --git a/qdf/linux/src/qdf_nbuf.c b/qdf/linux/src/qdf_nbuf.c index 1adf5d3837dc..30a5bcc59d55 100644 --- a/qdf/linux/src/qdf_nbuf.c +++ b/qdf/linux/src/qdf_nbuf.c @@ -2800,6 +2800,59 @@ struct qdf_tso_cmn_seg_info_t { uint32_t tcp_seq_num; }; +/** + * qdf_nbuf_adj_tso_frag() - adjustment for buffer address of tso fragment + * + * @skb: network buffer + * + * Return: byte offset length of 8 bytes aligned. + */ +#ifdef WAR_TXDMA_LIMITATION +static uint8_t qdf_nbuf_adj_tso_frag(struct sk_buff *skb) +{ + uint32_t eit_hdr_len; + uint8_t *eit_hdr; + uint8_t byte_8_align_offset; + + /* + * Workaround for TXDMA HW limitation. + * ADDR0&0x1FFFFFFF8 should not equal ADDR1&0x1FFFFFFF8. + * Otherwise, TXDMA will run into exception, which cause TX fail. + * ADDR0: the address of last words in previous buffer; + * ADDR1: the address of first words in next buffer; + * To avoid this, shift several bytes for ADDR0. + */ + eit_hdr = skb->data; + eit_hdr_len = (skb_transport_header(skb) + - skb_mac_header(skb)) + tcp_hdrlen(skb); + byte_8_align_offset = ((unsigned long)(eit_hdr) + eit_hdr_len) & 0x7L; + if (qdf_unlikely(byte_8_align_offset)) { + TSO_DEBUG("%pK,Len %d %d", + eit_hdr, eit_hdr_len, byte_8_align_offset); + if (unlikely(skb_headroom(skb) < byte_8_align_offset)) { + TSO_DEBUG("[%d]Insufficient headroom,[%pK],[%pK],[%d]", + __LINE__, skb->head, skb->data, + byte_8_align_offset); + return 0; + } + qdf_nbuf_push_head(skb, byte_8_align_offset); + qdf_mem_move(skb->data, + skb->data + byte_8_align_offset, + eit_hdr_len); + skb->len -= byte_8_align_offset; + skb->mac_header -= byte_8_align_offset; + skb->network_header -= byte_8_align_offset; + skb->transport_header -= byte_8_align_offset; + } + return byte_8_align_offset; +} +#else +static uint8_t qdf_nbuf_adj_tso_frag(struct sk_buff *skb) +{ + return 0; +} +#endif + /** * __qdf_nbuf_get_tso_cmn_seg_info() - get TSO common * information @@ -2970,12 +3023,15 @@ uint32_t __qdf_nbuf_get_tso_info(qdf_device_t osdev, struct sk_buff *skb, uint32_t skb_proc = skb->len; /* bytes of skb pending processing */ uint32_t tso_seg_size = skb_shinfo(skb)->gso_size; int j = 0; /* skb fragment index */ + uint8_t byte_8_align_offset; memset(&tso_cmn_info, 0x0, sizeof(tso_cmn_info)); total_num_seg = tso_info->tso_num_seg_list; curr_seg = tso_info->tso_seg_list; total_num_seg->num_seg.tso_cmn_num_seg = 0; + byte_8_align_offset = qdf_nbuf_adj_tso_frag(skb); + if (qdf_unlikely(__qdf_nbuf_get_tso_cmn_seg_info(osdev, skb, &tso_cmn_info))) { qdf_warn("TSO: error getting common segment info"); @@ -2991,7 +3047,9 @@ uint32_t __qdf_nbuf_get_tso_info(qdf_device_t osdev, struct sk_buff *skb, skb_proc -= tso_cmn_info.eit_hdr_len; /* get the address to the next tso fragment */ - tso_frag_vaddr = skb->data + tso_cmn_info.eit_hdr_len; + tso_frag_vaddr = skb->data + + tso_cmn_info.eit_hdr_len + + byte_8_align_offset; /* get the length of the next tso fragment */ tso_frag_len = min(skb_frag_len, tso_seg_size);