18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* sunvnet.c: Sun LDOM Virtual Network Driver.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
58c2ecf20Sopenharmony_ci * Copyright (C) 2016-2017 Oracle. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include <linux/highmem.h>
198c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
208c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
218c2ecf20Sopenharmony_ci#include <trace/events/sunvnet.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
248c2ecf20Sopenharmony_ci#include <linux/icmpv6.h>
258c2ecf20Sopenharmony_ci#endif
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <net/ip.h>
288c2ecf20Sopenharmony_ci#include <net/icmp.h>
298c2ecf20Sopenharmony_ci#include <net/route.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <asm/vio.h>
328c2ecf20Sopenharmony_ci#include <asm/ldc.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "sunvnet_common.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Heuristic for the number of times to exponentially backoff and
378c2ecf20Sopenharmony_ci * retry sending an LDC trigger when EAGAIN is encountered
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci#define	VNET_MAX_RETRIES	10
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ciMODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sun LDOM virtual network support library");
438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
448c2ecf20Sopenharmony_ciMODULE_VERSION("1.1");
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int __vnet_tx_trigger(struct vnet_port *port, u32 start);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic inline u32 vnet_tx_dring_avail(struct vio_dring_state *dr)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	return vio_dring_avail(dr, VNET_TX_RING_SIZE);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int vnet_handle_unknown(struct vnet_port *port, void *arg)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct vio_msg_tag *pkt = arg;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	pr_err("Received unknown msg [%02x:%02x:%04x:%08x]\n",
588c2ecf20Sopenharmony_ci	       pkt->type, pkt->stype, pkt->stype_env, pkt->sid);
598c2ecf20Sopenharmony_ci	pr_err("Resetting connection\n");
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	ldc_disconnect(port->vio.lp);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return -ECONNRESET;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int vnet_port_alloc_tx_ring(struct vnet_port *port);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ciint sunvnet_send_attr_common(struct vio_driver_state *vio)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct vnet_port *port = to_vnet_port(vio);
718c2ecf20Sopenharmony_ci	struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port);
728c2ecf20Sopenharmony_ci	struct vio_net_attr_info pkt;
738c2ecf20Sopenharmony_ci	int framelen = ETH_FRAME_LEN;
748c2ecf20Sopenharmony_ci	int i, err;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	err = vnet_port_alloc_tx_ring(to_vnet_port(vio));
778c2ecf20Sopenharmony_ci	if (err)
788c2ecf20Sopenharmony_ci		return err;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
818c2ecf20Sopenharmony_ci	pkt.tag.type = VIO_TYPE_CTRL;
828c2ecf20Sopenharmony_ci	pkt.tag.stype = VIO_SUBTYPE_INFO;
838c2ecf20Sopenharmony_ci	pkt.tag.stype_env = VIO_ATTR_INFO;
848c2ecf20Sopenharmony_ci	pkt.tag.sid = vio_send_sid(vio);
858c2ecf20Sopenharmony_ci	if (vio_version_before(vio, 1, 2))
868c2ecf20Sopenharmony_ci		pkt.xfer_mode = VIO_DRING_MODE;
878c2ecf20Sopenharmony_ci	else
888c2ecf20Sopenharmony_ci		pkt.xfer_mode = VIO_NEW_DRING_MODE;
898c2ecf20Sopenharmony_ci	pkt.addr_type = VNET_ADDR_ETHERMAC;
908c2ecf20Sopenharmony_ci	pkt.ack_freq = 0;
918c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++)
928c2ecf20Sopenharmony_ci		pkt.addr |= (u64)dev->dev_addr[i] << ((5 - i) * 8);
938c2ecf20Sopenharmony_ci	if (vio_version_after(vio, 1, 3)) {
948c2ecf20Sopenharmony_ci		if (port->rmtu) {
958c2ecf20Sopenharmony_ci			port->rmtu = min(VNET_MAXPACKET, port->rmtu);
968c2ecf20Sopenharmony_ci			pkt.mtu = port->rmtu;
978c2ecf20Sopenharmony_ci		} else {
988c2ecf20Sopenharmony_ci			port->rmtu = VNET_MAXPACKET;
998c2ecf20Sopenharmony_ci			pkt.mtu = port->rmtu;
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci		if (vio_version_after_eq(vio, 1, 6))
1028c2ecf20Sopenharmony_ci			pkt.options = VIO_TX_DRING;
1038c2ecf20Sopenharmony_ci	} else if (vio_version_before(vio, 1, 3)) {
1048c2ecf20Sopenharmony_ci		pkt.mtu = framelen;
1058c2ecf20Sopenharmony_ci	} else { /* v1.3 */
1068c2ecf20Sopenharmony_ci		pkt.mtu = framelen + VLAN_HLEN;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	pkt.cflags = 0;
1108c2ecf20Sopenharmony_ci	if (vio_version_after_eq(vio, 1, 7) && port->tso) {
1118c2ecf20Sopenharmony_ci		pkt.cflags |= VNET_LSO_IPV4_CAPAB;
1128c2ecf20Sopenharmony_ci		if (!port->tsolen)
1138c2ecf20Sopenharmony_ci			port->tsolen = VNET_MAXTSO;
1148c2ecf20Sopenharmony_ci		pkt.ipv4_lso_maxlen = port->tsolen;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	pkt.plnk_updt = PHYSLINK_UPDATE_NONE;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	viodbg(HS, "SEND NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] "
1208c2ecf20Sopenharmony_ci	       "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] "
1218c2ecf20Sopenharmony_ci	       "cflags[0x%04x] lso_max[%u]\n",
1228c2ecf20Sopenharmony_ci	       pkt.xfer_mode, pkt.addr_type,
1238c2ecf20Sopenharmony_ci	       (unsigned long long)pkt.addr,
1248c2ecf20Sopenharmony_ci	       pkt.ack_freq, pkt.plnk_updt, pkt.options,
1258c2ecf20Sopenharmony_ci	       (unsigned long long)pkt.mtu, pkt.cflags, pkt.ipv4_lso_maxlen);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return vio_ldc_send(vio, &pkt, sizeof(pkt));
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_send_attr_common);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int handle_attr_info(struct vio_driver_state *vio,
1328c2ecf20Sopenharmony_ci			    struct vio_net_attr_info *pkt)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct vnet_port *port = to_vnet_port(vio);
1358c2ecf20Sopenharmony_ci	u64	localmtu;
1368c2ecf20Sopenharmony_ci	u8	xfer_mode;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	viodbg(HS, "GOT NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] "
1398c2ecf20Sopenharmony_ci	       "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] "
1408c2ecf20Sopenharmony_ci	       " (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n",
1418c2ecf20Sopenharmony_ci	       pkt->xfer_mode, pkt->addr_type,
1428c2ecf20Sopenharmony_ci	       (unsigned long long)pkt->addr,
1438c2ecf20Sopenharmony_ci	       pkt->ack_freq, pkt->plnk_updt, pkt->options,
1448c2ecf20Sopenharmony_ci	       (unsigned long long)pkt->mtu, port->rmtu, pkt->cflags,
1458c2ecf20Sopenharmony_ci	       pkt->ipv4_lso_maxlen);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	pkt->tag.sid = vio_send_sid(vio);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	xfer_mode = pkt->xfer_mode;
1508c2ecf20Sopenharmony_ci	/* for version < 1.2, VIO_DRING_MODE = 0x3 and no bitmask */
1518c2ecf20Sopenharmony_ci	if (vio_version_before(vio, 1, 2) && xfer_mode == VIO_DRING_MODE)
1528c2ecf20Sopenharmony_ci		xfer_mode = VIO_NEW_DRING_MODE;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* MTU negotiation:
1558c2ecf20Sopenharmony_ci	 *	< v1.3 - ETH_FRAME_LEN exactly
1568c2ecf20Sopenharmony_ci	 *	> v1.3 - MIN(pkt.mtu, VNET_MAXPACKET, port->rmtu) and change
1578c2ecf20Sopenharmony_ci	 *			pkt->mtu for ACK
1588c2ecf20Sopenharmony_ci	 *	= v1.3 - ETH_FRAME_LEN + VLAN_HLEN exactly
1598c2ecf20Sopenharmony_ci	 */
1608c2ecf20Sopenharmony_ci	if (vio_version_before(vio, 1, 3)) {
1618c2ecf20Sopenharmony_ci		localmtu = ETH_FRAME_LEN;
1628c2ecf20Sopenharmony_ci	} else if (vio_version_after(vio, 1, 3)) {
1638c2ecf20Sopenharmony_ci		localmtu = port->rmtu ? port->rmtu : VNET_MAXPACKET;
1648c2ecf20Sopenharmony_ci		localmtu = min(pkt->mtu, localmtu);
1658c2ecf20Sopenharmony_ci		pkt->mtu = localmtu;
1668c2ecf20Sopenharmony_ci	} else { /* v1.3 */
1678c2ecf20Sopenharmony_ci		localmtu = ETH_FRAME_LEN + VLAN_HLEN;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	port->rmtu = localmtu;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* LSO negotiation */
1728c2ecf20Sopenharmony_ci	if (vio_version_after_eq(vio, 1, 7))
1738c2ecf20Sopenharmony_ci		port->tso &= !!(pkt->cflags & VNET_LSO_IPV4_CAPAB);
1748c2ecf20Sopenharmony_ci	else
1758c2ecf20Sopenharmony_ci		port->tso = false;
1768c2ecf20Sopenharmony_ci	if (port->tso) {
1778c2ecf20Sopenharmony_ci		if (!port->tsolen)
1788c2ecf20Sopenharmony_ci			port->tsolen = VNET_MAXTSO;
1798c2ecf20Sopenharmony_ci		port->tsolen = min(port->tsolen, pkt->ipv4_lso_maxlen);
1808c2ecf20Sopenharmony_ci		if (port->tsolen < VNET_MINTSO) {
1818c2ecf20Sopenharmony_ci			port->tso = false;
1828c2ecf20Sopenharmony_ci			port->tsolen = 0;
1838c2ecf20Sopenharmony_ci			pkt->cflags &= ~VNET_LSO_IPV4_CAPAB;
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci		pkt->ipv4_lso_maxlen = port->tsolen;
1868c2ecf20Sopenharmony_ci	} else {
1878c2ecf20Sopenharmony_ci		pkt->cflags &= ~VNET_LSO_IPV4_CAPAB;
1888c2ecf20Sopenharmony_ci		pkt->ipv4_lso_maxlen = 0;
1898c2ecf20Sopenharmony_ci		port->tsolen = 0;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* for version >= 1.6, ACK packet mode we support */
1938c2ecf20Sopenharmony_ci	if (vio_version_after_eq(vio, 1, 6)) {
1948c2ecf20Sopenharmony_ci		pkt->xfer_mode = VIO_NEW_DRING_MODE;
1958c2ecf20Sopenharmony_ci		pkt->options = VIO_TX_DRING;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!(xfer_mode | VIO_NEW_DRING_MODE) ||
1998c2ecf20Sopenharmony_ci	    pkt->addr_type != VNET_ADDR_ETHERMAC ||
2008c2ecf20Sopenharmony_ci	    pkt->mtu != localmtu) {
2018c2ecf20Sopenharmony_ci		viodbg(HS, "SEND NET ATTR NACK\n");
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		pkt->tag.stype = VIO_SUBTYPE_NACK;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		(void)vio_ldc_send(vio, pkt, sizeof(*pkt));
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		return -ECONNRESET;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	viodbg(HS, "SEND NET ATTR ACK xmode[0x%x] atype[0x%x] "
2118c2ecf20Sopenharmony_ci	       "addr[%llx] ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] "
2128c2ecf20Sopenharmony_ci	       "mtu[%llu] (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n",
2138c2ecf20Sopenharmony_ci	       pkt->xfer_mode, pkt->addr_type,
2148c2ecf20Sopenharmony_ci	       (unsigned long long)pkt->addr,
2158c2ecf20Sopenharmony_ci	       pkt->ack_freq, pkt->plnk_updt, pkt->options,
2168c2ecf20Sopenharmony_ci	       (unsigned long long)pkt->mtu, port->rmtu, pkt->cflags,
2178c2ecf20Sopenharmony_ci	       pkt->ipv4_lso_maxlen);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	pkt->tag.stype = VIO_SUBTYPE_ACK;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return vio_ldc_send(vio, pkt, sizeof(*pkt));
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int handle_attr_ack(struct vio_driver_state *vio,
2258c2ecf20Sopenharmony_ci			   struct vio_net_attr_info *pkt)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	viodbg(HS, "GOT NET ATTR ACK\n");
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return 0;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int handle_attr_nack(struct vio_driver_state *vio,
2338c2ecf20Sopenharmony_ci			    struct vio_net_attr_info *pkt)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	viodbg(HS, "GOT NET ATTR NACK\n");
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return -ECONNRESET;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ciint sunvnet_handle_attr_common(struct vio_driver_state *vio, void *arg)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct vio_net_attr_info *pkt = arg;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	switch (pkt->tag.stype) {
2458c2ecf20Sopenharmony_ci	case VIO_SUBTYPE_INFO:
2468c2ecf20Sopenharmony_ci		return handle_attr_info(vio, pkt);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	case VIO_SUBTYPE_ACK:
2498c2ecf20Sopenharmony_ci		return handle_attr_ack(vio, pkt);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	case VIO_SUBTYPE_NACK:
2528c2ecf20Sopenharmony_ci		return handle_attr_nack(vio, pkt);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	default:
2558c2ecf20Sopenharmony_ci		return -ECONNRESET;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_handle_attr_common);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_civoid sunvnet_handshake_complete_common(struct vio_driver_state *vio)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct vio_dring_state *dr;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	dr = &vio->drings[VIO_DRIVER_RX_RING];
2658c2ecf20Sopenharmony_ci	dr->rcv_nxt = 1;
2668c2ecf20Sopenharmony_ci	dr->snd_nxt = 1;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	dr = &vio->drings[VIO_DRIVER_TX_RING];
2698c2ecf20Sopenharmony_ci	dr->rcv_nxt = 1;
2708c2ecf20Sopenharmony_ci	dr->snd_nxt = 1;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_handshake_complete_common);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/* The hypervisor interface that implements copying to/from imported
2758c2ecf20Sopenharmony_ci * memory from another domain requires that copies are done to 8-byte
2768c2ecf20Sopenharmony_ci * aligned buffers, and that the lengths of such copies are also 8-byte
2778c2ecf20Sopenharmony_ci * multiples.
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci * So we align skb->data to an 8-byte multiple and pad-out the data
2808c2ecf20Sopenharmony_ci * area so we can round the copy length up to the next multiple of
2818c2ecf20Sopenharmony_ci * 8 for the copy.
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * The transmitter puts the actual start of the packet 6 bytes into
2848c2ecf20Sopenharmony_ci * the buffer it sends over, so that the IP headers after the ethernet
2858c2ecf20Sopenharmony_ci * header are aligned properly.  These 6 bytes are not in the descriptor
2868c2ecf20Sopenharmony_ci * length, they are simply implied.  This offset is represented using
2878c2ecf20Sopenharmony_ci * the VNET_PACKET_SKIP macro.
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_cistatic struct sk_buff *alloc_and_align_skb(struct net_device *dev,
2908c2ecf20Sopenharmony_ci					   unsigned int len)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2938c2ecf20Sopenharmony_ci	unsigned long addr, off;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	skb = netdev_alloc_skb(dev, len + VNET_PACKET_SKIP + 8 + 8);
2968c2ecf20Sopenharmony_ci	if (unlikely(!skb))
2978c2ecf20Sopenharmony_ci		return NULL;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	addr = (unsigned long)skb->data;
3008c2ecf20Sopenharmony_ci	off = ((addr + 7UL) & ~7UL) - addr;
3018c2ecf20Sopenharmony_ci	if (off)
3028c2ecf20Sopenharmony_ci		skb_reserve(skb, off);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return skb;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic inline void vnet_fullcsum_ipv4(struct sk_buff *skb)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct iphdr *iph = ip_hdr(skb);
3108c2ecf20Sopenharmony_ci	int offset = skb_transport_offset(skb);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (skb->protocol != htons(ETH_P_IP))
3138c2ecf20Sopenharmony_ci		return;
3148c2ecf20Sopenharmony_ci	if (iph->protocol != IPPROTO_TCP &&
3158c2ecf20Sopenharmony_ci	    iph->protocol != IPPROTO_UDP)
3168c2ecf20Sopenharmony_ci		return;
3178c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
3188c2ecf20Sopenharmony_ci	skb->csum_level = 1;
3198c2ecf20Sopenharmony_ci	skb->csum = 0;
3208c2ecf20Sopenharmony_ci	if (iph->protocol == IPPROTO_TCP) {
3218c2ecf20Sopenharmony_ci		struct tcphdr *ptcp = tcp_hdr(skb);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		ptcp->check = 0;
3248c2ecf20Sopenharmony_ci		skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
3258c2ecf20Sopenharmony_ci		ptcp->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
3268c2ecf20Sopenharmony_ci						skb->len - offset, IPPROTO_TCP,
3278c2ecf20Sopenharmony_ci						skb->csum);
3288c2ecf20Sopenharmony_ci	} else if (iph->protocol == IPPROTO_UDP) {
3298c2ecf20Sopenharmony_ci		struct udphdr *pudp = udp_hdr(skb);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		pudp->check = 0;
3328c2ecf20Sopenharmony_ci		skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
3338c2ecf20Sopenharmony_ci		pudp->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
3348c2ecf20Sopenharmony_ci						skb->len - offset, IPPROTO_UDP,
3358c2ecf20Sopenharmony_ci						skb->csum);
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
3408c2ecf20Sopenharmony_cistatic inline void vnet_fullcsum_ipv6(struct sk_buff *skb)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct ipv6hdr *ip6h = ipv6_hdr(skb);
3438c2ecf20Sopenharmony_ci	int offset = skb_transport_offset(skb);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (skb->protocol != htons(ETH_P_IPV6))
3468c2ecf20Sopenharmony_ci		return;
3478c2ecf20Sopenharmony_ci	if (ip6h->nexthdr != IPPROTO_TCP &&
3488c2ecf20Sopenharmony_ci	    ip6h->nexthdr != IPPROTO_UDP)
3498c2ecf20Sopenharmony_ci		return;
3508c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
3518c2ecf20Sopenharmony_ci	skb->csum_level = 1;
3528c2ecf20Sopenharmony_ci	skb->csum = 0;
3538c2ecf20Sopenharmony_ci	if (ip6h->nexthdr == IPPROTO_TCP) {
3548c2ecf20Sopenharmony_ci		struct tcphdr *ptcp = tcp_hdr(skb);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		ptcp->check = 0;
3578c2ecf20Sopenharmony_ci		skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
3588c2ecf20Sopenharmony_ci		ptcp->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
3598c2ecf20Sopenharmony_ci					      skb->len - offset, IPPROTO_TCP,
3608c2ecf20Sopenharmony_ci					      skb->csum);
3618c2ecf20Sopenharmony_ci	} else if (ip6h->nexthdr == IPPROTO_UDP) {
3628c2ecf20Sopenharmony_ci		struct udphdr *pudp = udp_hdr(skb);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		pudp->check = 0;
3658c2ecf20Sopenharmony_ci		skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
3668c2ecf20Sopenharmony_ci		pudp->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
3678c2ecf20Sopenharmony_ci					      skb->len - offset, IPPROTO_UDP,
3688c2ecf20Sopenharmony_ci					      skb->csum);
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci#endif
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port);
3768c2ecf20Sopenharmony_ci	unsigned int len = desc->size;
3778c2ecf20Sopenharmony_ci	unsigned int copy_len;
3788c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3798c2ecf20Sopenharmony_ci	int maxlen;
3808c2ecf20Sopenharmony_ci	int err;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	err = -EMSGSIZE;
3838c2ecf20Sopenharmony_ci	if (port->tso && port->tsolen > port->rmtu)
3848c2ecf20Sopenharmony_ci		maxlen = port->tsolen;
3858c2ecf20Sopenharmony_ci	else
3868c2ecf20Sopenharmony_ci		maxlen = port->rmtu;
3878c2ecf20Sopenharmony_ci	if (unlikely(len < ETH_ZLEN || len > maxlen)) {
3888c2ecf20Sopenharmony_ci		dev->stats.rx_length_errors++;
3898c2ecf20Sopenharmony_ci		goto out_dropped;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	skb = alloc_and_align_skb(dev, len);
3938c2ecf20Sopenharmony_ci	err = -ENOMEM;
3948c2ecf20Sopenharmony_ci	if (unlikely(!skb)) {
3958c2ecf20Sopenharmony_ci		dev->stats.rx_missed_errors++;
3968c2ecf20Sopenharmony_ci		goto out_dropped;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	copy_len = (len + VNET_PACKET_SKIP + 7U) & ~7U;
4008c2ecf20Sopenharmony_ci	skb_put(skb, copy_len);
4018c2ecf20Sopenharmony_ci	err = ldc_copy(port->vio.lp, LDC_COPY_IN,
4028c2ecf20Sopenharmony_ci		       skb->data, copy_len, 0,
4038c2ecf20Sopenharmony_ci		       desc->cookies, desc->ncookies);
4048c2ecf20Sopenharmony_ci	if (unlikely(err < 0)) {
4058c2ecf20Sopenharmony_ci		dev->stats.rx_frame_errors++;
4068c2ecf20Sopenharmony_ci		goto out_free_skb;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	skb_pull(skb, VNET_PACKET_SKIP);
4108c2ecf20Sopenharmony_ci	skb_trim(skb, len);
4118c2ecf20Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (vio_version_after_eq(&port->vio, 1, 8)) {
4148c2ecf20Sopenharmony_ci		struct vio_net_dext *dext = vio_net_ext(desc);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		skb_reset_network_header(skb);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		if (dext->flags & VNET_PKT_HCK_IPV4_HDRCKSUM) {
4198c2ecf20Sopenharmony_ci			if (skb->protocol == ETH_P_IP) {
4208c2ecf20Sopenharmony_ci				struct iphdr *iph = ip_hdr(skb);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci				iph->check = 0;
4238c2ecf20Sopenharmony_ci				ip_send_check(iph);
4248c2ecf20Sopenharmony_ci			}
4258c2ecf20Sopenharmony_ci		}
4268c2ecf20Sopenharmony_ci		if ((dext->flags & VNET_PKT_HCK_FULLCKSUM) &&
4278c2ecf20Sopenharmony_ci		    skb->ip_summed == CHECKSUM_NONE) {
4288c2ecf20Sopenharmony_ci			if (skb->protocol == htons(ETH_P_IP)) {
4298c2ecf20Sopenharmony_ci				struct iphdr *iph = ip_hdr(skb);
4308c2ecf20Sopenharmony_ci				int ihl = iph->ihl * 4;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci				skb_set_transport_header(skb, ihl);
4338c2ecf20Sopenharmony_ci				vnet_fullcsum_ipv4(skb);
4348c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
4358c2ecf20Sopenharmony_ci			} else if (skb->protocol == htons(ETH_P_IPV6)) {
4368c2ecf20Sopenharmony_ci				skb_set_transport_header(skb,
4378c2ecf20Sopenharmony_ci							 sizeof(struct ipv6hdr));
4388c2ecf20Sopenharmony_ci				vnet_fullcsum_ipv6(skb);
4398c2ecf20Sopenharmony_ci#endif
4408c2ecf20Sopenharmony_ci			}
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci		if (dext->flags & VNET_PKT_HCK_IPV4_HDRCKSUM_OK) {
4438c2ecf20Sopenharmony_ci			skb->ip_summed = CHECKSUM_PARTIAL;
4448c2ecf20Sopenharmony_ci			skb->csum_level = 0;
4458c2ecf20Sopenharmony_ci			if (dext->flags & VNET_PKT_HCK_FULLCKSUM_OK)
4468c2ecf20Sopenharmony_ci				skb->csum_level = 1;
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	skb->ip_summed = port->switch_port ? CHECKSUM_NONE : CHECKSUM_PARTIAL;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (unlikely(is_multicast_ether_addr(eth_hdr(skb)->h_dest)))
4538c2ecf20Sopenharmony_ci		dev->stats.multicast++;
4548c2ecf20Sopenharmony_ci	dev->stats.rx_packets++;
4558c2ecf20Sopenharmony_ci	dev->stats.rx_bytes += len;
4568c2ecf20Sopenharmony_ci	port->stats.rx_packets++;
4578c2ecf20Sopenharmony_ci	port->stats.rx_bytes += len;
4588c2ecf20Sopenharmony_ci	napi_gro_receive(&port->napi, skb);
4598c2ecf20Sopenharmony_ci	return 0;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ciout_free_skb:
4628c2ecf20Sopenharmony_ci	kfree_skb(skb);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ciout_dropped:
4658c2ecf20Sopenharmony_ci	dev->stats.rx_dropped++;
4668c2ecf20Sopenharmony_ci	return err;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic int vnet_send_ack(struct vnet_port *port, struct vio_dring_state *dr,
4708c2ecf20Sopenharmony_ci			 u32 start, u32 end, u8 vio_dring_state)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	struct vio_dring_data hdr = {
4738c2ecf20Sopenharmony_ci		.tag = {
4748c2ecf20Sopenharmony_ci			.type		= VIO_TYPE_DATA,
4758c2ecf20Sopenharmony_ci			.stype		= VIO_SUBTYPE_ACK,
4768c2ecf20Sopenharmony_ci			.stype_env	= VIO_DRING_DATA,
4778c2ecf20Sopenharmony_ci			.sid		= vio_send_sid(&port->vio),
4788c2ecf20Sopenharmony_ci		},
4798c2ecf20Sopenharmony_ci		.dring_ident		= dr->ident,
4808c2ecf20Sopenharmony_ci		.start_idx		= start,
4818c2ecf20Sopenharmony_ci		.end_idx		= end,
4828c2ecf20Sopenharmony_ci		.state			= vio_dring_state,
4838c2ecf20Sopenharmony_ci	};
4848c2ecf20Sopenharmony_ci	int err, delay;
4858c2ecf20Sopenharmony_ci	int retries = 0;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	hdr.seq = dr->snd_nxt;
4888c2ecf20Sopenharmony_ci	delay = 1;
4898c2ecf20Sopenharmony_ci	do {
4908c2ecf20Sopenharmony_ci		err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr));
4918c2ecf20Sopenharmony_ci		if (err > 0) {
4928c2ecf20Sopenharmony_ci			dr->snd_nxt++;
4938c2ecf20Sopenharmony_ci			break;
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci		udelay(delay);
4968c2ecf20Sopenharmony_ci		if ((delay <<= 1) > 128)
4978c2ecf20Sopenharmony_ci			delay = 128;
4988c2ecf20Sopenharmony_ci		if (retries++ > VNET_MAX_RETRIES) {
4998c2ecf20Sopenharmony_ci			pr_info("ECONNRESET %x:%x:%x:%x:%x:%x\n",
5008c2ecf20Sopenharmony_ci				port->raddr[0], port->raddr[1],
5018c2ecf20Sopenharmony_ci				port->raddr[2], port->raddr[3],
5028c2ecf20Sopenharmony_ci				port->raddr[4], port->raddr[5]);
5038c2ecf20Sopenharmony_ci			break;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci	} while (err == -EAGAIN);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (err <= 0 && vio_dring_state == VIO_DRING_STOPPED) {
5088c2ecf20Sopenharmony_ci		port->stop_rx_idx = end;
5098c2ecf20Sopenharmony_ci		port->stop_rx = true;
5108c2ecf20Sopenharmony_ci	} else {
5118c2ecf20Sopenharmony_ci		port->stop_rx_idx = 0;
5128c2ecf20Sopenharmony_ci		port->stop_rx = false;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	return err;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic struct vio_net_desc *get_rx_desc(struct vnet_port *port,
5198c2ecf20Sopenharmony_ci					struct vio_dring_state *dr,
5208c2ecf20Sopenharmony_ci					u32 index)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct vio_net_desc *desc = port->vio.desc_buf;
5238c2ecf20Sopenharmony_ci	int err;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	err = ldc_get_dring_entry(port->vio.lp, desc, dr->entry_size,
5268c2ecf20Sopenharmony_ci				  (index * dr->entry_size),
5278c2ecf20Sopenharmony_ci				  dr->cookies, dr->ncookies);
5288c2ecf20Sopenharmony_ci	if (err < 0)
5298c2ecf20Sopenharmony_ci		return ERR_PTR(err);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	return desc;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic int put_rx_desc(struct vnet_port *port,
5358c2ecf20Sopenharmony_ci		       struct vio_dring_state *dr,
5368c2ecf20Sopenharmony_ci		       struct vio_net_desc *desc,
5378c2ecf20Sopenharmony_ci		       u32 index)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	int err;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	err = ldc_put_dring_entry(port->vio.lp, desc, dr->entry_size,
5428c2ecf20Sopenharmony_ci				  (index * dr->entry_size),
5438c2ecf20Sopenharmony_ci				  dr->cookies, dr->ncookies);
5448c2ecf20Sopenharmony_ci	if (err < 0)
5458c2ecf20Sopenharmony_ci		return err;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return 0;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int vnet_walk_rx_one(struct vnet_port *port,
5518c2ecf20Sopenharmony_ci			    struct vio_dring_state *dr,
5528c2ecf20Sopenharmony_ci			    u32 index, int *needs_ack)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct vio_net_desc *desc = get_rx_desc(port, dr, index);
5558c2ecf20Sopenharmony_ci	struct vio_driver_state *vio = &port->vio;
5568c2ecf20Sopenharmony_ci	int err;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	BUG_ON(!desc);
5598c2ecf20Sopenharmony_ci	if (IS_ERR(desc))
5608c2ecf20Sopenharmony_ci		return PTR_ERR(desc);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (desc->hdr.state != VIO_DESC_READY)
5638c2ecf20Sopenharmony_ci		return 1;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	dma_rmb();
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%llx:%llx]\n",
5688c2ecf20Sopenharmony_ci	       desc->hdr.state, desc->hdr.ack,
5698c2ecf20Sopenharmony_ci	       desc->size, desc->ncookies,
5708c2ecf20Sopenharmony_ci	       desc->cookies[0].cookie_addr,
5718c2ecf20Sopenharmony_ci	       desc->cookies[0].cookie_size);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	err = vnet_rx_one(port, desc);
5748c2ecf20Sopenharmony_ci	if (err == -ECONNRESET)
5758c2ecf20Sopenharmony_ci		return err;
5768c2ecf20Sopenharmony_ci	trace_vnet_rx_one(port->vio._local_sid, port->vio._peer_sid,
5778c2ecf20Sopenharmony_ci			  index, desc->hdr.ack);
5788c2ecf20Sopenharmony_ci	desc->hdr.state = VIO_DESC_DONE;
5798c2ecf20Sopenharmony_ci	err = put_rx_desc(port, dr, desc, index);
5808c2ecf20Sopenharmony_ci	if (err < 0)
5818c2ecf20Sopenharmony_ci		return err;
5828c2ecf20Sopenharmony_ci	*needs_ack = desc->hdr.ack;
5838c2ecf20Sopenharmony_ci	return 0;
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cistatic int vnet_walk_rx(struct vnet_port *port, struct vio_dring_state *dr,
5878c2ecf20Sopenharmony_ci			u32 start, u32 end, int *npkts, int budget)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct vio_driver_state *vio = &port->vio;
5908c2ecf20Sopenharmony_ci	int ack_start = -1, ack_end = -1;
5918c2ecf20Sopenharmony_ci	bool send_ack = true;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	end = (end == (u32)-1) ? vio_dring_prev(dr, start)
5948c2ecf20Sopenharmony_ci			       : vio_dring_next(dr, end);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	viodbg(DATA, "vnet_walk_rx start[%08x] end[%08x]\n", start, end);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	while (start != end) {
5998c2ecf20Sopenharmony_ci		int ack = 0, err = vnet_walk_rx_one(port, dr, start, &ack);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		if (err == -ECONNRESET)
6028c2ecf20Sopenharmony_ci			return err;
6038c2ecf20Sopenharmony_ci		if (err != 0)
6048c2ecf20Sopenharmony_ci			break;
6058c2ecf20Sopenharmony_ci		(*npkts)++;
6068c2ecf20Sopenharmony_ci		if (ack_start == -1)
6078c2ecf20Sopenharmony_ci			ack_start = start;
6088c2ecf20Sopenharmony_ci		ack_end = start;
6098c2ecf20Sopenharmony_ci		start = vio_dring_next(dr, start);
6108c2ecf20Sopenharmony_ci		if (ack && start != end) {
6118c2ecf20Sopenharmony_ci			err = vnet_send_ack(port, dr, ack_start, ack_end,
6128c2ecf20Sopenharmony_ci					    VIO_DRING_ACTIVE);
6138c2ecf20Sopenharmony_ci			if (err == -ECONNRESET)
6148c2ecf20Sopenharmony_ci				return err;
6158c2ecf20Sopenharmony_ci			ack_start = -1;
6168c2ecf20Sopenharmony_ci		}
6178c2ecf20Sopenharmony_ci		if ((*npkts) >= budget) {
6188c2ecf20Sopenharmony_ci			send_ack = false;
6198c2ecf20Sopenharmony_ci			break;
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci	if (unlikely(ack_start == -1)) {
6238c2ecf20Sopenharmony_ci		ack_end = vio_dring_prev(dr, start);
6248c2ecf20Sopenharmony_ci		ack_start = ack_end;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci	if (send_ack) {
6278c2ecf20Sopenharmony_ci		port->napi_resume = false;
6288c2ecf20Sopenharmony_ci		trace_vnet_tx_send_stopped_ack(port->vio._local_sid,
6298c2ecf20Sopenharmony_ci					       port->vio._peer_sid,
6308c2ecf20Sopenharmony_ci					       ack_end, *npkts);
6318c2ecf20Sopenharmony_ci		return vnet_send_ack(port, dr, ack_start, ack_end,
6328c2ecf20Sopenharmony_ci				     VIO_DRING_STOPPED);
6338c2ecf20Sopenharmony_ci	} else  {
6348c2ecf20Sopenharmony_ci		trace_vnet_tx_defer_stopped_ack(port->vio._local_sid,
6358c2ecf20Sopenharmony_ci						port->vio._peer_sid,
6368c2ecf20Sopenharmony_ci						ack_end, *npkts);
6378c2ecf20Sopenharmony_ci		port->napi_resume = true;
6388c2ecf20Sopenharmony_ci		port->napi_stop_idx = ack_end;
6398c2ecf20Sopenharmony_ci		return 1;
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic int vnet_rx(struct vnet_port *port, void *msgbuf, int *npkts,
6448c2ecf20Sopenharmony_ci		   int budget)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct vio_dring_data *pkt = msgbuf;
6478c2ecf20Sopenharmony_ci	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_RX_RING];
6488c2ecf20Sopenharmony_ci	struct vio_driver_state *vio = &port->vio;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	viodbg(DATA, "vnet_rx stype_env[%04x] seq[%016llx] rcv_nxt[%016llx]\n",
6518c2ecf20Sopenharmony_ci	       pkt->tag.stype_env, pkt->seq, dr->rcv_nxt);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA))
6548c2ecf20Sopenharmony_ci		return 0;
6558c2ecf20Sopenharmony_ci	if (unlikely(pkt->seq != dr->rcv_nxt)) {
6568c2ecf20Sopenharmony_ci		pr_err("RX out of sequence seq[0x%llx] rcv_nxt[0x%llx]\n",
6578c2ecf20Sopenharmony_ci		       pkt->seq, dr->rcv_nxt);
6588c2ecf20Sopenharmony_ci		return 0;
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (!port->napi_resume)
6628c2ecf20Sopenharmony_ci		dr->rcv_nxt++;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	/* XXX Validate pkt->start_idx and pkt->end_idx XXX */
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return vnet_walk_rx(port, dr, pkt->start_idx, pkt->end_idx,
6678c2ecf20Sopenharmony_ci			    npkts, budget);
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic int idx_is_pending(struct vio_dring_state *dr, u32 end)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	u32 idx = dr->cons;
6738c2ecf20Sopenharmony_ci	int found = 0;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	while (idx != dr->prod) {
6768c2ecf20Sopenharmony_ci		if (idx == end) {
6778c2ecf20Sopenharmony_ci			found = 1;
6788c2ecf20Sopenharmony_ci			break;
6798c2ecf20Sopenharmony_ci		}
6808c2ecf20Sopenharmony_ci		idx = vio_dring_next(dr, idx);
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci	return found;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic int vnet_ack(struct vnet_port *port, void *msgbuf)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];
6888c2ecf20Sopenharmony_ci	struct vio_dring_data *pkt = msgbuf;
6898c2ecf20Sopenharmony_ci	struct net_device *dev;
6908c2ecf20Sopenharmony_ci	u32 end;
6918c2ecf20Sopenharmony_ci	struct vio_net_desc *desc;
6928c2ecf20Sopenharmony_ci	struct netdev_queue *txq;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA))
6958c2ecf20Sopenharmony_ci		return 0;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	end = pkt->end_idx;
6988c2ecf20Sopenharmony_ci	dev = VNET_PORT_TO_NET_DEVICE(port);
6998c2ecf20Sopenharmony_ci	netif_tx_lock(dev);
7008c2ecf20Sopenharmony_ci	if (unlikely(!idx_is_pending(dr, end))) {
7018c2ecf20Sopenharmony_ci		netif_tx_unlock(dev);
7028c2ecf20Sopenharmony_ci		return 0;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* sync for race conditions with vnet_start_xmit() and tell xmit it
7068c2ecf20Sopenharmony_ci	 * is time to send a trigger.
7078c2ecf20Sopenharmony_ci	 */
7088c2ecf20Sopenharmony_ci	trace_vnet_rx_stopped_ack(port->vio._local_sid,
7098c2ecf20Sopenharmony_ci				  port->vio._peer_sid, end);
7108c2ecf20Sopenharmony_ci	dr->cons = vio_dring_next(dr, end);
7118c2ecf20Sopenharmony_ci	desc = vio_dring_entry(dr, dr->cons);
7128c2ecf20Sopenharmony_ci	if (desc->hdr.state == VIO_DESC_READY && !port->start_cons) {
7138c2ecf20Sopenharmony_ci		/* vnet_start_xmit() just populated this dring but missed
7148c2ecf20Sopenharmony_ci		 * sending the "start" LDC message to the consumer.
7158c2ecf20Sopenharmony_ci		 * Send a "start" trigger on its behalf.
7168c2ecf20Sopenharmony_ci		 */
7178c2ecf20Sopenharmony_ci		if (__vnet_tx_trigger(port, dr->cons) > 0)
7188c2ecf20Sopenharmony_ci			port->start_cons = false;
7198c2ecf20Sopenharmony_ci		else
7208c2ecf20Sopenharmony_ci			port->start_cons = true;
7218c2ecf20Sopenharmony_ci	} else {
7228c2ecf20Sopenharmony_ci		port->start_cons = true;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci	netif_tx_unlock(dev);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	txq = netdev_get_tx_queue(dev, port->q_index);
7278c2ecf20Sopenharmony_ci	if (unlikely(netif_tx_queue_stopped(txq) &&
7288c2ecf20Sopenharmony_ci		     vnet_tx_dring_avail(dr) >= VNET_TX_WAKEUP_THRESH(dr)))
7298c2ecf20Sopenharmony_ci		return 1;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return 0;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic int vnet_nack(struct vnet_port *port, void *msgbuf)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	/* XXX just reset or similar XXX */
7378c2ecf20Sopenharmony_ci	return 0;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic int handle_mcast(struct vnet_port *port, void *msgbuf)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct vio_net_mcast_info *pkt = msgbuf;
7438c2ecf20Sopenharmony_ci	struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	if (pkt->tag.stype != VIO_SUBTYPE_ACK)
7468c2ecf20Sopenharmony_ci		pr_err("%s: Got unexpected MCAST reply [%02x:%02x:%04x:%08x]\n",
7478c2ecf20Sopenharmony_ci		       dev->name,
7488c2ecf20Sopenharmony_ci		       pkt->tag.type,
7498c2ecf20Sopenharmony_ci		       pkt->tag.stype,
7508c2ecf20Sopenharmony_ci		       pkt->tag.stype_env,
7518c2ecf20Sopenharmony_ci		       pkt->tag.sid);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	return 0;
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci/* If the queue is stopped, wake it up so that we'll
7578c2ecf20Sopenharmony_ci * send out another START message at the next TX.
7588c2ecf20Sopenharmony_ci */
7598c2ecf20Sopenharmony_cistatic void maybe_tx_wakeup(struct vnet_port *port)
7608c2ecf20Sopenharmony_ci{
7618c2ecf20Sopenharmony_ci	struct netdev_queue *txq;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	txq = netdev_get_tx_queue(VNET_PORT_TO_NET_DEVICE(port),
7648c2ecf20Sopenharmony_ci				  port->q_index);
7658c2ecf20Sopenharmony_ci	__netif_tx_lock(txq, smp_processor_id());
7668c2ecf20Sopenharmony_ci	if (likely(netif_tx_queue_stopped(txq)))
7678c2ecf20Sopenharmony_ci		netif_tx_wake_queue(txq);
7688c2ecf20Sopenharmony_ci	__netif_tx_unlock(txq);
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cibool sunvnet_port_is_up_common(struct vnet_port *vnet)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct vio_driver_state *vio = &vnet->vio;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	return !!(vio->hs_state & VIO_HS_COMPLETE);
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_port_is_up_common);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic int vnet_event_napi(struct vnet_port *port, int budget)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port);
7828c2ecf20Sopenharmony_ci	struct vio_driver_state *vio = &port->vio;
7838c2ecf20Sopenharmony_ci	int tx_wakeup, err;
7848c2ecf20Sopenharmony_ci	int npkts = 0;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	/* we don't expect any other bits */
7878c2ecf20Sopenharmony_ci	BUG_ON(port->rx_event & ~(LDC_EVENT_DATA_READY |
7888c2ecf20Sopenharmony_ci				  LDC_EVENT_RESET |
7898c2ecf20Sopenharmony_ci				  LDC_EVENT_UP));
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	/* RESET takes precedent over any other event */
7928c2ecf20Sopenharmony_ci	if (port->rx_event & LDC_EVENT_RESET) {
7938c2ecf20Sopenharmony_ci		/* a link went down */
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci		if (port->vsw == 1) {
7968c2ecf20Sopenharmony_ci			netif_tx_stop_all_queues(dev);
7978c2ecf20Sopenharmony_ci			netif_carrier_off(dev);
7988c2ecf20Sopenharmony_ci		}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci		vio_link_state_change(vio, LDC_EVENT_RESET);
8018c2ecf20Sopenharmony_ci		vnet_port_reset(port);
8028c2ecf20Sopenharmony_ci		vio_port_up(vio);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci		/* If the device is running but its tx queue was
8058c2ecf20Sopenharmony_ci		 * stopped (due to flow control), restart it.
8068c2ecf20Sopenharmony_ci		 * This is necessary since vnet_port_reset()
8078c2ecf20Sopenharmony_ci		 * clears the tx drings and thus we may never get
8088c2ecf20Sopenharmony_ci		 * back a VIO_TYPE_DATA ACK packet - which is
8098c2ecf20Sopenharmony_ci		 * the normal mechanism to restart the tx queue.
8108c2ecf20Sopenharmony_ci		 */
8118c2ecf20Sopenharmony_ci		if (netif_running(dev))
8128c2ecf20Sopenharmony_ci			maybe_tx_wakeup(port);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		port->rx_event = 0;
8158c2ecf20Sopenharmony_ci		port->stats.event_reset++;
8168c2ecf20Sopenharmony_ci		return 0;
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	if (port->rx_event & LDC_EVENT_UP) {
8208c2ecf20Sopenharmony_ci		/* a link came up */
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci		if (port->vsw == 1) {
8238c2ecf20Sopenharmony_ci			netif_carrier_on(port->dev);
8248c2ecf20Sopenharmony_ci			netif_tx_start_all_queues(port->dev);
8258c2ecf20Sopenharmony_ci		}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		vio_link_state_change(vio, LDC_EVENT_UP);
8288c2ecf20Sopenharmony_ci		port->rx_event = 0;
8298c2ecf20Sopenharmony_ci		port->stats.event_up++;
8308c2ecf20Sopenharmony_ci		return 0;
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	err = 0;
8348c2ecf20Sopenharmony_ci	tx_wakeup = 0;
8358c2ecf20Sopenharmony_ci	while (1) {
8368c2ecf20Sopenharmony_ci		union {
8378c2ecf20Sopenharmony_ci			struct vio_msg_tag tag;
8388c2ecf20Sopenharmony_ci			u64 raw[8];
8398c2ecf20Sopenharmony_ci		} msgbuf;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci		if (port->napi_resume) {
8428c2ecf20Sopenharmony_ci			struct vio_dring_data *pkt =
8438c2ecf20Sopenharmony_ci				(struct vio_dring_data *)&msgbuf;
8448c2ecf20Sopenharmony_ci			struct vio_dring_state *dr =
8458c2ecf20Sopenharmony_ci				&port->vio.drings[VIO_DRIVER_RX_RING];
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci			pkt->tag.type = VIO_TYPE_DATA;
8488c2ecf20Sopenharmony_ci			pkt->tag.stype = VIO_SUBTYPE_INFO;
8498c2ecf20Sopenharmony_ci			pkt->tag.stype_env = VIO_DRING_DATA;
8508c2ecf20Sopenharmony_ci			pkt->seq = dr->rcv_nxt;
8518c2ecf20Sopenharmony_ci			pkt->start_idx = vio_dring_next(dr,
8528c2ecf20Sopenharmony_ci							port->napi_stop_idx);
8538c2ecf20Sopenharmony_ci			pkt->end_idx = -1;
8548c2ecf20Sopenharmony_ci		} else {
8558c2ecf20Sopenharmony_ci			err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf));
8568c2ecf20Sopenharmony_ci			if (unlikely(err < 0)) {
8578c2ecf20Sopenharmony_ci				if (err == -ECONNRESET)
8588c2ecf20Sopenharmony_ci					vio_conn_reset(vio);
8598c2ecf20Sopenharmony_ci				break;
8608c2ecf20Sopenharmony_ci			}
8618c2ecf20Sopenharmony_ci			if (err == 0)
8628c2ecf20Sopenharmony_ci				break;
8638c2ecf20Sopenharmony_ci			viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n",
8648c2ecf20Sopenharmony_ci			       msgbuf.tag.type,
8658c2ecf20Sopenharmony_ci			       msgbuf.tag.stype,
8668c2ecf20Sopenharmony_ci			       msgbuf.tag.stype_env,
8678c2ecf20Sopenharmony_ci			       msgbuf.tag.sid);
8688c2ecf20Sopenharmony_ci			err = vio_validate_sid(vio, &msgbuf.tag);
8698c2ecf20Sopenharmony_ci			if (err < 0)
8708c2ecf20Sopenharmony_ci				break;
8718c2ecf20Sopenharmony_ci		}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci		if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) {
8748c2ecf20Sopenharmony_ci			if (msgbuf.tag.stype == VIO_SUBTYPE_INFO) {
8758c2ecf20Sopenharmony_ci				if (!sunvnet_port_is_up_common(port)) {
8768c2ecf20Sopenharmony_ci					/* failures like handshake_failure()
8778c2ecf20Sopenharmony_ci					 * may have cleaned up dring, but
8788c2ecf20Sopenharmony_ci					 * NAPI polling may bring us here.
8798c2ecf20Sopenharmony_ci					 */
8808c2ecf20Sopenharmony_ci					err = -ECONNRESET;
8818c2ecf20Sopenharmony_ci					break;
8828c2ecf20Sopenharmony_ci				}
8838c2ecf20Sopenharmony_ci				err = vnet_rx(port, &msgbuf, &npkts, budget);
8848c2ecf20Sopenharmony_ci				if (npkts >= budget)
8858c2ecf20Sopenharmony_ci					break;
8868c2ecf20Sopenharmony_ci				if (npkts == 0)
8878c2ecf20Sopenharmony_ci					break;
8888c2ecf20Sopenharmony_ci			} else if (msgbuf.tag.stype == VIO_SUBTYPE_ACK) {
8898c2ecf20Sopenharmony_ci				err = vnet_ack(port, &msgbuf);
8908c2ecf20Sopenharmony_ci				if (err > 0)
8918c2ecf20Sopenharmony_ci					tx_wakeup |= err;
8928c2ecf20Sopenharmony_ci			} else if (msgbuf.tag.stype == VIO_SUBTYPE_NACK) {
8938c2ecf20Sopenharmony_ci				err = vnet_nack(port, &msgbuf);
8948c2ecf20Sopenharmony_ci			}
8958c2ecf20Sopenharmony_ci		} else if (msgbuf.tag.type == VIO_TYPE_CTRL) {
8968c2ecf20Sopenharmony_ci			if (msgbuf.tag.stype_env == VNET_MCAST_INFO)
8978c2ecf20Sopenharmony_ci				err = handle_mcast(port, &msgbuf);
8988c2ecf20Sopenharmony_ci			else
8998c2ecf20Sopenharmony_ci				err = vio_control_pkt_engine(vio, &msgbuf);
9008c2ecf20Sopenharmony_ci			if (err)
9018c2ecf20Sopenharmony_ci				break;
9028c2ecf20Sopenharmony_ci		} else {
9038c2ecf20Sopenharmony_ci			err = vnet_handle_unknown(port, &msgbuf);
9048c2ecf20Sopenharmony_ci		}
9058c2ecf20Sopenharmony_ci		if (err == -ECONNRESET)
9068c2ecf20Sopenharmony_ci			break;
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci	if (unlikely(tx_wakeup && err != -ECONNRESET))
9098c2ecf20Sopenharmony_ci		maybe_tx_wakeup(port);
9108c2ecf20Sopenharmony_ci	return npkts;
9118c2ecf20Sopenharmony_ci}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ciint sunvnet_poll_common(struct napi_struct *napi, int budget)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	struct vnet_port *port = container_of(napi, struct vnet_port, napi);
9168c2ecf20Sopenharmony_ci	struct vio_driver_state *vio = &port->vio;
9178c2ecf20Sopenharmony_ci	int processed = vnet_event_napi(port, budget);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	if (processed < budget) {
9208c2ecf20Sopenharmony_ci		napi_complete_done(napi, processed);
9218c2ecf20Sopenharmony_ci		port->rx_event &= ~LDC_EVENT_DATA_READY;
9228c2ecf20Sopenharmony_ci		vio_set_intr(vio->vdev->rx_ino, HV_INTR_ENABLED);
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci	return processed;
9258c2ecf20Sopenharmony_ci}
9268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_poll_common);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_civoid sunvnet_event_common(void *arg, int event)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	struct vnet_port *port = arg;
9318c2ecf20Sopenharmony_ci	struct vio_driver_state *vio = &port->vio;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	port->rx_event |= event;
9348c2ecf20Sopenharmony_ci	vio_set_intr(vio->vdev->rx_ino, HV_INTR_DISABLED);
9358c2ecf20Sopenharmony_ci	napi_schedule(&port->napi);
9368c2ecf20Sopenharmony_ci}
9378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_event_common);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_cistatic int __vnet_tx_trigger(struct vnet_port *port, u32 start)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];
9428c2ecf20Sopenharmony_ci	struct vio_dring_data hdr = {
9438c2ecf20Sopenharmony_ci		.tag = {
9448c2ecf20Sopenharmony_ci			.type		= VIO_TYPE_DATA,
9458c2ecf20Sopenharmony_ci			.stype		= VIO_SUBTYPE_INFO,
9468c2ecf20Sopenharmony_ci			.stype_env	= VIO_DRING_DATA,
9478c2ecf20Sopenharmony_ci			.sid		= vio_send_sid(&port->vio),
9488c2ecf20Sopenharmony_ci		},
9498c2ecf20Sopenharmony_ci		.dring_ident		= dr->ident,
9508c2ecf20Sopenharmony_ci		.start_idx		= start,
9518c2ecf20Sopenharmony_ci		.end_idx		= (u32)-1,
9528c2ecf20Sopenharmony_ci	};
9538c2ecf20Sopenharmony_ci	int err, delay;
9548c2ecf20Sopenharmony_ci	int retries = 0;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	if (port->stop_rx) {
9578c2ecf20Sopenharmony_ci		trace_vnet_tx_pending_stopped_ack(port->vio._local_sid,
9588c2ecf20Sopenharmony_ci						  port->vio._peer_sid,
9598c2ecf20Sopenharmony_ci						  port->stop_rx_idx, -1);
9608c2ecf20Sopenharmony_ci		err = vnet_send_ack(port,
9618c2ecf20Sopenharmony_ci				    &port->vio.drings[VIO_DRIVER_RX_RING],
9628c2ecf20Sopenharmony_ci				    port->stop_rx_idx, -1,
9638c2ecf20Sopenharmony_ci				    VIO_DRING_STOPPED);
9648c2ecf20Sopenharmony_ci		if (err <= 0)
9658c2ecf20Sopenharmony_ci			return err;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	hdr.seq = dr->snd_nxt;
9698c2ecf20Sopenharmony_ci	delay = 1;
9708c2ecf20Sopenharmony_ci	do {
9718c2ecf20Sopenharmony_ci		err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr));
9728c2ecf20Sopenharmony_ci		if (err > 0) {
9738c2ecf20Sopenharmony_ci			dr->snd_nxt++;
9748c2ecf20Sopenharmony_ci			break;
9758c2ecf20Sopenharmony_ci		}
9768c2ecf20Sopenharmony_ci		udelay(delay);
9778c2ecf20Sopenharmony_ci		if ((delay <<= 1) > 128)
9788c2ecf20Sopenharmony_ci			delay = 128;
9798c2ecf20Sopenharmony_ci		if (retries++ > VNET_MAX_RETRIES)
9808c2ecf20Sopenharmony_ci			break;
9818c2ecf20Sopenharmony_ci	} while (err == -EAGAIN);
9828c2ecf20Sopenharmony_ci	trace_vnet_tx_trigger(port->vio._local_sid,
9838c2ecf20Sopenharmony_ci			      port->vio._peer_sid, start, err);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	return err;
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_cistatic struct sk_buff *vnet_clean_tx_ring(struct vnet_port *port,
9898c2ecf20Sopenharmony_ci					  unsigned *pending)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];
9928c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
9938c2ecf20Sopenharmony_ci	int i, txi;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	*pending = 0;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	txi = dr->prod;
9988c2ecf20Sopenharmony_ci	for (i = 0; i < VNET_TX_RING_SIZE; ++i) {
9998c2ecf20Sopenharmony_ci		struct vio_net_desc *d;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci		--txi;
10028c2ecf20Sopenharmony_ci		if (txi < 0)
10038c2ecf20Sopenharmony_ci			txi = VNET_TX_RING_SIZE - 1;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci		d = vio_dring_entry(dr, txi);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci		if (d->hdr.state == VIO_DESC_READY) {
10088c2ecf20Sopenharmony_ci			(*pending)++;
10098c2ecf20Sopenharmony_ci			continue;
10108c2ecf20Sopenharmony_ci		}
10118c2ecf20Sopenharmony_ci		if (port->tx_bufs[txi].skb) {
10128c2ecf20Sopenharmony_ci			if (d->hdr.state != VIO_DESC_DONE)
10138c2ecf20Sopenharmony_ci				pr_notice("invalid ring buffer state %d\n",
10148c2ecf20Sopenharmony_ci					  d->hdr.state);
10158c2ecf20Sopenharmony_ci			BUG_ON(port->tx_bufs[txi].skb->next);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci			port->tx_bufs[txi].skb->next = skb;
10188c2ecf20Sopenharmony_ci			skb = port->tx_bufs[txi].skb;
10198c2ecf20Sopenharmony_ci			port->tx_bufs[txi].skb = NULL;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci			ldc_unmap(port->vio.lp,
10228c2ecf20Sopenharmony_ci				  port->tx_bufs[txi].cookies,
10238c2ecf20Sopenharmony_ci				  port->tx_bufs[txi].ncookies);
10248c2ecf20Sopenharmony_ci		} else if (d->hdr.state == VIO_DESC_FREE) {
10258c2ecf20Sopenharmony_ci			break;
10268c2ecf20Sopenharmony_ci		}
10278c2ecf20Sopenharmony_ci		d->hdr.state = VIO_DESC_FREE;
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci	return skb;
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistatic inline void vnet_free_skbs(struct sk_buff *skb)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	struct sk_buff *next;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	while (skb) {
10378c2ecf20Sopenharmony_ci		next = skb->next;
10388c2ecf20Sopenharmony_ci		skb->next = NULL;
10398c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
10408c2ecf20Sopenharmony_ci		skb = next;
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_civoid sunvnet_clean_timer_expire_common(struct timer_list *t)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	struct vnet_port *port = from_timer(port, t, clean_timer);
10478c2ecf20Sopenharmony_ci	struct sk_buff *freeskbs;
10488c2ecf20Sopenharmony_ci	unsigned pending;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	netif_tx_lock(VNET_PORT_TO_NET_DEVICE(port));
10518c2ecf20Sopenharmony_ci	freeskbs = vnet_clean_tx_ring(port, &pending);
10528c2ecf20Sopenharmony_ci	netif_tx_unlock(VNET_PORT_TO_NET_DEVICE(port));
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	vnet_free_skbs(freeskbs);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	if (pending)
10578c2ecf20Sopenharmony_ci		(void)mod_timer(&port->clean_timer,
10588c2ecf20Sopenharmony_ci				jiffies + VNET_CLEAN_TIMEOUT);
10598c2ecf20Sopenharmony_ci	 else
10608c2ecf20Sopenharmony_ci		del_timer(&port->clean_timer);
10618c2ecf20Sopenharmony_ci}
10628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_clean_timer_expire_common);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic inline int vnet_skb_map(struct ldc_channel *lp, struct sk_buff *skb,
10658c2ecf20Sopenharmony_ci			       struct ldc_trans_cookie *cookies, int ncookies,
10668c2ecf20Sopenharmony_ci			       unsigned int map_perm)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	int i, nc, err, blen;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	/* header */
10718c2ecf20Sopenharmony_ci	blen = skb_headlen(skb);
10728c2ecf20Sopenharmony_ci	if (blen < ETH_ZLEN)
10738c2ecf20Sopenharmony_ci		blen = ETH_ZLEN;
10748c2ecf20Sopenharmony_ci	blen += VNET_PACKET_SKIP;
10758c2ecf20Sopenharmony_ci	blen += 8 - (blen & 7);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	err = ldc_map_single(lp, skb->data - VNET_PACKET_SKIP, blen, cookies,
10788c2ecf20Sopenharmony_ci			     ncookies, map_perm);
10798c2ecf20Sopenharmony_ci	if (err < 0)
10808c2ecf20Sopenharmony_ci		return err;
10818c2ecf20Sopenharmony_ci	nc = err;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
10848c2ecf20Sopenharmony_ci		skb_frag_t *f = &skb_shinfo(skb)->frags[i];
10858c2ecf20Sopenharmony_ci		u8 *vaddr;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci		if (nc < ncookies) {
10888c2ecf20Sopenharmony_ci			vaddr = kmap_atomic(skb_frag_page(f));
10898c2ecf20Sopenharmony_ci			blen = skb_frag_size(f);
10908c2ecf20Sopenharmony_ci			blen += 8 - (blen & 7);
10918c2ecf20Sopenharmony_ci			err = ldc_map_single(lp, vaddr + skb_frag_off(f),
10928c2ecf20Sopenharmony_ci					     blen, cookies + nc, ncookies - nc,
10938c2ecf20Sopenharmony_ci					     map_perm);
10948c2ecf20Sopenharmony_ci			kunmap_atomic(vaddr);
10958c2ecf20Sopenharmony_ci		} else {
10968c2ecf20Sopenharmony_ci			err = -EMSGSIZE;
10978c2ecf20Sopenharmony_ci		}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci		if (err < 0) {
11008c2ecf20Sopenharmony_ci			ldc_unmap(lp, cookies, nc);
11018c2ecf20Sopenharmony_ci			return err;
11028c2ecf20Sopenharmony_ci		}
11038c2ecf20Sopenharmony_ci		nc += err;
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci	return nc;
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, int ncookies)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
11118c2ecf20Sopenharmony_ci	int i, len, pad, docopy;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	len = skb->len;
11148c2ecf20Sopenharmony_ci	pad = 0;
11158c2ecf20Sopenharmony_ci	if (len < ETH_ZLEN) {
11168c2ecf20Sopenharmony_ci		pad += ETH_ZLEN - skb->len;
11178c2ecf20Sopenharmony_ci		len += pad;
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci	len += VNET_PACKET_SKIP;
11208c2ecf20Sopenharmony_ci	pad += 8 - (len & 7);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	/* make sure we have enough cookies and alignment in every frag */
11238c2ecf20Sopenharmony_ci	docopy = skb_shinfo(skb)->nr_frags >= ncookies;
11248c2ecf20Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
11258c2ecf20Sopenharmony_ci		skb_frag_t *f = &skb_shinfo(skb)->frags[i];
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci		docopy |= skb_frag_off(f) & 7;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci	if (((unsigned long)skb->data & 7) != VNET_PACKET_SKIP ||
11308c2ecf20Sopenharmony_ci	    skb_tailroom(skb) < pad ||
11318c2ecf20Sopenharmony_ci	    skb_headroom(skb) < VNET_PACKET_SKIP || docopy) {
11328c2ecf20Sopenharmony_ci		int start = 0, offset;
11338c2ecf20Sopenharmony_ci		__wsum csum;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci		len = skb->len > ETH_ZLEN ? skb->len : ETH_ZLEN;
11368c2ecf20Sopenharmony_ci		nskb = alloc_and_align_skb(skb->dev, len);
11378c2ecf20Sopenharmony_ci		if (!nskb) {
11388c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
11398c2ecf20Sopenharmony_ci			return NULL;
11408c2ecf20Sopenharmony_ci		}
11418c2ecf20Sopenharmony_ci		skb_reserve(nskb, VNET_PACKET_SKIP);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci		nskb->protocol = skb->protocol;
11448c2ecf20Sopenharmony_ci		offset = skb_mac_header(skb) - skb->data;
11458c2ecf20Sopenharmony_ci		skb_set_mac_header(nskb, offset);
11468c2ecf20Sopenharmony_ci		offset = skb_network_header(skb) - skb->data;
11478c2ecf20Sopenharmony_ci		skb_set_network_header(nskb, offset);
11488c2ecf20Sopenharmony_ci		offset = skb_transport_header(skb) - skb->data;
11498c2ecf20Sopenharmony_ci		skb_set_transport_header(nskb, offset);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci		offset = 0;
11528c2ecf20Sopenharmony_ci		nskb->csum_offset = skb->csum_offset;
11538c2ecf20Sopenharmony_ci		nskb->ip_summed = skb->ip_summed;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
11568c2ecf20Sopenharmony_ci			start = skb_checksum_start_offset(skb);
11578c2ecf20Sopenharmony_ci		if (start) {
11588c2ecf20Sopenharmony_ci			int offset = start + nskb->csum_offset;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci			/* copy the headers, no csum here */
11618c2ecf20Sopenharmony_ci			if (skb_copy_bits(skb, 0, nskb->data, start)) {
11628c2ecf20Sopenharmony_ci				dev_kfree_skb(nskb);
11638c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
11648c2ecf20Sopenharmony_ci				return NULL;
11658c2ecf20Sopenharmony_ci			}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci			/* copy the rest, with csum calculation */
11688c2ecf20Sopenharmony_ci			*(__sum16 *)(skb->data + offset) = 0;
11698c2ecf20Sopenharmony_ci			csum = skb_copy_and_csum_bits(skb, start,
11708c2ecf20Sopenharmony_ci						      nskb->data + start,
11718c2ecf20Sopenharmony_ci						      skb->len - start);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci			/* add in the header checksums */
11748c2ecf20Sopenharmony_ci			if (skb->protocol == htons(ETH_P_IP)) {
11758c2ecf20Sopenharmony_ci				struct iphdr *iph = ip_hdr(nskb);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci				if (iph->protocol == IPPROTO_TCP ||
11788c2ecf20Sopenharmony_ci				    iph->protocol == IPPROTO_UDP) {
11798c2ecf20Sopenharmony_ci					csum = csum_tcpudp_magic(iph->saddr,
11808c2ecf20Sopenharmony_ci								 iph->daddr,
11818c2ecf20Sopenharmony_ci								 skb->len - start,
11828c2ecf20Sopenharmony_ci								 iph->protocol,
11838c2ecf20Sopenharmony_ci								 csum);
11848c2ecf20Sopenharmony_ci				}
11858c2ecf20Sopenharmony_ci			} else if (skb->protocol == htons(ETH_P_IPV6)) {
11868c2ecf20Sopenharmony_ci				struct ipv6hdr *ip6h = ipv6_hdr(nskb);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci				if (ip6h->nexthdr == IPPROTO_TCP ||
11898c2ecf20Sopenharmony_ci				    ip6h->nexthdr == IPPROTO_UDP) {
11908c2ecf20Sopenharmony_ci					csum = csum_ipv6_magic(&ip6h->saddr,
11918c2ecf20Sopenharmony_ci							       &ip6h->daddr,
11928c2ecf20Sopenharmony_ci							       skb->len - start,
11938c2ecf20Sopenharmony_ci							       ip6h->nexthdr,
11948c2ecf20Sopenharmony_ci							       csum);
11958c2ecf20Sopenharmony_ci				}
11968c2ecf20Sopenharmony_ci			}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci			/* save the final result */
11998c2ecf20Sopenharmony_ci			*(__sum16 *)(nskb->data + offset) = csum;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci			nskb->ip_summed = CHECKSUM_NONE;
12028c2ecf20Sopenharmony_ci		} else if (skb_copy_bits(skb, 0, nskb->data, skb->len)) {
12038c2ecf20Sopenharmony_ci			dev_kfree_skb(nskb);
12048c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
12058c2ecf20Sopenharmony_ci			return NULL;
12068c2ecf20Sopenharmony_ci		}
12078c2ecf20Sopenharmony_ci		(void)skb_put(nskb, skb->len);
12088c2ecf20Sopenharmony_ci		if (skb_is_gso(skb)) {
12098c2ecf20Sopenharmony_ci			skb_shinfo(nskb)->gso_size = skb_shinfo(skb)->gso_size;
12108c2ecf20Sopenharmony_ci			skb_shinfo(nskb)->gso_type = skb_shinfo(skb)->gso_type;
12118c2ecf20Sopenharmony_ci		}
12128c2ecf20Sopenharmony_ci		nskb->queue_mapping = skb->queue_mapping;
12138c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
12148c2ecf20Sopenharmony_ci		skb = nskb;
12158c2ecf20Sopenharmony_ci	}
12168c2ecf20Sopenharmony_ci	return skb;
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic netdev_tx_t
12208c2ecf20Sopenharmony_civnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb,
12218c2ecf20Sopenharmony_ci		     struct vnet_port *(*vnet_tx_port)
12228c2ecf20Sopenharmony_ci		     (struct sk_buff *, struct net_device *))
12238c2ecf20Sopenharmony_ci{
12248c2ecf20Sopenharmony_ci	struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port);
12258c2ecf20Sopenharmony_ci	struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING];
12268c2ecf20Sopenharmony_ci	struct sk_buff *segs, *curr, *next;
12278c2ecf20Sopenharmony_ci	int maclen, datalen;
12288c2ecf20Sopenharmony_ci	int status;
12298c2ecf20Sopenharmony_ci	int gso_size, gso_type, gso_segs;
12308c2ecf20Sopenharmony_ci	int hlen = skb_transport_header(skb) - skb_mac_header(skb);
12318c2ecf20Sopenharmony_ci	int proto = IPPROTO_IP;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP))
12348c2ecf20Sopenharmony_ci		proto = ip_hdr(skb)->protocol;
12358c2ecf20Sopenharmony_ci	else if (skb->protocol == htons(ETH_P_IPV6))
12368c2ecf20Sopenharmony_ci		proto = ipv6_hdr(skb)->nexthdr;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	if (proto == IPPROTO_TCP) {
12398c2ecf20Sopenharmony_ci		hlen += tcp_hdr(skb)->doff * 4;
12408c2ecf20Sopenharmony_ci	} else if (proto == IPPROTO_UDP) {
12418c2ecf20Sopenharmony_ci		hlen += sizeof(struct udphdr);
12428c2ecf20Sopenharmony_ci	} else {
12438c2ecf20Sopenharmony_ci		pr_err("vnet_handle_offloads GSO with unknown transport "
12448c2ecf20Sopenharmony_ci		       "protocol %d tproto %d\n", skb->protocol, proto);
12458c2ecf20Sopenharmony_ci		hlen = 128; /* XXX */
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci	datalen = port->tsolen - hlen;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	gso_size = skb_shinfo(skb)->gso_size;
12508c2ecf20Sopenharmony_ci	gso_type = skb_shinfo(skb)->gso_type;
12518c2ecf20Sopenharmony_ci	gso_segs = skb_shinfo(skb)->gso_segs;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	if (port->tso && gso_size < datalen)
12548c2ecf20Sopenharmony_ci		gso_segs = DIV_ROUND_UP(skb->len - hlen, datalen);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	if (unlikely(vnet_tx_dring_avail(dr) < gso_segs)) {
12578c2ecf20Sopenharmony_ci		struct netdev_queue *txq;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci		txq  = netdev_get_tx_queue(dev, port->q_index);
12608c2ecf20Sopenharmony_ci		netif_tx_stop_queue(txq);
12618c2ecf20Sopenharmony_ci		if (vnet_tx_dring_avail(dr) < skb_shinfo(skb)->gso_segs)
12628c2ecf20Sopenharmony_ci			return NETDEV_TX_BUSY;
12638c2ecf20Sopenharmony_ci		netif_tx_wake_queue(txq);
12648c2ecf20Sopenharmony_ci	}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	maclen = skb_network_header(skb) - skb_mac_header(skb);
12678c2ecf20Sopenharmony_ci	skb_pull(skb, maclen);
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	if (port->tso && gso_size < datalen) {
12708c2ecf20Sopenharmony_ci		if (skb_unclone(skb, GFP_ATOMIC))
12718c2ecf20Sopenharmony_ci			goto out_dropped;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci		/* segment to TSO size */
12748c2ecf20Sopenharmony_ci		skb_shinfo(skb)->gso_size = datalen;
12758c2ecf20Sopenharmony_ci		skb_shinfo(skb)->gso_segs = gso_segs;
12768c2ecf20Sopenharmony_ci	}
12778c2ecf20Sopenharmony_ci	segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO);
12788c2ecf20Sopenharmony_ci	if (IS_ERR(segs))
12798c2ecf20Sopenharmony_ci		goto out_dropped;
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	skb_push(skb, maclen);
12828c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	status = 0;
12858c2ecf20Sopenharmony_ci	skb_list_walk_safe(segs, curr, next) {
12868c2ecf20Sopenharmony_ci		skb_mark_not_on_list(curr);
12878c2ecf20Sopenharmony_ci		if (port->tso && curr->len > dev->mtu) {
12888c2ecf20Sopenharmony_ci			skb_shinfo(curr)->gso_size = gso_size;
12898c2ecf20Sopenharmony_ci			skb_shinfo(curr)->gso_type = gso_type;
12908c2ecf20Sopenharmony_ci			skb_shinfo(curr)->gso_segs =
12918c2ecf20Sopenharmony_ci				DIV_ROUND_UP(curr->len - hlen, gso_size);
12928c2ecf20Sopenharmony_ci		} else {
12938c2ecf20Sopenharmony_ci			skb_shinfo(curr)->gso_size = 0;
12948c2ecf20Sopenharmony_ci		}
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci		skb_push(curr, maclen);
12978c2ecf20Sopenharmony_ci		skb_reset_mac_header(curr);
12988c2ecf20Sopenharmony_ci		memcpy(skb_mac_header(curr), skb_mac_header(skb),
12998c2ecf20Sopenharmony_ci		       maclen);
13008c2ecf20Sopenharmony_ci		curr->csum_start = skb_transport_header(curr) - curr->head;
13018c2ecf20Sopenharmony_ci		if (ip_hdr(curr)->protocol == IPPROTO_TCP)
13028c2ecf20Sopenharmony_ci			curr->csum_offset = offsetof(struct tcphdr, check);
13038c2ecf20Sopenharmony_ci		else if (ip_hdr(curr)->protocol == IPPROTO_UDP)
13048c2ecf20Sopenharmony_ci			curr->csum_offset = offsetof(struct udphdr, check);
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci		if (!(status & NETDEV_TX_MASK))
13078c2ecf20Sopenharmony_ci			status = sunvnet_start_xmit_common(curr, dev,
13088c2ecf20Sopenharmony_ci							   vnet_tx_port);
13098c2ecf20Sopenharmony_ci		if (status & NETDEV_TX_MASK)
13108c2ecf20Sopenharmony_ci			dev_kfree_skb_any(curr);
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	if (!(status & NETDEV_TX_MASK))
13148c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
13158c2ecf20Sopenharmony_ci	return status;
13168c2ecf20Sopenharmony_ciout_dropped:
13178c2ecf20Sopenharmony_ci	dev->stats.tx_dropped++;
13188c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
13198c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
13208c2ecf20Sopenharmony_ci}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_cinetdev_tx_t
13238c2ecf20Sopenharmony_cisunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev,
13248c2ecf20Sopenharmony_ci			  struct vnet_port *(*vnet_tx_port)
13258c2ecf20Sopenharmony_ci			  (struct sk_buff *, struct net_device *))
13268c2ecf20Sopenharmony_ci{
13278c2ecf20Sopenharmony_ci	struct vnet_port *port = NULL;
13288c2ecf20Sopenharmony_ci	struct vio_dring_state *dr;
13298c2ecf20Sopenharmony_ci	struct vio_net_desc *d;
13308c2ecf20Sopenharmony_ci	unsigned int len;
13318c2ecf20Sopenharmony_ci	struct sk_buff *freeskbs = NULL;
13328c2ecf20Sopenharmony_ci	int i, err, txi;
13338c2ecf20Sopenharmony_ci	unsigned pending = 0;
13348c2ecf20Sopenharmony_ci	struct netdev_queue *txq;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	rcu_read_lock();
13378c2ecf20Sopenharmony_ci	port = vnet_tx_port(skb, dev);
13388c2ecf20Sopenharmony_ci	if (unlikely(!port))
13398c2ecf20Sopenharmony_ci		goto out_dropped;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	if (skb_is_gso(skb) && skb->len > port->tsolen) {
13428c2ecf20Sopenharmony_ci		err = vnet_handle_offloads(port, skb, vnet_tx_port);
13438c2ecf20Sopenharmony_ci		rcu_read_unlock();
13448c2ecf20Sopenharmony_ci		return err;
13458c2ecf20Sopenharmony_ci	}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	if (!skb_is_gso(skb) && skb->len > port->rmtu) {
13488c2ecf20Sopenharmony_ci		unsigned long localmtu = port->rmtu - ETH_HLEN;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci		if (vio_version_after_eq(&port->vio, 1, 3))
13518c2ecf20Sopenharmony_ci			localmtu -= VLAN_HLEN;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci		if (skb->protocol == htons(ETH_P_IP))
13548c2ecf20Sopenharmony_ci			icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
13558c2ecf20Sopenharmony_ci				      htonl(localmtu));
13568c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
13578c2ecf20Sopenharmony_ci		else if (skb->protocol == htons(ETH_P_IPV6))
13588c2ecf20Sopenharmony_ci			icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, localmtu);
13598c2ecf20Sopenharmony_ci#endif
13608c2ecf20Sopenharmony_ci		goto out_dropped;
13618c2ecf20Sopenharmony_ci	}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	skb = vnet_skb_shape(skb, 2);
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	if (unlikely(!skb))
13668c2ecf20Sopenharmony_ci		goto out_dropped;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
13698c2ecf20Sopenharmony_ci		if (skb->protocol == htons(ETH_P_IP))
13708c2ecf20Sopenharmony_ci			vnet_fullcsum_ipv4(skb);
13718c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
13728c2ecf20Sopenharmony_ci		else if (skb->protocol == htons(ETH_P_IPV6))
13738c2ecf20Sopenharmony_ci			vnet_fullcsum_ipv6(skb);
13748c2ecf20Sopenharmony_ci#endif
13758c2ecf20Sopenharmony_ci	}
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	dr = &port->vio.drings[VIO_DRIVER_TX_RING];
13788c2ecf20Sopenharmony_ci	i = skb_get_queue_mapping(skb);
13798c2ecf20Sopenharmony_ci	txq = netdev_get_tx_queue(dev, i);
13808c2ecf20Sopenharmony_ci	if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
13818c2ecf20Sopenharmony_ci		if (!netif_tx_queue_stopped(txq)) {
13828c2ecf20Sopenharmony_ci			netif_tx_stop_queue(txq);
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci			/* This is a hard error, log it. */
13858c2ecf20Sopenharmony_ci			netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
13868c2ecf20Sopenharmony_ci			dev->stats.tx_errors++;
13878c2ecf20Sopenharmony_ci		}
13888c2ecf20Sopenharmony_ci		rcu_read_unlock();
13898c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
13908c2ecf20Sopenharmony_ci	}
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	d = vio_dring_cur(dr);
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	txi = dr->prod;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	freeskbs = vnet_clean_tx_ring(port, &pending);
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	BUG_ON(port->tx_bufs[txi].skb);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	len = skb->len;
14018c2ecf20Sopenharmony_ci	if (len < ETH_ZLEN)
14028c2ecf20Sopenharmony_ci		len = ETH_ZLEN;
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	err = vnet_skb_map(port->vio.lp, skb, port->tx_bufs[txi].cookies, 2,
14058c2ecf20Sopenharmony_ci			   (LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_RW));
14068c2ecf20Sopenharmony_ci	if (err < 0) {
14078c2ecf20Sopenharmony_ci		netdev_info(dev, "tx buffer map error %d\n", err);
14088c2ecf20Sopenharmony_ci		goto out_dropped;
14098c2ecf20Sopenharmony_ci	}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	port->tx_bufs[txi].skb = skb;
14128c2ecf20Sopenharmony_ci	skb = NULL;
14138c2ecf20Sopenharmony_ci	port->tx_bufs[txi].ncookies = err;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	/* We don't rely on the ACKs to free the skb in vnet_start_xmit(),
14168c2ecf20Sopenharmony_ci	 * thus it is safe to not set VIO_ACK_ENABLE for each transmission:
14178c2ecf20Sopenharmony_ci	 * the protocol itself does not require it as long as the peer
14188c2ecf20Sopenharmony_ci	 * sends a VIO_SUBTYPE_ACK for VIO_DRING_STOPPED.
14198c2ecf20Sopenharmony_ci	 *
14208c2ecf20Sopenharmony_ci	 * An ACK for every packet in the ring is expensive as the
14218c2ecf20Sopenharmony_ci	 * sending of LDC messages is slow and affects performance.
14228c2ecf20Sopenharmony_ci	 */
14238c2ecf20Sopenharmony_ci	d->hdr.ack = VIO_ACK_DISABLE;
14248c2ecf20Sopenharmony_ci	d->size = len;
14258c2ecf20Sopenharmony_ci	d->ncookies = port->tx_bufs[txi].ncookies;
14268c2ecf20Sopenharmony_ci	for (i = 0; i < d->ncookies; i++)
14278c2ecf20Sopenharmony_ci		d->cookies[i] = port->tx_bufs[txi].cookies[i];
14288c2ecf20Sopenharmony_ci	if (vio_version_after_eq(&port->vio, 1, 7)) {
14298c2ecf20Sopenharmony_ci		struct vio_net_dext *dext = vio_net_ext(d);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci		memset(dext, 0, sizeof(*dext));
14328c2ecf20Sopenharmony_ci		if (skb_is_gso(port->tx_bufs[txi].skb)) {
14338c2ecf20Sopenharmony_ci			dext->ipv4_lso_mss = skb_shinfo(port->tx_bufs[txi].skb)
14348c2ecf20Sopenharmony_ci					     ->gso_size;
14358c2ecf20Sopenharmony_ci			dext->flags |= VNET_PKT_IPV4_LSO;
14368c2ecf20Sopenharmony_ci		}
14378c2ecf20Sopenharmony_ci		if (vio_version_after_eq(&port->vio, 1, 8) &&
14388c2ecf20Sopenharmony_ci		    !port->switch_port) {
14398c2ecf20Sopenharmony_ci			dext->flags |= VNET_PKT_HCK_IPV4_HDRCKSUM_OK;
14408c2ecf20Sopenharmony_ci			dext->flags |= VNET_PKT_HCK_FULLCKSUM_OK;
14418c2ecf20Sopenharmony_ci		}
14428c2ecf20Sopenharmony_ci	}
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	/* This has to be a non-SMP write barrier because we are writing
14458c2ecf20Sopenharmony_ci	 * to memory which is shared with the peer LDOM.
14468c2ecf20Sopenharmony_ci	 */
14478c2ecf20Sopenharmony_ci	dma_wmb();
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	d->hdr.state = VIO_DESC_READY;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	/* Exactly one ldc "start" trigger (for dr->cons) needs to be sent
14528c2ecf20Sopenharmony_ci	 * to notify the consumer that some descriptors are READY.
14538c2ecf20Sopenharmony_ci	 * After that "start" trigger, no additional triggers are needed until
14548c2ecf20Sopenharmony_ci	 * a DRING_STOPPED is received from the consumer. The dr->cons field
14558c2ecf20Sopenharmony_ci	 * (set up by vnet_ack()) has the value of the next dring index
14568c2ecf20Sopenharmony_ci	 * that has not yet been ack-ed. We send a "start" trigger here
14578c2ecf20Sopenharmony_ci	 * if, and only if, start_cons is true (reset it afterward). Conversely,
14588c2ecf20Sopenharmony_ci	 * vnet_ack() should check if the dring corresponding to cons
14598c2ecf20Sopenharmony_ci	 * is marked READY, but start_cons was false.
14608c2ecf20Sopenharmony_ci	 * If so, vnet_ack() should send out the missed "start" trigger.
14618c2ecf20Sopenharmony_ci	 *
14628c2ecf20Sopenharmony_ci	 * Note that the dma_wmb() above makes sure the cookies et al. are
14638c2ecf20Sopenharmony_ci	 * not globally visible before the VIO_DESC_READY, and that the
14648c2ecf20Sopenharmony_ci	 * stores are ordered correctly by the compiler. The consumer will
14658c2ecf20Sopenharmony_ci	 * not proceed until the VIO_DESC_READY is visible assuring that
14668c2ecf20Sopenharmony_ci	 * the consumer does not observe anything related to descriptors
14678c2ecf20Sopenharmony_ci	 * out of order. The HV trap from the LDC start trigger is the
14688c2ecf20Sopenharmony_ci	 * producer to consumer announcement that work is available to the
14698c2ecf20Sopenharmony_ci	 * consumer
14708c2ecf20Sopenharmony_ci	 */
14718c2ecf20Sopenharmony_ci	if (!port->start_cons) { /* previous trigger suffices */
14728c2ecf20Sopenharmony_ci		trace_vnet_skip_tx_trigger(port->vio._local_sid,
14738c2ecf20Sopenharmony_ci					   port->vio._peer_sid, dr->cons);
14748c2ecf20Sopenharmony_ci		goto ldc_start_done;
14758c2ecf20Sopenharmony_ci	}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	err = __vnet_tx_trigger(port, dr->cons);
14788c2ecf20Sopenharmony_ci	if (unlikely(err < 0)) {
14798c2ecf20Sopenharmony_ci		netdev_info(dev, "TX trigger error %d\n", err);
14808c2ecf20Sopenharmony_ci		d->hdr.state = VIO_DESC_FREE;
14818c2ecf20Sopenharmony_ci		skb = port->tx_bufs[txi].skb;
14828c2ecf20Sopenharmony_ci		port->tx_bufs[txi].skb = NULL;
14838c2ecf20Sopenharmony_ci		dev->stats.tx_carrier_errors++;
14848c2ecf20Sopenharmony_ci		goto out_dropped;
14858c2ecf20Sopenharmony_ci	}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cildc_start_done:
14888c2ecf20Sopenharmony_ci	port->start_cons = false;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	dev->stats.tx_packets++;
14918c2ecf20Sopenharmony_ci	dev->stats.tx_bytes += port->tx_bufs[txi].skb->len;
14928c2ecf20Sopenharmony_ci	port->stats.tx_packets++;
14938c2ecf20Sopenharmony_ci	port->stats.tx_bytes += port->tx_bufs[txi].skb->len;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1);
14968c2ecf20Sopenharmony_ci	if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
14978c2ecf20Sopenharmony_ci		netif_tx_stop_queue(txq);
14988c2ecf20Sopenharmony_ci		smp_rmb();
14998c2ecf20Sopenharmony_ci		if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr))
15008c2ecf20Sopenharmony_ci			netif_tx_wake_queue(txq);
15018c2ecf20Sopenharmony_ci	}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	(void)mod_timer(&port->clean_timer, jiffies + VNET_CLEAN_TIMEOUT);
15048c2ecf20Sopenharmony_ci	rcu_read_unlock();
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	vnet_free_skbs(freeskbs);
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ciout_dropped:
15118c2ecf20Sopenharmony_ci	if (pending)
15128c2ecf20Sopenharmony_ci		(void)mod_timer(&port->clean_timer,
15138c2ecf20Sopenharmony_ci				jiffies + VNET_CLEAN_TIMEOUT);
15148c2ecf20Sopenharmony_ci	else if (port)
15158c2ecf20Sopenharmony_ci		del_timer(&port->clean_timer);
15168c2ecf20Sopenharmony_ci	rcu_read_unlock();
15178c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
15188c2ecf20Sopenharmony_ci	vnet_free_skbs(freeskbs);
15198c2ecf20Sopenharmony_ci	dev->stats.tx_dropped++;
15208c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
15218c2ecf20Sopenharmony_ci}
15228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_start_xmit_common);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_civoid sunvnet_tx_timeout_common(struct net_device *dev, unsigned int txqueue)
15258c2ecf20Sopenharmony_ci{
15268c2ecf20Sopenharmony_ci	/* XXX Implement me XXX */
15278c2ecf20Sopenharmony_ci}
15288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_tx_timeout_common);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ciint sunvnet_open_common(struct net_device *dev)
15318c2ecf20Sopenharmony_ci{
15328c2ecf20Sopenharmony_ci	netif_carrier_on(dev);
15338c2ecf20Sopenharmony_ci	netif_tx_start_all_queues(dev);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	return 0;
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_open_common);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ciint sunvnet_close_common(struct net_device *dev)
15408c2ecf20Sopenharmony_ci{
15418c2ecf20Sopenharmony_ci	netif_tx_stop_all_queues(dev);
15428c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	return 0;
15458c2ecf20Sopenharmony_ci}
15468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_close_common);
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_cistatic struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	struct vnet_mcast_entry *m;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	for (m = vp->mcast_list; m; m = m->next) {
15538c2ecf20Sopenharmony_ci		if (ether_addr_equal(m->addr, addr))
15548c2ecf20Sopenharmony_ci			return m;
15558c2ecf20Sopenharmony_ci	}
15568c2ecf20Sopenharmony_ci	return NULL;
15578c2ecf20Sopenharmony_ci}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_cistatic void __update_mc_list(struct vnet *vp, struct net_device *dev)
15608c2ecf20Sopenharmony_ci{
15618c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
15648c2ecf20Sopenharmony_ci		struct vnet_mcast_entry *m;
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci		m = __vnet_mc_find(vp, ha->addr);
15678c2ecf20Sopenharmony_ci		if (m) {
15688c2ecf20Sopenharmony_ci			m->hit = 1;
15698c2ecf20Sopenharmony_ci			continue;
15708c2ecf20Sopenharmony_ci		}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci		if (!m) {
15738c2ecf20Sopenharmony_ci			m = kzalloc(sizeof(*m), GFP_ATOMIC);
15748c2ecf20Sopenharmony_ci			if (!m)
15758c2ecf20Sopenharmony_ci				continue;
15768c2ecf20Sopenharmony_ci			memcpy(m->addr, ha->addr, ETH_ALEN);
15778c2ecf20Sopenharmony_ci			m->hit = 1;
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci			m->next = vp->mcast_list;
15808c2ecf20Sopenharmony_ci			vp->mcast_list = m;
15818c2ecf20Sopenharmony_ci		}
15828c2ecf20Sopenharmony_ci	}
15838c2ecf20Sopenharmony_ci}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_cistatic void __send_mc_list(struct vnet *vp, struct vnet_port *port)
15868c2ecf20Sopenharmony_ci{
15878c2ecf20Sopenharmony_ci	struct vio_net_mcast_info info;
15888c2ecf20Sopenharmony_ci	struct vnet_mcast_entry *m, **pp;
15898c2ecf20Sopenharmony_ci	int n_addrs;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	memset(&info, 0, sizeof(info));
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	info.tag.type = VIO_TYPE_CTRL;
15948c2ecf20Sopenharmony_ci	info.tag.stype = VIO_SUBTYPE_INFO;
15958c2ecf20Sopenharmony_ci	info.tag.stype_env = VNET_MCAST_INFO;
15968c2ecf20Sopenharmony_ci	info.tag.sid = vio_send_sid(&port->vio);
15978c2ecf20Sopenharmony_ci	info.set = 1;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	n_addrs = 0;
16008c2ecf20Sopenharmony_ci	for (m = vp->mcast_list; m; m = m->next) {
16018c2ecf20Sopenharmony_ci		if (m->sent)
16028c2ecf20Sopenharmony_ci			continue;
16038c2ecf20Sopenharmony_ci		m->sent = 1;
16048c2ecf20Sopenharmony_ci		memcpy(&info.mcast_addr[n_addrs * ETH_ALEN],
16058c2ecf20Sopenharmony_ci		       m->addr, ETH_ALEN);
16068c2ecf20Sopenharmony_ci		if (++n_addrs == VNET_NUM_MCAST) {
16078c2ecf20Sopenharmony_ci			info.count = n_addrs;
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci			(void)vio_ldc_send(&port->vio, &info,
16108c2ecf20Sopenharmony_ci					   sizeof(info));
16118c2ecf20Sopenharmony_ci			n_addrs = 0;
16128c2ecf20Sopenharmony_ci		}
16138c2ecf20Sopenharmony_ci	}
16148c2ecf20Sopenharmony_ci	if (n_addrs) {
16158c2ecf20Sopenharmony_ci		info.count = n_addrs;
16168c2ecf20Sopenharmony_ci		(void)vio_ldc_send(&port->vio, &info, sizeof(info));
16178c2ecf20Sopenharmony_ci	}
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	info.set = 0;
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	n_addrs = 0;
16228c2ecf20Sopenharmony_ci	pp = &vp->mcast_list;
16238c2ecf20Sopenharmony_ci	while ((m = *pp) != NULL) {
16248c2ecf20Sopenharmony_ci		if (m->hit) {
16258c2ecf20Sopenharmony_ci			m->hit = 0;
16268c2ecf20Sopenharmony_ci			pp = &m->next;
16278c2ecf20Sopenharmony_ci			continue;
16288c2ecf20Sopenharmony_ci		}
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci		memcpy(&info.mcast_addr[n_addrs * ETH_ALEN],
16318c2ecf20Sopenharmony_ci		       m->addr, ETH_ALEN);
16328c2ecf20Sopenharmony_ci		if (++n_addrs == VNET_NUM_MCAST) {
16338c2ecf20Sopenharmony_ci			info.count = n_addrs;
16348c2ecf20Sopenharmony_ci			(void)vio_ldc_send(&port->vio, &info,
16358c2ecf20Sopenharmony_ci					   sizeof(info));
16368c2ecf20Sopenharmony_ci			n_addrs = 0;
16378c2ecf20Sopenharmony_ci		}
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci		*pp = m->next;
16408c2ecf20Sopenharmony_ci		kfree(m);
16418c2ecf20Sopenharmony_ci	}
16428c2ecf20Sopenharmony_ci	if (n_addrs) {
16438c2ecf20Sopenharmony_ci		info.count = n_addrs;
16448c2ecf20Sopenharmony_ci		(void)vio_ldc_send(&port->vio, &info, sizeof(info));
16458c2ecf20Sopenharmony_ci	}
16468c2ecf20Sopenharmony_ci}
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_civoid sunvnet_set_rx_mode_common(struct net_device *dev, struct vnet *vp)
16498c2ecf20Sopenharmony_ci{
16508c2ecf20Sopenharmony_ci	struct vnet_port *port;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	rcu_read_lock();
16538c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, &vp->port_list, list) {
16548c2ecf20Sopenharmony_ci		if (port->switch_port) {
16558c2ecf20Sopenharmony_ci			__update_mc_list(vp, dev);
16568c2ecf20Sopenharmony_ci			__send_mc_list(vp, port);
16578c2ecf20Sopenharmony_ci			break;
16588c2ecf20Sopenharmony_ci		}
16598c2ecf20Sopenharmony_ci	}
16608c2ecf20Sopenharmony_ci	rcu_read_unlock();
16618c2ecf20Sopenharmony_ci}
16628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_set_rx_mode_common);
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ciint sunvnet_set_mac_addr_common(struct net_device *dev, void *p)
16658c2ecf20Sopenharmony_ci{
16668c2ecf20Sopenharmony_ci	return -EINVAL;
16678c2ecf20Sopenharmony_ci}
16688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_set_mac_addr_common);
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_civoid sunvnet_port_free_tx_bufs_common(struct vnet_port *port)
16718c2ecf20Sopenharmony_ci{
16728c2ecf20Sopenharmony_ci	struct vio_dring_state *dr;
16738c2ecf20Sopenharmony_ci	int i;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	dr = &port->vio.drings[VIO_DRIVER_TX_RING];
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	if (!dr->base)
16788c2ecf20Sopenharmony_ci		return;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	for (i = 0; i < VNET_TX_RING_SIZE; i++) {
16818c2ecf20Sopenharmony_ci		struct vio_net_desc *d;
16828c2ecf20Sopenharmony_ci		void *skb = port->tx_bufs[i].skb;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci		if (!skb)
16858c2ecf20Sopenharmony_ci			continue;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci		d = vio_dring_entry(dr, i);
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci		ldc_unmap(port->vio.lp,
16908c2ecf20Sopenharmony_ci			  port->tx_bufs[i].cookies,
16918c2ecf20Sopenharmony_ci			  port->tx_bufs[i].ncookies);
16928c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
16938c2ecf20Sopenharmony_ci		port->tx_bufs[i].skb = NULL;
16948c2ecf20Sopenharmony_ci		d->hdr.state = VIO_DESC_FREE;
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci	ldc_free_exp_dring(port->vio.lp, dr->base,
16978c2ecf20Sopenharmony_ci			   (dr->entry_size * dr->num_entries),
16988c2ecf20Sopenharmony_ci			   dr->cookies, dr->ncookies);
16998c2ecf20Sopenharmony_ci	dr->base = NULL;
17008c2ecf20Sopenharmony_ci	dr->entry_size = 0;
17018c2ecf20Sopenharmony_ci	dr->num_entries = 0;
17028c2ecf20Sopenharmony_ci	dr->pending = 0;
17038c2ecf20Sopenharmony_ci	dr->ncookies = 0;
17048c2ecf20Sopenharmony_ci}
17058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_port_free_tx_bufs_common);
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_civoid vnet_port_reset(struct vnet_port *port)
17088c2ecf20Sopenharmony_ci{
17098c2ecf20Sopenharmony_ci	del_timer(&port->clean_timer);
17108c2ecf20Sopenharmony_ci	sunvnet_port_free_tx_bufs_common(port);
17118c2ecf20Sopenharmony_ci	port->rmtu = 0;
17128c2ecf20Sopenharmony_ci	port->tso = (port->vsw == 0);  /* no tso in vsw, misbehaves in bridge */
17138c2ecf20Sopenharmony_ci	port->tsolen = 0;
17148c2ecf20Sopenharmony_ci}
17158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vnet_port_reset);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_cistatic int vnet_port_alloc_tx_ring(struct vnet_port *port)
17188c2ecf20Sopenharmony_ci{
17198c2ecf20Sopenharmony_ci	struct vio_dring_state *dr;
17208c2ecf20Sopenharmony_ci	unsigned long len, elen;
17218c2ecf20Sopenharmony_ci	int i, err, ncookies;
17228c2ecf20Sopenharmony_ci	void *dring;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	dr = &port->vio.drings[VIO_DRIVER_TX_RING];
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	elen = sizeof(struct vio_net_desc) +
17278c2ecf20Sopenharmony_ci	       sizeof(struct ldc_trans_cookie) * 2;
17288c2ecf20Sopenharmony_ci	if (vio_version_after_eq(&port->vio, 1, 7))
17298c2ecf20Sopenharmony_ci		elen += sizeof(struct vio_net_dext);
17308c2ecf20Sopenharmony_ci	len = VNET_TX_RING_SIZE * elen;
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	ncookies = VIO_MAX_RING_COOKIES;
17338c2ecf20Sopenharmony_ci	dring = ldc_alloc_exp_dring(port->vio.lp, len,
17348c2ecf20Sopenharmony_ci				    dr->cookies, &ncookies,
17358c2ecf20Sopenharmony_ci				    (LDC_MAP_SHADOW |
17368c2ecf20Sopenharmony_ci				     LDC_MAP_DIRECT |
17378c2ecf20Sopenharmony_ci				     LDC_MAP_RW));
17388c2ecf20Sopenharmony_ci	if (IS_ERR(dring)) {
17398c2ecf20Sopenharmony_ci		err = PTR_ERR(dring);
17408c2ecf20Sopenharmony_ci		goto err_out;
17418c2ecf20Sopenharmony_ci	}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	dr->base = dring;
17448c2ecf20Sopenharmony_ci	dr->entry_size = elen;
17458c2ecf20Sopenharmony_ci	dr->num_entries = VNET_TX_RING_SIZE;
17468c2ecf20Sopenharmony_ci	dr->prod = 0;
17478c2ecf20Sopenharmony_ci	dr->cons = 0;
17488c2ecf20Sopenharmony_ci	port->start_cons  = true; /* need an initial trigger */
17498c2ecf20Sopenharmony_ci	dr->pending = VNET_TX_RING_SIZE;
17508c2ecf20Sopenharmony_ci	dr->ncookies = ncookies;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	for (i = 0; i < VNET_TX_RING_SIZE; ++i) {
17538c2ecf20Sopenharmony_ci		struct vio_net_desc *d;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci		d = vio_dring_entry(dr, i);
17568c2ecf20Sopenharmony_ci		d->hdr.state = VIO_DESC_FREE;
17578c2ecf20Sopenharmony_ci	}
17588c2ecf20Sopenharmony_ci	return 0;
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_cierr_out:
17618c2ecf20Sopenharmony_ci	sunvnet_port_free_tx_bufs_common(port);
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	return err;
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
17678c2ecf20Sopenharmony_civoid sunvnet_poll_controller_common(struct net_device *dev, struct vnet *vp)
17688c2ecf20Sopenharmony_ci{
17698c2ecf20Sopenharmony_ci	struct vnet_port *port;
17708c2ecf20Sopenharmony_ci	unsigned long flags;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vp->lock, flags);
17738c2ecf20Sopenharmony_ci	if (!list_empty(&vp->port_list)) {
17748c2ecf20Sopenharmony_ci		port = list_entry(vp->port_list.next, struct vnet_port, list);
17758c2ecf20Sopenharmony_ci		napi_schedule(&port->napi);
17768c2ecf20Sopenharmony_ci	}
17778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vp->lock, flags);
17788c2ecf20Sopenharmony_ci}
17798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_poll_controller_common);
17808c2ecf20Sopenharmony_ci#endif
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_civoid sunvnet_port_add_txq_common(struct vnet_port *port)
17838c2ecf20Sopenharmony_ci{
17848c2ecf20Sopenharmony_ci	struct vnet *vp = port->vp;
17858c2ecf20Sopenharmony_ci	int smallest = 0;
17868c2ecf20Sopenharmony_ci	int i;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	/* find the first least-used q
17898c2ecf20Sopenharmony_ci	 * When there are more ldoms than q's, we start to
17908c2ecf20Sopenharmony_ci	 * double up on ports per queue.
17918c2ecf20Sopenharmony_ci	 */
17928c2ecf20Sopenharmony_ci	for (i = 0; i < VNET_MAX_TXQS; i++) {
17938c2ecf20Sopenharmony_ci		if (vp->q_used[i] == 0) {
17948c2ecf20Sopenharmony_ci			smallest = i;
17958c2ecf20Sopenharmony_ci			break;
17968c2ecf20Sopenharmony_ci		}
17978c2ecf20Sopenharmony_ci		if (vp->q_used[i] < vp->q_used[smallest])
17988c2ecf20Sopenharmony_ci			smallest = i;
17998c2ecf20Sopenharmony_ci	}
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	vp->nports++;
18028c2ecf20Sopenharmony_ci	vp->q_used[smallest]++;
18038c2ecf20Sopenharmony_ci	port->q_index = smallest;
18048c2ecf20Sopenharmony_ci}
18058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_port_add_txq_common);
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_civoid sunvnet_port_rm_txq_common(struct vnet_port *port)
18088c2ecf20Sopenharmony_ci{
18098c2ecf20Sopenharmony_ci	port->vp->nports--;
18108c2ecf20Sopenharmony_ci	port->vp->q_used[port->q_index]--;
18118c2ecf20Sopenharmony_ci	port->q_index = 0;
18128c2ecf20Sopenharmony_ci}
18138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sunvnet_port_rm_txq_common);
1814