|
@@ -58,7 +58,7 @@ static struct kobj_type ktype_veth_pool;
|
|
|
|
|
|
static const char ibmveth_driver_name[] = "ibmveth";
|
|
|
static const char ibmveth_driver_string[] = "IBM Power Virtual Ethernet Driver";
|
|
|
-#define ibmveth_driver_version "1.05"
|
|
|
+#define ibmveth_driver_version "1.06"
|
|
|
|
|
|
MODULE_AUTHOR("Santiago Leon <santil@linux.vnet.ibm.com>");
|
|
|
MODULE_DESCRIPTION("IBM Power Virtual Ethernet Driver");
|
|
@@ -137,6 +137,11 @@ static inline int ibmveth_rxq_frame_offset(struct ibmveth_adapter *adapter)
|
|
|
return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_OFF_MASK;
|
|
|
}
|
|
|
|
|
|
+static inline int ibmveth_rxq_large_packet(struct ibmveth_adapter *adapter)
|
|
|
+{
|
|
|
+ return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_LRG_PKT;
|
|
|
+}
|
|
|
+
|
|
|
static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter)
|
|
|
{
|
|
|
return be32_to_cpu(adapter->rx_queue.queue_addr[adapter->rx_queue.index].length);
|
|
@@ -1174,6 +1179,45 @@ map_failed:
|
|
|
goto retry_bounce;
|
|
|
}
|
|
|
|
|
|
+static void ibmveth_rx_mss_helper(struct sk_buff *skb, u16 mss, int lrg_pkt)
|
|
|
+{
|
|
|
+ int offset = 0;
|
|
|
+
|
|
|
+ /* only TCP packets will be aggregated */
|
|
|
+ if (skb->protocol == htons(ETH_P_IP)) {
|
|
|
+ struct iphdr *iph = (struct iphdr *)skb->data;
|
|
|
+
|
|
|
+ if (iph->protocol == IPPROTO_TCP) {
|
|
|
+ offset = iph->ihl * 4;
|
|
|
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
|
|
|
+ struct ipv6hdr *iph6 = (struct ipv6hdr *)skb->data;
|
|
|
+
|
|
|
+ if (iph6->nexthdr == IPPROTO_TCP) {
|
|
|
+ offset = sizeof(struct ipv6hdr);
|
|
|
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ /* if mss is not set through Large Packet bit/mss in rx buffer,
|
|
|
+ * expect that the mss will be written to the tcp header checksum.
|
|
|
+ */
|
|
|
+ if (lrg_pkt) {
|
|
|
+ skb_shinfo(skb)->gso_size = mss;
|
|
|
+ } else if (offset) {
|
|
|
+ struct tcphdr *tcph = (struct tcphdr *)(skb->data + offset);
|
|
|
+
|
|
|
+ skb_shinfo(skb)->gso_size = ntohs(tcph->check);
|
|
|
+ tcph->check = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int ibmveth_poll(struct napi_struct *napi, int budget)
|
|
|
{
|
|
|
struct ibmveth_adapter *adapter =
|
|
@@ -1182,6 +1226,7 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
|
|
|
int frames_processed = 0;
|
|
|
unsigned long lpar_rc;
|
|
|
struct iphdr *iph;
|
|
|
+ u16 mss = 0;
|
|
|
|
|
|
restart_poll:
|
|
|
while (frames_processed < budget) {
|
|
@@ -1199,9 +1244,21 @@ restart_poll:
|
|
|
int length = ibmveth_rxq_frame_length(adapter);
|
|
|
int offset = ibmveth_rxq_frame_offset(adapter);
|
|
|
int csum_good = ibmveth_rxq_csum_good(adapter);
|
|
|
+ int lrg_pkt = ibmveth_rxq_large_packet(adapter);
|
|
|
|
|
|
skb = ibmveth_rxq_get_buffer(adapter);
|
|
|
|
|
|
+ /* if the large packet bit is set in the rx queue
|
|
|
+ * descriptor, the mss will be written by PHYP eight
|
|
|
+ * bytes from the start of the rx buffer, which is
|
|
|
+ * skb->data at this stage
|
|
|
+ */
|
|
|
+ if (lrg_pkt) {
|
|
|
+ __be64 *rxmss = (__be64 *)(skb->data + 8);
|
|
|
+
|
|
|
+ mss = (u16)be64_to_cpu(*rxmss);
|
|
|
+ }
|
|
|
+
|
|
|
new_skb = NULL;
|
|
|
if (length < rx_copybreak)
|
|
|
new_skb = netdev_alloc_skb(netdev, length);
|
|
@@ -1235,11 +1292,15 @@ restart_poll:
|
|
|
if (iph->check == 0xffff) {
|
|
|
iph->check = 0;
|
|
|
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
|
|
|
- adapter->rx_large_packets++;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (length > netdev->mtu + ETH_HLEN) {
|
|
|
+ ibmveth_rx_mss_helper(skb, mss, lrg_pkt);
|
|
|
+ adapter->rx_large_packets++;
|
|
|
+ }
|
|
|
+
|
|
|
napi_gro_receive(napi, skb); /* send it up */
|
|
|
|
|
|
netdev->stats.rx_packets++;
|