Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9a72dd4d authored by David L Stevens's avatar David L Stevens Committed by David S. Miller
Browse files

sunvnet: add GSO support



This patch adds GSO support to the sunvnet driver.

Signed-off-by: default avatarDavid L Stevens <david.stevens@oracle.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1b6b0a47
Loading
Loading
Loading
Loading
+72 −1
Original line number Diff line number Diff line
@@ -1102,6 +1102,10 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, int ncookies)
			return NULL;
		}
		(void)skb_put(nskb, skb->len);
		if (skb_is_gso(skb)) {
			skb_shinfo(nskb)->gso_size = skb_shinfo(skb)->gso_size;
			skb_shinfo(nskb)->gso_type = skb_shinfo(skb)->gso_type;
		}
		dev_kfree_skb(skb);
		skb = nskb;
	}
@@ -1120,6 +1124,66 @@ vnet_select_queue(struct net_device *dev, struct sk_buff *skb,
	return port->q_index;
}

static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev);

static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb)
{
	struct net_device *dev = port->vp->dev;
	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];
	struct sk_buff *segs;
	int maclen;
	int status;

	if (unlikely(vnet_tx_dring_avail(dr) < skb_shinfo(skb)->gso_segs)) {
		struct netdev_queue *txq;

		txq  = netdev_get_tx_queue(dev, port->q_index);
		netif_tx_stop_queue(txq);
		if (vnet_tx_dring_avail(dr) < skb_shinfo(skb)->gso_segs)
			return NETDEV_TX_BUSY;
		netif_tx_wake_queue(txq);
	}

	maclen = skb_network_header(skb) - skb_mac_header(skb);
	skb_pull(skb, maclen);

	segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO);
	if (IS_ERR(segs)) {
		dev->stats.tx_dropped++;
		return NETDEV_TX_OK;
	}

	skb_push(skb, maclen);
	skb_reset_mac_header(skb);

	status = 0;
	while (segs) {
		struct sk_buff *curr = segs;

		segs = segs->next;
		curr->next = NULL;

		skb_push(curr, maclen);
		skb_reset_mac_header(curr);
		memcpy(skb_mac_header(curr), skb_mac_header(skb),
		       maclen);
		curr->csum_start = skb_transport_header(curr) - curr->head;
		if (ip_hdr(curr)->protocol == IPPROTO_TCP)
			curr->csum_offset = offsetof(struct tcphdr, check);
		else if (ip_hdr(curr)->protocol == IPPROTO_UDP)
			curr->csum_offset = offsetof(struct udphdr, check);

		if (!(status & NETDEV_TX_MASK))
			status = vnet_start_xmit(curr, dev);
		if (status & NETDEV_TX_MASK)
			dev_kfree_skb_any(curr);
	}

	if (!(status & NETDEV_TX_MASK))
		dev_kfree_skb_any(skb);
	return status;
}

static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct vnet *vp = netdev_priv(dev);
@@ -1139,6 +1203,12 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
		goto out_dropped;
	}

	if (skb_is_gso(skb)) {
		err = vnet_handle_offloads(port, skb);
		rcu_read_unlock();
		return err;
	}

	if (skb->len > port->rmtu) {
		unsigned long localmtu = port->rmtu - ETH_HLEN;

@@ -1642,7 +1712,8 @@ static struct vnet *vnet_new(const u64 *local_mac)
	dev->ethtool_ops = &vnet_ethtool_ops;
	dev->watchdog_timeo = VNET_TX_TIMEOUT;

	dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG;
	dev->hw_features = NETIF_F_GSO | NETIF_F_GSO_SOFTWARE |
			   NETIF_F_HW_CSUM | NETIF_F_SG;
	dev->features = dev->hw_features;

	err = register_netdev(dev);