|
@@ -13,16 +13,19 @@
|
|
|
|
|
|
#include "qlcnic.h"
|
|
#include "qlcnic.h"
|
|
|
|
|
|
-#define TX_ETHER_PKT 0x01
|
|
|
|
-#define TX_TCP_PKT 0x02
|
|
|
|
-#define TX_UDP_PKT 0x03
|
|
|
|
-#define TX_IP_PKT 0x04
|
|
|
|
-#define TX_TCP_LSO 0x05
|
|
|
|
-#define TX_TCP_LSO6 0x06
|
|
|
|
-#define TX_TCPV6_PKT 0x0b
|
|
|
|
-#define TX_UDPV6_PKT 0x0c
|
|
|
|
-#define FLAGS_VLAN_TAGGED 0x10
|
|
|
|
-#define FLAGS_VLAN_OOB 0x40
|
|
|
|
|
|
+#define QLCNIC_TX_ETHER_PKT 0x01
|
|
|
|
+#define QLCNIC_TX_TCP_PKT 0x02
|
|
|
|
+#define QLCNIC_TX_UDP_PKT 0x03
|
|
|
|
+#define QLCNIC_TX_IP_PKT 0x04
|
|
|
|
+#define QLCNIC_TX_TCP_LSO 0x05
|
|
|
|
+#define QLCNIC_TX_TCP_LSO6 0x06
|
|
|
|
+#define QLCNIC_TX_ENCAP_PKT 0x07
|
|
|
|
+#define QLCNIC_TX_ENCAP_LSO 0x08
|
|
|
|
+#define QLCNIC_TX_TCPV6_PKT 0x0b
|
|
|
|
+#define QLCNIC_TX_UDPV6_PKT 0x0c
|
|
|
|
+
|
|
|
|
+#define QLCNIC_FLAGS_VLAN_TAGGED 0x10
|
|
|
|
+#define QLCNIC_FLAGS_VLAN_OOB 0x40
|
|
|
|
|
|
#define qlcnic_set_tx_vlan_tci(cmd_desc, v) \
|
|
#define qlcnic_set_tx_vlan_tci(cmd_desc, v) \
|
|
(cmd_desc)->vlan_TCI = cpu_to_le16(v);
|
|
(cmd_desc)->vlan_TCI = cpu_to_le16(v);
|
|
@@ -364,6 +367,101 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
|
|
spin_unlock(&adapter->mac_learn_lock);
|
|
spin_unlock(&adapter->mac_learn_lock);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#define QLCNIC_ENCAP_VXLAN_PKT BIT_0
|
|
|
|
+#define QLCNIC_ENCAP_OUTER_L3_IP6 BIT_1
|
|
|
|
+#define QLCNIC_ENCAP_INNER_L3_IP6 BIT_2
|
|
|
|
+#define QLCNIC_ENCAP_INNER_L4_UDP BIT_3
|
|
|
|
+#define QLCNIC_ENCAP_DO_L3_CSUM BIT_4
|
|
|
|
+#define QLCNIC_ENCAP_DO_L4_CSUM BIT_5
|
|
|
|
+
|
|
|
|
+static int qlcnic_tx_encap_pkt(struct qlcnic_adapter *adapter,
|
|
|
|
+ struct cmd_desc_type0 *first_desc,
|
|
|
|
+ struct sk_buff *skb,
|
|
|
|
+ struct qlcnic_host_tx_ring *tx_ring)
|
|
|
|
+{
|
|
|
|
+ u8 opcode = 0, inner_hdr_len = 0, outer_hdr_len = 0, total_hdr_len = 0;
|
|
|
|
+ int copied, copy_len, descr_size;
|
|
|
|
+ u32 producer = tx_ring->producer;
|
|
|
|
+ struct cmd_desc_type0 *hwdesc;
|
|
|
|
+ u16 flags = 0, encap_descr = 0;
|
|
|
|
+
|
|
|
|
+ opcode = QLCNIC_TX_ETHER_PKT;
|
|
|
|
+ encap_descr = QLCNIC_ENCAP_VXLAN_PKT;
|
|
|
|
+
|
|
|
|
+ if (skb_is_gso(skb)) {
|
|
|
|
+ inner_hdr_len = skb_inner_transport_header(skb) +
|
|
|
|
+ inner_tcp_hdrlen(skb) -
|
|
|
|
+ skb_inner_mac_header(skb);
|
|
|
|
+
|
|
|
|
+ /* VXLAN header size = 8 */
|
|
|
|
+ outer_hdr_len = skb_transport_offset(skb) + 8 +
|
|
|
|
+ sizeof(struct udphdr);
|
|
|
|
+ first_desc->outer_hdr_length = outer_hdr_len;
|
|
|
|
+ total_hdr_len = inner_hdr_len + outer_hdr_len;
|
|
|
|
+ encap_descr |= QLCNIC_ENCAP_DO_L3_CSUM |
|
|
|
|
+ QLCNIC_ENCAP_DO_L4_CSUM;
|
|
|
|
+ first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
|
|
|
|
+ first_desc->hdr_length = inner_hdr_len;
|
|
|
|
+
|
|
|
|
+ /* Copy inner and outer headers in Tx descriptor(s)
|
|
|
|
+ * If total_hdr_len > cmd_desc_type0, use multiple
|
|
|
|
+ * descriptors
|
|
|
|
+ */
|
|
|
|
+ copied = 0;
|
|
|
|
+ descr_size = (int)sizeof(struct cmd_desc_type0);
|
|
|
|
+ while (copied < total_hdr_len) {
|
|
|
|
+ copy_len = min(descr_size, (total_hdr_len - copied));
|
|
|
|
+ hwdesc = &tx_ring->desc_head[producer];
|
|
|
|
+ tx_ring->cmd_buf_arr[producer].skb = NULL;
|
|
|
|
+ skb_copy_from_linear_data_offset(skb, copied,
|
|
|
|
+ (char *)hwdesc,
|
|
|
|
+ copy_len);
|
|
|
|
+ copied += copy_len;
|
|
|
|
+ producer = get_next_index(producer, tx_ring->num_desc);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ tx_ring->producer = producer;
|
|
|
|
+
|
|
|
|
+ /* Make sure updated tx_ring->producer is visible
|
|
|
|
+ * for qlcnic_tx_avail()
|
|
|
|
+ */
|
|
|
|
+ smp_mb();
|
|
|
|
+ adapter->stats.encap_lso_frames++;
|
|
|
|
+
|
|
|
|
+ opcode = QLCNIC_TX_ENCAP_LSO;
|
|
|
|
+ } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
|
|
+ if (inner_ip_hdr(skb)->version == 6) {
|
|
|
|
+ if (inner_ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)
|
|
|
|
+ encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP;
|
|
|
|
+ } else {
|
|
|
|
+ if (inner_ip_hdr(skb)->protocol == IPPROTO_UDP)
|
|
|
|
+ encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ adapter->stats.encap_tx_csummed++;
|
|
|
|
+ opcode = QLCNIC_TX_ENCAP_PKT;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Prepare first 16 bits of byte offset 16 of Tx descriptor */
|
|
|
|
+ if (ip_hdr(skb)->version == 6)
|
|
|
|
+ encap_descr |= QLCNIC_ENCAP_OUTER_L3_IP6;
|
|
|
|
+
|
|
|
|
+ /* outer IP header's size in 32bit words size*/
|
|
|
|
+ encap_descr |= (skb_network_header_len(skb) >> 2) << 6;
|
|
|
|
+
|
|
|
|
+ /* outer IP header offset */
|
|
|
|
+ encap_descr |= skb_network_offset(skb) << 10;
|
|
|
|
+ first_desc->encap_descr = cpu_to_le16(encap_descr);
|
|
|
|
+
|
|
|
|
+ first_desc->tcp_hdr_offset = skb_inner_transport_header(skb) -
|
|
|
|
+ skb->data;
|
|
|
|
+ first_desc->ip_hdr_offset = skb_inner_network_offset(skb);
|
|
|
|
+
|
|
|
|
+ qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
|
|
static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
|
|
struct cmd_desc_type0 *first_desc, struct sk_buff *skb,
|
|
struct cmd_desc_type0 *first_desc, struct sk_buff *skb,
|
|
struct qlcnic_host_tx_ring *tx_ring)
|
|
struct qlcnic_host_tx_ring *tx_ring)
|
|
@@ -378,11 +476,11 @@ static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
|
|
|
|
|
|
if (protocol == ETH_P_8021Q) {
|
|
if (protocol == ETH_P_8021Q) {
|
|
vh = (struct vlan_ethhdr *)skb->data;
|
|
vh = (struct vlan_ethhdr *)skb->data;
|
|
- flags = FLAGS_VLAN_TAGGED;
|
|
|
|
|
|
+ flags = QLCNIC_FLAGS_VLAN_TAGGED;
|
|
vlan_tci = ntohs(vh->h_vlan_TCI);
|
|
vlan_tci = ntohs(vh->h_vlan_TCI);
|
|
protocol = ntohs(vh->h_vlan_encapsulated_proto);
|
|
protocol = ntohs(vh->h_vlan_encapsulated_proto);
|
|
} else if (vlan_tx_tag_present(skb)) {
|
|
} else if (vlan_tx_tag_present(skb)) {
|
|
- flags = FLAGS_VLAN_OOB;
|
|
|
|
|
|
+ flags = QLCNIC_FLAGS_VLAN_OOB;
|
|
vlan_tci = vlan_tx_tag_get(skb);
|
|
vlan_tci = vlan_tx_tag_get(skb);
|
|
}
|
|
}
|
|
if (unlikely(adapter->tx_pvid)) {
|
|
if (unlikely(adapter->tx_pvid)) {
|
|
@@ -391,7 +489,7 @@ static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
|
|
if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED))
|
|
if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED))
|
|
goto set_flags;
|
|
goto set_flags;
|
|
|
|
|
|
- flags = FLAGS_VLAN_OOB;
|
|
|
|
|
|
+ flags = QLCNIC_FLAGS_VLAN_OOB;
|
|
vlan_tci = adapter->tx_pvid;
|
|
vlan_tci = adapter->tx_pvid;
|
|
}
|
|
}
|
|
set_flags:
|
|
set_flags:
|
|
@@ -402,25 +500,26 @@ set_flags:
|
|
flags |= BIT_0;
|
|
flags |= BIT_0;
|
|
memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
|
|
memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
|
|
}
|
|
}
|
|
- opcode = TX_ETHER_PKT;
|
|
|
|
|
|
+ opcode = QLCNIC_TX_ETHER_PKT;
|
|
if (skb_is_gso(skb)) {
|
|
if (skb_is_gso(skb)) {
|
|
hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
|
|
hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
|
|
first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
|
|
first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
|
|
- first_desc->total_hdr_length = hdr_len;
|
|
|
|
- opcode = (protocol == ETH_P_IPV6) ? TX_TCP_LSO6 : TX_TCP_LSO;
|
|
|
|
|
|
+ first_desc->hdr_length = hdr_len;
|
|
|
|
+ opcode = (protocol == ETH_P_IPV6) ? QLCNIC_TX_TCP_LSO6 :
|
|
|
|
+ QLCNIC_TX_TCP_LSO;
|
|
|
|
|
|
/* For LSO, we need to copy the MAC/IP/TCP headers into
|
|
/* For LSO, we need to copy the MAC/IP/TCP headers into
|
|
* the descriptor ring */
|
|
* the descriptor ring */
|
|
copied = 0;
|
|
copied = 0;
|
|
offset = 2;
|
|
offset = 2;
|
|
|
|
|
|
- if (flags & FLAGS_VLAN_OOB) {
|
|
|
|
- first_desc->total_hdr_length += VLAN_HLEN;
|
|
|
|
|
|
+ if (flags & QLCNIC_FLAGS_VLAN_OOB) {
|
|
|
|
+ first_desc->hdr_length += VLAN_HLEN;
|
|
first_desc->tcp_hdr_offset = VLAN_HLEN;
|
|
first_desc->tcp_hdr_offset = VLAN_HLEN;
|
|
first_desc->ip_hdr_offset = VLAN_HLEN;
|
|
first_desc->ip_hdr_offset = VLAN_HLEN;
|
|
|
|
|
|
/* Only in case of TSO on vlan device */
|
|
/* Only in case of TSO on vlan device */
|
|
- flags |= FLAGS_VLAN_TAGGED;
|
|
|
|
|
|
+ flags |= QLCNIC_FLAGS_VLAN_TAGGED;
|
|
|
|
|
|
/* Create a TSO vlan header template for firmware */
|
|
/* Create a TSO vlan header template for firmware */
|
|
hwdesc = &tx_ring->desc_head[producer];
|
|
hwdesc = &tx_ring->desc_head[producer];
|
|
@@ -464,16 +563,16 @@ set_flags:
|
|
l4proto = ip_hdr(skb)->protocol;
|
|
l4proto = ip_hdr(skb)->protocol;
|
|
|
|
|
|
if (l4proto == IPPROTO_TCP)
|
|
if (l4proto == IPPROTO_TCP)
|
|
- opcode = TX_TCP_PKT;
|
|
|
|
|
|
+ opcode = QLCNIC_TX_TCP_PKT;
|
|
else if (l4proto == IPPROTO_UDP)
|
|
else if (l4proto == IPPROTO_UDP)
|
|
- opcode = TX_UDP_PKT;
|
|
|
|
|
|
+ opcode = QLCNIC_TX_UDP_PKT;
|
|
} else if (protocol == ETH_P_IPV6) {
|
|
} else if (protocol == ETH_P_IPV6) {
|
|
l4proto = ipv6_hdr(skb)->nexthdr;
|
|
l4proto = ipv6_hdr(skb)->nexthdr;
|
|
|
|
|
|
if (l4proto == IPPROTO_TCP)
|
|
if (l4proto == IPPROTO_TCP)
|
|
- opcode = TX_TCPV6_PKT;
|
|
|
|
|
|
+ opcode = QLCNIC_TX_TCPV6_PKT;
|
|
else if (l4proto == IPPROTO_UDP)
|
|
else if (l4proto == IPPROTO_UDP)
|
|
- opcode = TX_UDPV6_PKT;
|
|
|
|
|
|
+ opcode = QLCNIC_TX_UDPV6_PKT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
first_desc->tcp_hdr_offset += skb_transport_offset(skb);
|
|
first_desc->tcp_hdr_offset += skb_transport_offset(skb);
|
|
@@ -563,6 +662,8 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
|
|
struct ethhdr *phdr;
|
|
struct ethhdr *phdr;
|
|
int i, k, frag_count, delta = 0;
|
|
int i, k, frag_count, delta = 0;
|
|
u32 producer, num_txd;
|
|
u32 producer, num_txd;
|
|
|
|
+ u16 protocol;
|
|
|
|
+ bool l4_is_udp = false;
|
|
|
|
|
|
if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
|
|
if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
|
|
netif_tx_stop_all_queues(netdev);
|
|
netif_tx_stop_all_queues(netdev);
|
|
@@ -653,8 +754,23 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
|
|
tx_ring->producer = get_next_index(producer, num_txd);
|
|
tx_ring->producer = get_next_index(producer, num_txd);
|
|
smp_mb();
|
|
smp_mb();
|
|
|
|
|
|
- if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb, tx_ring)))
|
|
|
|
- goto unwind_buff;
|
|
|
|
|
|
+ protocol = ntohs(skb->protocol);
|
|
|
|
+ if (protocol == ETH_P_IP)
|
|
|
|
+ l4_is_udp = ip_hdr(skb)->protocol == IPPROTO_UDP;
|
|
|
|
+ else if (protocol == ETH_P_IPV6)
|
|
|
|
+ l4_is_udp = ipv6_hdr(skb)->nexthdr == IPPROTO_UDP;
|
|
|
|
+
|
|
|
|
+ /* Check if it is a VXLAN packet */
|
|
|
|
+ if (!skb->encapsulation || !l4_is_udp ||
|
|
|
|
+ !qlcnic_encap_tx_offload(adapter)) {
|
|
|
|
+ if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb,
|
|
|
|
+ tx_ring)))
|
|
|
|
+ goto unwind_buff;
|
|
|
|
+ } else {
|
|
|
|
+ if (unlikely(qlcnic_tx_encap_pkt(adapter, first_desc,
|
|
|
|
+ skb, tx_ring)))
|
|
|
|
+ goto unwind_buff;
|
|
|
|
+ }
|
|
|
|
|
|
if (adapter->drv_mac_learn)
|
|
if (adapter->drv_mac_learn)
|
|
qlcnic_send_filter(adapter, first_desc, skb);
|
|
qlcnic_send_filter(adapter, first_desc, skb);
|