|
@@ -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);
|
|
|
|