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