1e689cf4aSJeff Kirsher /* sunvnet.c: Sun LDOM Virtual Network Driver. 2e689cf4aSJeff Kirsher * 3e689cf4aSJeff Kirsher * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> 4e689cf4aSJeff Kirsher */ 5e689cf4aSJeff Kirsher 6e689cf4aSJeff Kirsher #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7e689cf4aSJeff Kirsher 8e689cf4aSJeff Kirsher #include <linux/module.h> 9e689cf4aSJeff Kirsher #include <linux/kernel.h> 10e689cf4aSJeff Kirsher #include <linux/types.h> 11e689cf4aSJeff Kirsher #include <linux/slab.h> 12e689cf4aSJeff Kirsher #include <linux/delay.h> 13e689cf4aSJeff Kirsher #include <linux/init.h> 14e689cf4aSJeff Kirsher #include <linux/netdevice.h> 15e689cf4aSJeff Kirsher #include <linux/ethtool.h> 16e689cf4aSJeff Kirsher #include <linux/etherdevice.h> 17e689cf4aSJeff Kirsher #include <linux/mutex.h> 18da38c564SDavid L Stevens #include <linux/highmem.h> 19e4defc77SDavid L Stevens #include <linux/if_vlan.h> 20e689cf4aSJeff Kirsher 21a2b78e9bSDavid L Stevens #if IS_ENABLED(CONFIG_IPV6) 22a2b78e9bSDavid L Stevens #include <linux/icmpv6.h> 23a2b78e9bSDavid L Stevens #endif 24a2b78e9bSDavid L Stevens 256d0ba919SDavid L Stevens #include <net/ip.h> 26a2b78e9bSDavid L Stevens #include <net/icmp.h> 27a2b78e9bSDavid L Stevens #include <net/route.h> 28a2b78e9bSDavid L Stevens 29e689cf4aSJeff Kirsher #include <asm/vio.h> 30e689cf4aSJeff Kirsher #include <asm/ldc.h> 31e689cf4aSJeff Kirsher 32e689cf4aSJeff Kirsher #include "sunvnet.h" 33e689cf4aSJeff Kirsher 34e689cf4aSJeff Kirsher #define DRV_MODULE_NAME "sunvnet" 35e689cf4aSJeff Kirsher #define DRV_MODULE_VERSION "1.0" 36e689cf4aSJeff Kirsher #define DRV_MODULE_RELDATE "June 25, 2007" 37e689cf4aSJeff Kirsher 38f73d12bdSBill Pemberton static char version[] = 39e689cf4aSJeff Kirsher DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 40e689cf4aSJeff Kirsher MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 41e689cf4aSJeff Kirsher MODULE_DESCRIPTION("Sun LDOM virtual network driver"); 42e689cf4aSJeff Kirsher MODULE_LICENSE("GPL"); 43e689cf4aSJeff Kirsher MODULE_VERSION(DRV_MODULE_VERSION); 44e689cf4aSJeff Kirsher 45d51bffd1SSowmini Varadhan #define VNET_MAX_TXQS 16 46d51bffd1SSowmini Varadhan 47adddc32dSSowmini Varadhan /* Heuristic for the number of times to exponentially backoff and 48adddc32dSSowmini Varadhan * retry sending an LDC trigger when EAGAIN is encountered 49adddc32dSSowmini Varadhan */ 50adddc32dSSowmini Varadhan #define VNET_MAX_RETRIES 10 51adddc32dSSowmini Varadhan 52d1015645SSowmini Varadhan static int __vnet_tx_trigger(struct vnet_port *port, u32 start); 538e2b60cdSDavid L Stevens static void vnet_port_reset(struct vnet_port *port); 54d1015645SSowmini Varadhan 55e689cf4aSJeff Kirsher /* Ordered from largest major to lowest */ 56e689cf4aSJeff Kirsher static struct vio_version vnet_versions[] = { 576d0ba919SDavid L Stevens { .major = 1, .minor = 8 }, 586d0ba919SDavid L Stevens { .major = 1, .minor = 7 }, 59e4defc77SDavid L Stevens { .major = 1, .minor = 6 }, 60e689cf4aSJeff Kirsher { .major = 1, .minor = 0 }, 61e689cf4aSJeff Kirsher }; 62e689cf4aSJeff Kirsher 63e689cf4aSJeff Kirsher static inline u32 vnet_tx_dring_avail(struct vio_dring_state *dr) 64e689cf4aSJeff Kirsher { 65e689cf4aSJeff Kirsher return vio_dring_avail(dr, VNET_TX_RING_SIZE); 66e689cf4aSJeff Kirsher } 67e689cf4aSJeff Kirsher 68e689cf4aSJeff Kirsher static int vnet_handle_unknown(struct vnet_port *port, void *arg) 69e689cf4aSJeff Kirsher { 70e689cf4aSJeff Kirsher struct vio_msg_tag *pkt = arg; 71e689cf4aSJeff Kirsher 72e689cf4aSJeff Kirsher pr_err("Received unknown msg [%02x:%02x:%04x:%08x]\n", 73e689cf4aSJeff Kirsher pkt->type, pkt->stype, pkt->stype_env, pkt->sid); 74e689cf4aSJeff Kirsher pr_err("Resetting connection\n"); 75e689cf4aSJeff Kirsher 76e689cf4aSJeff Kirsher ldc_disconnect(port->vio.lp); 77e689cf4aSJeff Kirsher 78e689cf4aSJeff Kirsher return -ECONNRESET; 79e689cf4aSJeff Kirsher } 80e689cf4aSJeff Kirsher 81d6732489SDavid L Stevens static int vnet_port_alloc_tx_ring(struct vnet_port *port); 82d6732489SDavid L Stevens 83e689cf4aSJeff Kirsher static int vnet_send_attr(struct vio_driver_state *vio) 84e689cf4aSJeff Kirsher { 85e689cf4aSJeff Kirsher struct vnet_port *port = to_vnet_port(vio); 86e689cf4aSJeff Kirsher struct net_device *dev = port->vp->dev; 87e689cf4aSJeff Kirsher struct vio_net_attr_info pkt; 88e4defc77SDavid L Stevens int framelen = ETH_FRAME_LEN; 89d6732489SDavid L Stevens int i, err; 90d6732489SDavid L Stevens 91d6732489SDavid L Stevens err = vnet_port_alloc_tx_ring(to_vnet_port(vio)); 92d6732489SDavid L Stevens if (err) 93d6732489SDavid L Stevens return err; 94e689cf4aSJeff Kirsher 95e689cf4aSJeff Kirsher memset(&pkt, 0, sizeof(pkt)); 96e689cf4aSJeff Kirsher pkt.tag.type = VIO_TYPE_CTRL; 97e689cf4aSJeff Kirsher pkt.tag.stype = VIO_SUBTYPE_INFO; 98e689cf4aSJeff Kirsher pkt.tag.stype_env = VIO_ATTR_INFO; 99e689cf4aSJeff Kirsher pkt.tag.sid = vio_send_sid(vio); 100e4defc77SDavid L Stevens if (vio_version_before(vio, 1, 2)) 101e689cf4aSJeff Kirsher pkt.xfer_mode = VIO_DRING_MODE; 102e4defc77SDavid L Stevens else 103e4defc77SDavid L Stevens pkt.xfer_mode = VIO_NEW_DRING_MODE; 104e689cf4aSJeff Kirsher pkt.addr_type = VNET_ADDR_ETHERMAC; 105e689cf4aSJeff Kirsher pkt.ack_freq = 0; 106e689cf4aSJeff Kirsher for (i = 0; i < 6; i++) 107e689cf4aSJeff Kirsher pkt.addr |= (u64)dev->dev_addr[i] << ((5 - i) * 8); 108e4defc77SDavid L Stevens if (vio_version_after(vio, 1, 3)) { 109e4defc77SDavid L Stevens if (port->rmtu) { 110e4defc77SDavid L Stevens port->rmtu = min(VNET_MAXPACKET, port->rmtu); 111e4defc77SDavid L Stevens pkt.mtu = port->rmtu; 112e4defc77SDavid L Stevens } else { 113e4defc77SDavid L Stevens port->rmtu = VNET_MAXPACKET; 114e4defc77SDavid L Stevens pkt.mtu = port->rmtu; 115e4defc77SDavid L Stevens } 116e4defc77SDavid L Stevens if (vio_version_after_eq(vio, 1, 6)) 117e4defc77SDavid L Stevens pkt.options = VIO_TX_DRING; 118e4defc77SDavid L Stevens } else if (vio_version_before(vio, 1, 3)) { 119e4defc77SDavid L Stevens pkt.mtu = framelen; 120e4defc77SDavid L Stevens } else { /* v1.3 */ 121e4defc77SDavid L Stevens pkt.mtu = framelen + VLAN_HLEN; 122e4defc77SDavid L Stevens } 123e4defc77SDavid L Stevens 124e4defc77SDavid L Stevens pkt.cflags = 0; 125368e36edSDavid L Stevens if (vio_version_after_eq(vio, 1, 7) && port->tso) { 126368e36edSDavid L Stevens pkt.cflags |= VNET_LSO_IPV4_CAPAB; 127368e36edSDavid L Stevens if (!port->tsolen) 128368e36edSDavid L Stevens port->tsolen = VNET_MAXTSO; 129368e36edSDavid L Stevens pkt.ipv4_lso_maxlen = port->tsolen; 130368e36edSDavid L Stevens } 131368e36edSDavid L Stevens 132368e36edSDavid L Stevens pkt.plnk_updt = PHYSLINK_UPDATE_NONE; 133e689cf4aSJeff Kirsher 134e689cf4aSJeff Kirsher viodbg(HS, "SEND NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] " 135e4defc77SDavid L Stevens "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] " 136e4defc77SDavid L Stevens "cflags[0x%04x] lso_max[%u]\n", 137e689cf4aSJeff Kirsher pkt.xfer_mode, pkt.addr_type, 138e689cf4aSJeff Kirsher (unsigned long long)pkt.addr, 139e4defc77SDavid L Stevens pkt.ack_freq, pkt.plnk_updt, pkt.options, 140e4defc77SDavid L Stevens (unsigned long long)pkt.mtu, pkt.cflags, pkt.ipv4_lso_maxlen); 141e4defc77SDavid L Stevens 142e689cf4aSJeff Kirsher 143e689cf4aSJeff Kirsher return vio_ldc_send(vio, &pkt, sizeof(pkt)); 144e689cf4aSJeff Kirsher } 145e689cf4aSJeff Kirsher 146e689cf4aSJeff Kirsher static int handle_attr_info(struct vio_driver_state *vio, 147e689cf4aSJeff Kirsher struct vio_net_attr_info *pkt) 148e689cf4aSJeff Kirsher { 149e4defc77SDavid L Stevens struct vnet_port *port = to_vnet_port(vio); 150e4defc77SDavid L Stevens u64 localmtu; 151e4defc77SDavid L Stevens u8 xfer_mode; 152e4defc77SDavid L Stevens 153e4defc77SDavid L Stevens viodbg(HS, "GOT NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] " 154e4defc77SDavid L Stevens "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] " 155e4defc77SDavid L Stevens " (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n", 156e689cf4aSJeff Kirsher pkt->xfer_mode, pkt->addr_type, 157e689cf4aSJeff Kirsher (unsigned long long)pkt->addr, 158e4defc77SDavid L Stevens pkt->ack_freq, pkt->plnk_updt, pkt->options, 159e4defc77SDavid L Stevens (unsigned long long)pkt->mtu, port->rmtu, pkt->cflags, 160e4defc77SDavid L Stevens pkt->ipv4_lso_maxlen); 161e689cf4aSJeff Kirsher 162e689cf4aSJeff Kirsher pkt->tag.sid = vio_send_sid(vio); 163e689cf4aSJeff Kirsher 164e4defc77SDavid L Stevens xfer_mode = pkt->xfer_mode; 165e4defc77SDavid L Stevens /* for version < 1.2, VIO_DRING_MODE = 0x3 and no bitmask */ 166e4defc77SDavid L Stevens if (vio_version_before(vio, 1, 2) && xfer_mode == VIO_DRING_MODE) 167e4defc77SDavid L Stevens xfer_mode = VIO_NEW_DRING_MODE; 168e4defc77SDavid L Stevens 169e4defc77SDavid L Stevens /* MTU negotiation: 170e4defc77SDavid L Stevens * < v1.3 - ETH_FRAME_LEN exactly 171e4defc77SDavid L Stevens * > v1.3 - MIN(pkt.mtu, VNET_MAXPACKET, port->rmtu) and change 172e4defc77SDavid L Stevens * pkt->mtu for ACK 173e4defc77SDavid L Stevens * = v1.3 - ETH_FRAME_LEN + VLAN_HLEN exactly 174e4defc77SDavid L Stevens */ 175e4defc77SDavid L Stevens if (vio_version_before(vio, 1, 3)) { 176e4defc77SDavid L Stevens localmtu = ETH_FRAME_LEN; 177e4defc77SDavid L Stevens } else if (vio_version_after(vio, 1, 3)) { 178e4defc77SDavid L Stevens localmtu = port->rmtu ? port->rmtu : VNET_MAXPACKET; 179e4defc77SDavid L Stevens localmtu = min(pkt->mtu, localmtu); 180e4defc77SDavid L Stevens pkt->mtu = localmtu; 181e4defc77SDavid L Stevens } else { /* v1.3 */ 182e4defc77SDavid L Stevens localmtu = ETH_FRAME_LEN + VLAN_HLEN; 183e4defc77SDavid L Stevens } 184e4defc77SDavid L Stevens port->rmtu = localmtu; 185e4defc77SDavid L Stevens 186368e36edSDavid L Stevens /* LSO negotiation */ 187368e36edSDavid L Stevens if (vio_version_after_eq(vio, 1, 7)) 188368e36edSDavid L Stevens port->tso &= !!(pkt->cflags & VNET_LSO_IPV4_CAPAB); 189368e36edSDavid L Stevens else 190368e36edSDavid L Stevens port->tso = false; 191368e36edSDavid L Stevens if (port->tso) { 192368e36edSDavid L Stevens if (!port->tsolen) 193368e36edSDavid L Stevens port->tsolen = VNET_MAXTSO; 194368e36edSDavid L Stevens port->tsolen = min(port->tsolen, pkt->ipv4_lso_maxlen); 195368e36edSDavid L Stevens if (port->tsolen < VNET_MINTSO) { 196368e36edSDavid L Stevens port->tso = false; 197368e36edSDavid L Stevens port->tsolen = 0; 198368e36edSDavid L Stevens pkt->cflags &= ~VNET_LSO_IPV4_CAPAB; 199368e36edSDavid L Stevens } 200368e36edSDavid L Stevens pkt->ipv4_lso_maxlen = port->tsolen; 201368e36edSDavid L Stevens } else { 202368e36edSDavid L Stevens pkt->cflags &= ~VNET_LSO_IPV4_CAPAB; 203368e36edSDavid L Stevens pkt->ipv4_lso_maxlen = 0; 204368e36edSDavid L Stevens } 205368e36edSDavid L Stevens 206e4defc77SDavid L Stevens /* for version >= 1.6, ACK packet mode we support */ 207e4defc77SDavid L Stevens if (vio_version_after_eq(vio, 1, 6)) { 208e4defc77SDavid L Stevens pkt->xfer_mode = VIO_NEW_DRING_MODE; 209e4defc77SDavid L Stevens pkt->options = VIO_TX_DRING; 210e4defc77SDavid L Stevens } 211e4defc77SDavid L Stevens 212e4defc77SDavid L Stevens if (!(xfer_mode | VIO_NEW_DRING_MODE) || 213e689cf4aSJeff Kirsher pkt->addr_type != VNET_ADDR_ETHERMAC || 214e4defc77SDavid L Stevens pkt->mtu != localmtu) { 215e689cf4aSJeff Kirsher viodbg(HS, "SEND NET ATTR NACK\n"); 216e689cf4aSJeff Kirsher 217e689cf4aSJeff Kirsher pkt->tag.stype = VIO_SUBTYPE_NACK; 218e689cf4aSJeff Kirsher 219e689cf4aSJeff Kirsher (void) vio_ldc_send(vio, pkt, sizeof(*pkt)); 220e689cf4aSJeff Kirsher 221e689cf4aSJeff Kirsher return -ECONNRESET; 222e689cf4aSJeff Kirsher } else { 223e4defc77SDavid L Stevens viodbg(HS, "SEND NET ATTR ACK xmode[0x%x] atype[0x%x] " 224e4defc77SDavid L Stevens "addr[%llx] ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] " 225e4defc77SDavid L Stevens "mtu[%llu] (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n", 226e4defc77SDavid L Stevens pkt->xfer_mode, pkt->addr_type, 227e4defc77SDavid L Stevens (unsigned long long)pkt->addr, 228e4defc77SDavid L Stevens pkt->ack_freq, pkt->plnk_updt, pkt->options, 229e4defc77SDavid L Stevens (unsigned long long)pkt->mtu, port->rmtu, pkt->cflags, 230e4defc77SDavid L Stevens pkt->ipv4_lso_maxlen); 231e689cf4aSJeff Kirsher 232e689cf4aSJeff Kirsher pkt->tag.stype = VIO_SUBTYPE_ACK; 233e689cf4aSJeff Kirsher 234e689cf4aSJeff Kirsher return vio_ldc_send(vio, pkt, sizeof(*pkt)); 235e689cf4aSJeff Kirsher } 236e689cf4aSJeff Kirsher 237e689cf4aSJeff Kirsher } 238e689cf4aSJeff Kirsher 239e689cf4aSJeff Kirsher static int handle_attr_ack(struct vio_driver_state *vio, 240e689cf4aSJeff Kirsher struct vio_net_attr_info *pkt) 241e689cf4aSJeff Kirsher { 242e689cf4aSJeff Kirsher viodbg(HS, "GOT NET ATTR ACK\n"); 243e689cf4aSJeff Kirsher 244e689cf4aSJeff Kirsher return 0; 245e689cf4aSJeff Kirsher } 246e689cf4aSJeff Kirsher 247e689cf4aSJeff Kirsher static int handle_attr_nack(struct vio_driver_state *vio, 248e689cf4aSJeff Kirsher struct vio_net_attr_info *pkt) 249e689cf4aSJeff Kirsher { 250e689cf4aSJeff Kirsher viodbg(HS, "GOT NET ATTR NACK\n"); 251e689cf4aSJeff Kirsher 252e689cf4aSJeff Kirsher return -ECONNRESET; 253e689cf4aSJeff Kirsher } 254e689cf4aSJeff Kirsher 255e689cf4aSJeff Kirsher static int vnet_handle_attr(struct vio_driver_state *vio, void *arg) 256e689cf4aSJeff Kirsher { 257e689cf4aSJeff Kirsher struct vio_net_attr_info *pkt = arg; 258e689cf4aSJeff Kirsher 259e689cf4aSJeff Kirsher switch (pkt->tag.stype) { 260e689cf4aSJeff Kirsher case VIO_SUBTYPE_INFO: 261e689cf4aSJeff Kirsher return handle_attr_info(vio, pkt); 262e689cf4aSJeff Kirsher 263e689cf4aSJeff Kirsher case VIO_SUBTYPE_ACK: 264e689cf4aSJeff Kirsher return handle_attr_ack(vio, pkt); 265e689cf4aSJeff Kirsher 266e689cf4aSJeff Kirsher case VIO_SUBTYPE_NACK: 267e689cf4aSJeff Kirsher return handle_attr_nack(vio, pkt); 268e689cf4aSJeff Kirsher 269e689cf4aSJeff Kirsher default: 270e689cf4aSJeff Kirsher return -ECONNRESET; 271e689cf4aSJeff Kirsher } 272e689cf4aSJeff Kirsher } 273e689cf4aSJeff Kirsher 274e689cf4aSJeff Kirsher static void vnet_handshake_complete(struct vio_driver_state *vio) 275e689cf4aSJeff Kirsher { 276e689cf4aSJeff Kirsher struct vio_dring_state *dr; 277e689cf4aSJeff Kirsher 278e689cf4aSJeff Kirsher dr = &vio->drings[VIO_DRIVER_RX_RING]; 279e689cf4aSJeff Kirsher dr->snd_nxt = dr->rcv_nxt = 1; 280e689cf4aSJeff Kirsher 281e689cf4aSJeff Kirsher dr = &vio->drings[VIO_DRIVER_TX_RING]; 282e689cf4aSJeff Kirsher dr->snd_nxt = dr->rcv_nxt = 1; 283e689cf4aSJeff Kirsher } 284e689cf4aSJeff Kirsher 285e689cf4aSJeff Kirsher /* The hypervisor interface that implements copying to/from imported 286e689cf4aSJeff Kirsher * memory from another domain requires that copies are done to 8-byte 287e689cf4aSJeff Kirsher * aligned buffers, and that the lengths of such copies are also 8-byte 288e689cf4aSJeff Kirsher * multiples. 289e689cf4aSJeff Kirsher * 290e689cf4aSJeff Kirsher * So we align skb->data to an 8-byte multiple and pad-out the data 291e689cf4aSJeff Kirsher * area so we can round the copy length up to the next multiple of 292e689cf4aSJeff Kirsher * 8 for the copy. 293e689cf4aSJeff Kirsher * 294e689cf4aSJeff Kirsher * The transmitter puts the actual start of the packet 6 bytes into 295e689cf4aSJeff Kirsher * the buffer it sends over, so that the IP headers after the ethernet 296e689cf4aSJeff Kirsher * header are aligned properly. These 6 bytes are not in the descriptor 297e689cf4aSJeff Kirsher * length, they are simply implied. This offset is represented using 298e689cf4aSJeff Kirsher * the VNET_PACKET_SKIP macro. 299e689cf4aSJeff Kirsher */ 300e689cf4aSJeff Kirsher static struct sk_buff *alloc_and_align_skb(struct net_device *dev, 301e689cf4aSJeff Kirsher unsigned int len) 302e689cf4aSJeff Kirsher { 303e689cf4aSJeff Kirsher struct sk_buff *skb = netdev_alloc_skb(dev, len+VNET_PACKET_SKIP+8+8); 304e689cf4aSJeff Kirsher unsigned long addr, off; 305e689cf4aSJeff Kirsher 306e689cf4aSJeff Kirsher if (unlikely(!skb)) 307e689cf4aSJeff Kirsher return NULL; 308e689cf4aSJeff Kirsher 309e689cf4aSJeff Kirsher addr = (unsigned long) skb->data; 310e689cf4aSJeff Kirsher off = ((addr + 7UL) & ~7UL) - addr; 311e689cf4aSJeff Kirsher if (off) 312e689cf4aSJeff Kirsher skb_reserve(skb, off); 313e689cf4aSJeff Kirsher 314e689cf4aSJeff Kirsher return skb; 315e689cf4aSJeff Kirsher } 316e689cf4aSJeff Kirsher 3176d0ba919SDavid L Stevens static inline void vnet_fullcsum(struct sk_buff *skb) 3186d0ba919SDavid L Stevens { 3196d0ba919SDavid L Stevens struct iphdr *iph = ip_hdr(skb); 3206d0ba919SDavid L Stevens int offset = skb_transport_offset(skb); 3216d0ba919SDavid L Stevens 3226d0ba919SDavid L Stevens if (skb->protocol != htons(ETH_P_IP)) 3236d0ba919SDavid L Stevens return; 3246d0ba919SDavid L Stevens if (iph->protocol != IPPROTO_TCP && 3256d0ba919SDavid L Stevens iph->protocol != IPPROTO_UDP) 3266d0ba919SDavid L Stevens return; 3276d0ba919SDavid L Stevens skb->ip_summed = CHECKSUM_NONE; 3286d0ba919SDavid L Stevens skb->csum_level = 1; 3296d0ba919SDavid L Stevens skb->csum = 0; 3306d0ba919SDavid L Stevens if (iph->protocol == IPPROTO_TCP) { 3316d0ba919SDavid L Stevens struct tcphdr *ptcp = tcp_hdr(skb); 3326d0ba919SDavid L Stevens 3336d0ba919SDavid L Stevens ptcp->check = 0; 3346d0ba919SDavid L Stevens skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); 3356d0ba919SDavid L Stevens ptcp->check = csum_tcpudp_magic(iph->saddr, iph->daddr, 3366d0ba919SDavid L Stevens skb->len - offset, IPPROTO_TCP, 3376d0ba919SDavid L Stevens skb->csum); 3386d0ba919SDavid L Stevens } else if (iph->protocol == IPPROTO_UDP) { 3396d0ba919SDavid L Stevens struct udphdr *pudp = udp_hdr(skb); 3406d0ba919SDavid L Stevens 3416d0ba919SDavid L Stevens pudp->check = 0; 3426d0ba919SDavid L Stevens skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); 3436d0ba919SDavid L Stevens pudp->check = csum_tcpudp_magic(iph->saddr, iph->daddr, 3446d0ba919SDavid L Stevens skb->len - offset, IPPROTO_UDP, 3456d0ba919SDavid L Stevens skb->csum); 3466d0ba919SDavid L Stevens } 3476d0ba919SDavid L Stevens } 3486d0ba919SDavid L Stevens 3496d0ba919SDavid L Stevens static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) 350e689cf4aSJeff Kirsher { 351e689cf4aSJeff Kirsher struct net_device *dev = port->vp->dev; 3526d0ba919SDavid L Stevens unsigned int len = desc->size; 353e689cf4aSJeff Kirsher unsigned int copy_len; 354e689cf4aSJeff Kirsher struct sk_buff *skb; 35586cfeab6SDavid L Stevens int maxlen; 356e689cf4aSJeff Kirsher int err; 357e689cf4aSJeff Kirsher 358e689cf4aSJeff Kirsher err = -EMSGSIZE; 35986cfeab6SDavid L Stevens if (port->tso && port->tsolen > port->rmtu) 36086cfeab6SDavid L Stevens maxlen = port->tsolen; 36186cfeab6SDavid L Stevens else 36286cfeab6SDavid L Stevens maxlen = port->rmtu; 36386cfeab6SDavid L Stevens if (unlikely(len < ETH_ZLEN || len > maxlen)) { 364e689cf4aSJeff Kirsher dev->stats.rx_length_errors++; 365e689cf4aSJeff Kirsher goto out_dropped; 366e689cf4aSJeff Kirsher } 367e689cf4aSJeff Kirsher 368e689cf4aSJeff Kirsher skb = alloc_and_align_skb(dev, len); 369e689cf4aSJeff Kirsher err = -ENOMEM; 370e689cf4aSJeff Kirsher if (unlikely(!skb)) { 371e689cf4aSJeff Kirsher dev->stats.rx_missed_errors++; 372e689cf4aSJeff Kirsher goto out_dropped; 373e689cf4aSJeff Kirsher } 374e689cf4aSJeff Kirsher 375e689cf4aSJeff Kirsher copy_len = (len + VNET_PACKET_SKIP + 7U) & ~7U; 376e689cf4aSJeff Kirsher skb_put(skb, copy_len); 377e689cf4aSJeff Kirsher err = ldc_copy(port->vio.lp, LDC_COPY_IN, 378e689cf4aSJeff Kirsher skb->data, copy_len, 0, 3796d0ba919SDavid L Stevens desc->cookies, desc->ncookies); 380e689cf4aSJeff Kirsher if (unlikely(err < 0)) { 381e689cf4aSJeff Kirsher dev->stats.rx_frame_errors++; 382e689cf4aSJeff Kirsher goto out_free_skb; 383e689cf4aSJeff Kirsher } 384e689cf4aSJeff Kirsher 385e689cf4aSJeff Kirsher skb_pull(skb, VNET_PACKET_SKIP); 386e689cf4aSJeff Kirsher skb_trim(skb, len); 387e689cf4aSJeff Kirsher skb->protocol = eth_type_trans(skb, dev); 388e689cf4aSJeff Kirsher 3896d0ba919SDavid L Stevens if (vio_version_after_eq(&port->vio, 1, 8)) { 3906d0ba919SDavid L Stevens struct vio_net_dext *dext = vio_net_ext(desc); 3916d0ba919SDavid L Stevens 3926d0ba919SDavid L Stevens if (dext->flags & VNET_PKT_HCK_IPV4_HDRCKSUM) { 3936d0ba919SDavid L Stevens if (skb->protocol == ETH_P_IP) { 3946d0ba919SDavid L Stevens struct iphdr *iph = (struct iphdr *)skb->data; 3956d0ba919SDavid L Stevens 3966d0ba919SDavid L Stevens iph->check = 0; 3976d0ba919SDavid L Stevens ip_send_check(iph); 3986d0ba919SDavid L Stevens } 3996d0ba919SDavid L Stevens } 4006d0ba919SDavid L Stevens if ((dext->flags & VNET_PKT_HCK_FULLCKSUM) && 4016d0ba919SDavid L Stevens skb->ip_summed == CHECKSUM_NONE) 4026d0ba919SDavid L Stevens vnet_fullcsum(skb); 4036d0ba919SDavid L Stevens if (dext->flags & VNET_PKT_HCK_IPV4_HDRCKSUM_OK) { 4046d0ba919SDavid L Stevens skb->ip_summed = CHECKSUM_PARTIAL; 4056d0ba919SDavid L Stevens skb->csum_level = 0; 4066d0ba919SDavid L Stevens if (dext->flags & VNET_PKT_HCK_FULLCKSUM_OK) 4076d0ba919SDavid L Stevens skb->csum_level = 1; 4086d0ba919SDavid L Stevens } 4096d0ba919SDavid L Stevens } 4106d0ba919SDavid L Stevens 4111b6b0a47SDavid L Stevens skb->ip_summed = port->switch_port ? CHECKSUM_NONE : CHECKSUM_PARTIAL; 4121b6b0a47SDavid L Stevens 413e689cf4aSJeff Kirsher dev->stats.rx_packets++; 414e689cf4aSJeff Kirsher dev->stats.rx_bytes += len; 41569088822SSowmini Varadhan napi_gro_receive(&port->napi, skb); 416e689cf4aSJeff Kirsher return 0; 417e689cf4aSJeff Kirsher 418e689cf4aSJeff Kirsher out_free_skb: 419e689cf4aSJeff Kirsher kfree_skb(skb); 420e689cf4aSJeff Kirsher 421e689cf4aSJeff Kirsher out_dropped: 422e689cf4aSJeff Kirsher dev->stats.rx_dropped++; 423e689cf4aSJeff Kirsher return err; 424e689cf4aSJeff Kirsher } 425e689cf4aSJeff Kirsher 426e689cf4aSJeff Kirsher static int vnet_send_ack(struct vnet_port *port, struct vio_dring_state *dr, 427e689cf4aSJeff Kirsher u32 start, u32 end, u8 vio_dring_state) 428e689cf4aSJeff Kirsher { 429e689cf4aSJeff Kirsher struct vio_dring_data hdr = { 430e689cf4aSJeff Kirsher .tag = { 431e689cf4aSJeff Kirsher .type = VIO_TYPE_DATA, 432e689cf4aSJeff Kirsher .stype = VIO_SUBTYPE_ACK, 433e689cf4aSJeff Kirsher .stype_env = VIO_DRING_DATA, 434e689cf4aSJeff Kirsher .sid = vio_send_sid(&port->vio), 435e689cf4aSJeff Kirsher }, 436e689cf4aSJeff Kirsher .dring_ident = dr->ident, 437e689cf4aSJeff Kirsher .start_idx = start, 438e689cf4aSJeff Kirsher .end_idx = end, 439e689cf4aSJeff Kirsher .state = vio_dring_state, 440e689cf4aSJeff Kirsher }; 441e689cf4aSJeff Kirsher int err, delay; 442adddc32dSSowmini Varadhan int retries = 0; 443e689cf4aSJeff Kirsher 444e689cf4aSJeff Kirsher hdr.seq = dr->snd_nxt; 445e689cf4aSJeff Kirsher delay = 1; 446e689cf4aSJeff Kirsher do { 447e689cf4aSJeff Kirsher err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); 448e689cf4aSJeff Kirsher if (err > 0) { 449e689cf4aSJeff Kirsher dr->snd_nxt++; 450e689cf4aSJeff Kirsher break; 451e689cf4aSJeff Kirsher } 452e689cf4aSJeff Kirsher udelay(delay); 453e689cf4aSJeff Kirsher if ((delay <<= 1) > 128) 454e689cf4aSJeff Kirsher delay = 128; 455adddc32dSSowmini Varadhan if (retries++ > VNET_MAX_RETRIES) { 456adddc32dSSowmini Varadhan pr_info("ECONNRESET %x:%x:%x:%x:%x:%x\n", 457adddc32dSSowmini Varadhan port->raddr[0], port->raddr[1], 458adddc32dSSowmini Varadhan port->raddr[2], port->raddr[3], 459adddc32dSSowmini Varadhan port->raddr[4], port->raddr[5]); 460d1015645SSowmini Varadhan break; 461adddc32dSSowmini Varadhan } 462e689cf4aSJeff Kirsher } while (err == -EAGAIN); 463e689cf4aSJeff Kirsher 464d1015645SSowmini Varadhan if (err <= 0 && vio_dring_state == VIO_DRING_STOPPED) { 465d1015645SSowmini Varadhan port->stop_rx_idx = end; 466d1015645SSowmini Varadhan port->stop_rx = true; 467d1015645SSowmini Varadhan } else { 468d1015645SSowmini Varadhan port->stop_rx_idx = 0; 469d1015645SSowmini Varadhan port->stop_rx = false; 470d1015645SSowmini Varadhan } 471d1015645SSowmini Varadhan 472e689cf4aSJeff Kirsher return err; 473e689cf4aSJeff Kirsher } 474e689cf4aSJeff Kirsher 475e689cf4aSJeff Kirsher static struct vio_net_desc *get_rx_desc(struct vnet_port *port, 476e689cf4aSJeff Kirsher struct vio_dring_state *dr, 477e689cf4aSJeff Kirsher u32 index) 478e689cf4aSJeff Kirsher { 479e689cf4aSJeff Kirsher struct vio_net_desc *desc = port->vio.desc_buf; 480e689cf4aSJeff Kirsher int err; 481e689cf4aSJeff Kirsher 482e689cf4aSJeff Kirsher err = ldc_get_dring_entry(port->vio.lp, desc, dr->entry_size, 483e689cf4aSJeff Kirsher (index * dr->entry_size), 484e689cf4aSJeff Kirsher dr->cookies, dr->ncookies); 485e689cf4aSJeff Kirsher if (err < 0) 486e689cf4aSJeff Kirsher return ERR_PTR(err); 487e689cf4aSJeff Kirsher 488e689cf4aSJeff Kirsher return desc; 489e689cf4aSJeff Kirsher } 490e689cf4aSJeff Kirsher 491e689cf4aSJeff Kirsher static int put_rx_desc(struct vnet_port *port, 492e689cf4aSJeff Kirsher struct vio_dring_state *dr, 493e689cf4aSJeff Kirsher struct vio_net_desc *desc, 494e689cf4aSJeff Kirsher u32 index) 495e689cf4aSJeff Kirsher { 496e689cf4aSJeff Kirsher int err; 497e689cf4aSJeff Kirsher 498e689cf4aSJeff Kirsher err = ldc_put_dring_entry(port->vio.lp, desc, dr->entry_size, 499e689cf4aSJeff Kirsher (index * dr->entry_size), 500e689cf4aSJeff Kirsher dr->cookies, dr->ncookies); 501e689cf4aSJeff Kirsher if (err < 0) 502e689cf4aSJeff Kirsher return err; 503e689cf4aSJeff Kirsher 504e689cf4aSJeff Kirsher return 0; 505e689cf4aSJeff Kirsher } 506e689cf4aSJeff Kirsher 507e689cf4aSJeff Kirsher static int vnet_walk_rx_one(struct vnet_port *port, 508e689cf4aSJeff Kirsher struct vio_dring_state *dr, 509e689cf4aSJeff Kirsher u32 index, int *needs_ack) 510e689cf4aSJeff Kirsher { 511e689cf4aSJeff Kirsher struct vio_net_desc *desc = get_rx_desc(port, dr, index); 512e689cf4aSJeff Kirsher struct vio_driver_state *vio = &port->vio; 513e689cf4aSJeff Kirsher int err; 514e689cf4aSJeff Kirsher 51569088822SSowmini Varadhan BUG_ON(desc == NULL); 516e689cf4aSJeff Kirsher if (IS_ERR(desc)) 517e689cf4aSJeff Kirsher return PTR_ERR(desc); 518e689cf4aSJeff Kirsher 51978dcff7bSDavid L Stevens if (desc->hdr.state != VIO_DESC_READY) 52078dcff7bSDavid L Stevens return 1; 52178dcff7bSDavid L Stevens 52278dcff7bSDavid L Stevens rmb(); 52378dcff7bSDavid L Stevens 524e689cf4aSJeff Kirsher viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%llx:%llx]\n", 525e689cf4aSJeff Kirsher desc->hdr.state, desc->hdr.ack, 526e689cf4aSJeff Kirsher desc->size, desc->ncookies, 527e689cf4aSJeff Kirsher desc->cookies[0].cookie_addr, 528e689cf4aSJeff Kirsher desc->cookies[0].cookie_size); 529e689cf4aSJeff Kirsher 5306d0ba919SDavid L Stevens err = vnet_rx_one(port, desc); 531e689cf4aSJeff Kirsher if (err == -ECONNRESET) 532e689cf4aSJeff Kirsher return err; 533e689cf4aSJeff Kirsher desc->hdr.state = VIO_DESC_DONE; 534e689cf4aSJeff Kirsher err = put_rx_desc(port, dr, desc, index); 535e689cf4aSJeff Kirsher if (err < 0) 536e689cf4aSJeff Kirsher return err; 537e689cf4aSJeff Kirsher *needs_ack = desc->hdr.ack; 538e689cf4aSJeff Kirsher return 0; 539e689cf4aSJeff Kirsher } 540e689cf4aSJeff Kirsher 541e689cf4aSJeff Kirsher static int vnet_walk_rx(struct vnet_port *port, struct vio_dring_state *dr, 54269088822SSowmini Varadhan u32 start, u32 end, int *npkts, int budget) 543e689cf4aSJeff Kirsher { 544e689cf4aSJeff Kirsher struct vio_driver_state *vio = &port->vio; 545e689cf4aSJeff Kirsher int ack_start = -1, ack_end = -1; 54669088822SSowmini Varadhan bool send_ack = true; 547e689cf4aSJeff Kirsher 548fe47c3c2SDwight Engen end = (end == (u32) -1) ? vio_dring_prev(dr, start) 549fe47c3c2SDwight Engen : vio_dring_next(dr, end); 550e689cf4aSJeff Kirsher 551e689cf4aSJeff Kirsher viodbg(DATA, "vnet_walk_rx start[%08x] end[%08x]\n", start, end); 552e689cf4aSJeff Kirsher 553e689cf4aSJeff Kirsher while (start != end) { 554e689cf4aSJeff Kirsher int ack = 0, err = vnet_walk_rx_one(port, dr, start, &ack); 555e689cf4aSJeff Kirsher if (err == -ECONNRESET) 556e689cf4aSJeff Kirsher return err; 557e689cf4aSJeff Kirsher if (err != 0) 558e689cf4aSJeff Kirsher break; 55969088822SSowmini Varadhan (*npkts)++; 560e689cf4aSJeff Kirsher if (ack_start == -1) 561e689cf4aSJeff Kirsher ack_start = start; 562e689cf4aSJeff Kirsher ack_end = start; 563fe47c3c2SDwight Engen start = vio_dring_next(dr, start); 564e689cf4aSJeff Kirsher if (ack && start != end) { 565e689cf4aSJeff Kirsher err = vnet_send_ack(port, dr, ack_start, ack_end, 566e689cf4aSJeff Kirsher VIO_DRING_ACTIVE); 567e689cf4aSJeff Kirsher if (err == -ECONNRESET) 568e689cf4aSJeff Kirsher return err; 569e689cf4aSJeff Kirsher ack_start = -1; 570e689cf4aSJeff Kirsher } 57169088822SSowmini Varadhan if ((*npkts) >= budget) { 57269088822SSowmini Varadhan send_ack = false; 57369088822SSowmini Varadhan break; 57469088822SSowmini Varadhan } 575e689cf4aSJeff Kirsher } 576e689cf4aSJeff Kirsher if (unlikely(ack_start == -1)) 577fe47c3c2SDwight Engen ack_start = ack_end = vio_dring_prev(dr, start); 57869088822SSowmini Varadhan if (send_ack) { 57969088822SSowmini Varadhan port->napi_resume = false; 58069088822SSowmini Varadhan return vnet_send_ack(port, dr, ack_start, ack_end, 58169088822SSowmini Varadhan VIO_DRING_STOPPED); 58269088822SSowmini Varadhan } else { 58369088822SSowmini Varadhan port->napi_resume = true; 58469088822SSowmini Varadhan port->napi_stop_idx = ack_end; 58569088822SSowmini Varadhan return 1; 58669088822SSowmini Varadhan } 587e689cf4aSJeff Kirsher } 588e689cf4aSJeff Kirsher 58969088822SSowmini Varadhan static int vnet_rx(struct vnet_port *port, void *msgbuf, int *npkts, 59069088822SSowmini Varadhan int budget) 591e689cf4aSJeff Kirsher { 592e689cf4aSJeff Kirsher struct vio_dring_data *pkt = msgbuf; 593e689cf4aSJeff Kirsher struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_RX_RING]; 594e689cf4aSJeff Kirsher struct vio_driver_state *vio = &port->vio; 595e689cf4aSJeff Kirsher 596e689cf4aSJeff Kirsher viodbg(DATA, "vnet_rx stype_env[%04x] seq[%016llx] rcv_nxt[%016llx]\n", 597e689cf4aSJeff Kirsher pkt->tag.stype_env, pkt->seq, dr->rcv_nxt); 598e689cf4aSJeff Kirsher 599e689cf4aSJeff Kirsher if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) 600e689cf4aSJeff Kirsher return 0; 601e689cf4aSJeff Kirsher if (unlikely(pkt->seq != dr->rcv_nxt)) { 602e689cf4aSJeff Kirsher pr_err("RX out of sequence seq[0x%llx] rcv_nxt[0x%llx]\n", 603e689cf4aSJeff Kirsher pkt->seq, dr->rcv_nxt); 604e689cf4aSJeff Kirsher return 0; 605e689cf4aSJeff Kirsher } 606e689cf4aSJeff Kirsher 60769088822SSowmini Varadhan if (!port->napi_resume) 608e689cf4aSJeff Kirsher dr->rcv_nxt++; 609e689cf4aSJeff Kirsher 610e689cf4aSJeff Kirsher /* XXX Validate pkt->start_idx and pkt->end_idx XXX */ 611e689cf4aSJeff Kirsher 61269088822SSowmini Varadhan return vnet_walk_rx(port, dr, pkt->start_idx, pkt->end_idx, 61369088822SSowmini Varadhan npkts, budget); 614e689cf4aSJeff Kirsher } 615e689cf4aSJeff Kirsher 616e689cf4aSJeff Kirsher static int idx_is_pending(struct vio_dring_state *dr, u32 end) 617e689cf4aSJeff Kirsher { 618e689cf4aSJeff Kirsher u32 idx = dr->cons; 619e689cf4aSJeff Kirsher int found = 0; 620e689cf4aSJeff Kirsher 621e689cf4aSJeff Kirsher while (idx != dr->prod) { 622e689cf4aSJeff Kirsher if (idx == end) { 623e689cf4aSJeff Kirsher found = 1; 624e689cf4aSJeff Kirsher break; 625e689cf4aSJeff Kirsher } 626fe47c3c2SDwight Engen idx = vio_dring_next(dr, idx); 627e689cf4aSJeff Kirsher } 628e689cf4aSJeff Kirsher return found; 629e689cf4aSJeff Kirsher } 630e689cf4aSJeff Kirsher 631e689cf4aSJeff Kirsher static int vnet_ack(struct vnet_port *port, void *msgbuf) 632e689cf4aSJeff Kirsher { 633e689cf4aSJeff Kirsher struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 634e689cf4aSJeff Kirsher struct vio_dring_data *pkt = msgbuf; 635e689cf4aSJeff Kirsher struct net_device *dev; 636e689cf4aSJeff Kirsher struct vnet *vp; 637e689cf4aSJeff Kirsher u32 end; 638d1015645SSowmini Varadhan struct vio_net_desc *desc; 639d51bffd1SSowmini Varadhan struct netdev_queue *txq; 640d51bffd1SSowmini Varadhan 641e689cf4aSJeff Kirsher if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) 642e689cf4aSJeff Kirsher return 0; 643e689cf4aSJeff Kirsher 644e689cf4aSJeff Kirsher end = pkt->end_idx; 64569088822SSowmini Varadhan vp = port->vp; 64669088822SSowmini Varadhan dev = vp->dev; 647b0cffed5SSowmini Varadhan netif_tx_lock(dev); 648b0cffed5SSowmini Varadhan if (unlikely(!idx_is_pending(dr, end))) { 649b0cffed5SSowmini Varadhan netif_tx_unlock(dev); 650b0cffed5SSowmini Varadhan return 0; 651b0cffed5SSowmini Varadhan } 652b0cffed5SSowmini Varadhan 653d1015645SSowmini Varadhan /* sync for race conditions with vnet_start_xmit() and tell xmit it 654d1015645SSowmini Varadhan * is time to send a trigger. 655d1015645SSowmini Varadhan */ 656fe47c3c2SDwight Engen dr->cons = vio_dring_next(dr, end); 657d1015645SSowmini Varadhan desc = vio_dring_entry(dr, dr->cons); 658777362d7SSowmini Varadhan if (desc->hdr.state == VIO_DESC_READY && !port->start_cons) { 659d1015645SSowmini Varadhan /* vnet_start_xmit() just populated this dring but missed 660d1015645SSowmini Varadhan * sending the "start" LDC message to the consumer. 661d1015645SSowmini Varadhan * Send a "start" trigger on its behalf. 662d1015645SSowmini Varadhan */ 663d1015645SSowmini Varadhan if (__vnet_tx_trigger(port, dr->cons) > 0) 664d1015645SSowmini Varadhan port->start_cons = false; 665d1015645SSowmini Varadhan else 666d1015645SSowmini Varadhan port->start_cons = true; 667d1015645SSowmini Varadhan } else { 668d1015645SSowmini Varadhan port->start_cons = true; 669d1015645SSowmini Varadhan } 67069088822SSowmini Varadhan netif_tx_unlock(dev); 671d1015645SSowmini Varadhan 672d51bffd1SSowmini Varadhan txq = netdev_get_tx_queue(dev, port->q_index); 673d51bffd1SSowmini Varadhan if (unlikely(netif_tx_queue_stopped(txq) && 674e689cf4aSJeff Kirsher vnet_tx_dring_avail(dr) >= VNET_TX_WAKEUP_THRESH(dr))) 675e689cf4aSJeff Kirsher return 1; 676e689cf4aSJeff Kirsher 677e689cf4aSJeff Kirsher return 0; 678e689cf4aSJeff Kirsher } 679e689cf4aSJeff Kirsher 680e689cf4aSJeff Kirsher static int vnet_nack(struct vnet_port *port, void *msgbuf) 681e689cf4aSJeff Kirsher { 682e689cf4aSJeff Kirsher /* XXX just reset or similar XXX */ 683e689cf4aSJeff Kirsher return 0; 684e689cf4aSJeff Kirsher } 685e689cf4aSJeff Kirsher 686e689cf4aSJeff Kirsher static int handle_mcast(struct vnet_port *port, void *msgbuf) 687e689cf4aSJeff Kirsher { 688e689cf4aSJeff Kirsher struct vio_net_mcast_info *pkt = msgbuf; 689e689cf4aSJeff Kirsher 690e689cf4aSJeff Kirsher if (pkt->tag.stype != VIO_SUBTYPE_ACK) 691e689cf4aSJeff Kirsher pr_err("%s: Got unexpected MCAST reply [%02x:%02x:%04x:%08x]\n", 692e689cf4aSJeff Kirsher port->vp->dev->name, 693e689cf4aSJeff Kirsher pkt->tag.type, 694e689cf4aSJeff Kirsher pkt->tag.stype, 695e689cf4aSJeff Kirsher pkt->tag.stype_env, 696e689cf4aSJeff Kirsher pkt->tag.sid); 697e689cf4aSJeff Kirsher 698e689cf4aSJeff Kirsher return 0; 699e689cf4aSJeff Kirsher } 700e689cf4aSJeff Kirsher 701d51bffd1SSowmini Varadhan /* Got back a STOPPED LDC message on port. If the queue is stopped, 702d51bffd1SSowmini Varadhan * wake it up so that we'll send out another START message at the 703d51bffd1SSowmini Varadhan * next TX. 704d51bffd1SSowmini Varadhan */ 705d51bffd1SSowmini Varadhan static void maybe_tx_wakeup(struct vnet_port *port) 706e689cf4aSJeff Kirsher { 707d51bffd1SSowmini Varadhan struct netdev_queue *txq; 708e689cf4aSJeff Kirsher 709d51bffd1SSowmini Varadhan txq = netdev_get_tx_queue(port->vp->dev, port->q_index); 710d51bffd1SSowmini Varadhan __netif_tx_lock(txq, smp_processor_id()); 711d51bffd1SSowmini Varadhan if (likely(netif_tx_queue_stopped(txq))) { 712e689cf4aSJeff Kirsher struct vio_dring_state *dr; 713e689cf4aSJeff Kirsher 714e689cf4aSJeff Kirsher dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 715d51bffd1SSowmini Varadhan netif_tx_wake_queue(txq); 716e689cf4aSJeff Kirsher } 717d51bffd1SSowmini Varadhan __netif_tx_unlock(txq); 718e689cf4aSJeff Kirsher } 719e689cf4aSJeff Kirsher 72069088822SSowmini Varadhan static inline bool port_is_up(struct vnet_port *vnet) 721e689cf4aSJeff Kirsher { 72269088822SSowmini Varadhan struct vio_driver_state *vio = &vnet->vio; 72369088822SSowmini Varadhan 72469088822SSowmini Varadhan return !!(vio->hs_state & VIO_HS_COMPLETE); 72569088822SSowmini Varadhan } 72669088822SSowmini Varadhan 72769088822SSowmini Varadhan static int vnet_event_napi(struct vnet_port *port, int budget) 72869088822SSowmini Varadhan { 729e689cf4aSJeff Kirsher struct vio_driver_state *vio = &port->vio; 730e689cf4aSJeff Kirsher int tx_wakeup, err; 73169088822SSowmini Varadhan int npkts = 0; 73269088822SSowmini Varadhan int event = (port->rx_event & LDC_EVENT_RESET); 733e689cf4aSJeff Kirsher 73469088822SSowmini Varadhan ldc_ctrl: 735e689cf4aSJeff Kirsher if (unlikely(event == LDC_EVENT_RESET || 736e689cf4aSJeff Kirsher event == LDC_EVENT_UP)) { 737e689cf4aSJeff Kirsher vio_link_state_change(vio, event); 738e689cf4aSJeff Kirsher 739e4defc77SDavid L Stevens if (event == LDC_EVENT_RESET) { 7408e2b60cdSDavid L Stevens vnet_port_reset(port); 741e689cf4aSJeff Kirsher vio_port_up(vio); 742e4defc77SDavid L Stevens } 74369088822SSowmini Varadhan port->rx_event = 0; 74469088822SSowmini Varadhan return 0; 745e689cf4aSJeff Kirsher } 74669088822SSowmini Varadhan /* We may have multiple LDC events in rx_event. Unroll send_events() */ 74769088822SSowmini Varadhan event = (port->rx_event & LDC_EVENT_UP); 74869088822SSowmini Varadhan port->rx_event &= ~(LDC_EVENT_RESET|LDC_EVENT_UP); 74969088822SSowmini Varadhan if (event == LDC_EVENT_UP) 75069088822SSowmini Varadhan goto ldc_ctrl; 75169088822SSowmini Varadhan event = port->rx_event; 75269088822SSowmini Varadhan if (!(event & LDC_EVENT_DATA_READY)) 75369088822SSowmini Varadhan return 0; 754e689cf4aSJeff Kirsher 75569088822SSowmini Varadhan /* we dont expect any other bits than RESET, UP, DATA_READY */ 75669088822SSowmini Varadhan BUG_ON(event != LDC_EVENT_DATA_READY); 757e689cf4aSJeff Kirsher 758e689cf4aSJeff Kirsher tx_wakeup = err = 0; 759e689cf4aSJeff Kirsher while (1) { 760e689cf4aSJeff Kirsher union { 761e689cf4aSJeff Kirsher struct vio_msg_tag tag; 762e689cf4aSJeff Kirsher u64 raw[8]; 763e689cf4aSJeff Kirsher } msgbuf; 764e689cf4aSJeff Kirsher 76569088822SSowmini Varadhan if (port->napi_resume) { 76669088822SSowmini Varadhan struct vio_dring_data *pkt = 76769088822SSowmini Varadhan (struct vio_dring_data *)&msgbuf; 76869088822SSowmini Varadhan struct vio_dring_state *dr = 76969088822SSowmini Varadhan &port->vio.drings[VIO_DRIVER_RX_RING]; 77069088822SSowmini Varadhan 77169088822SSowmini Varadhan pkt->tag.type = VIO_TYPE_DATA; 77269088822SSowmini Varadhan pkt->tag.stype = VIO_SUBTYPE_INFO; 77369088822SSowmini Varadhan pkt->tag.stype_env = VIO_DRING_DATA; 77469088822SSowmini Varadhan pkt->seq = dr->rcv_nxt; 775fe47c3c2SDwight Engen pkt->start_idx = vio_dring_next(dr, port->napi_stop_idx); 77669088822SSowmini Varadhan pkt->end_idx = -1; 77769088822SSowmini Varadhan goto napi_resume; 77869088822SSowmini Varadhan } 779e689cf4aSJeff Kirsher err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf)); 780e689cf4aSJeff Kirsher if (unlikely(err < 0)) { 781e689cf4aSJeff Kirsher if (err == -ECONNRESET) 782e689cf4aSJeff Kirsher vio_conn_reset(vio); 783e689cf4aSJeff Kirsher break; 784e689cf4aSJeff Kirsher } 785e689cf4aSJeff Kirsher if (err == 0) 786e689cf4aSJeff Kirsher break; 787e689cf4aSJeff Kirsher viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n", 788e689cf4aSJeff Kirsher msgbuf.tag.type, 789e689cf4aSJeff Kirsher msgbuf.tag.stype, 790e689cf4aSJeff Kirsher msgbuf.tag.stype_env, 791e689cf4aSJeff Kirsher msgbuf.tag.sid); 792e689cf4aSJeff Kirsher err = vio_validate_sid(vio, &msgbuf.tag); 793e689cf4aSJeff Kirsher if (err < 0) 794e689cf4aSJeff Kirsher break; 79569088822SSowmini Varadhan napi_resume: 796e689cf4aSJeff Kirsher if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) { 797e689cf4aSJeff Kirsher if (msgbuf.tag.stype == VIO_SUBTYPE_INFO) { 79869088822SSowmini Varadhan if (!port_is_up(port)) { 79969088822SSowmini Varadhan /* failures like handshake_failure() 80069088822SSowmini Varadhan * may have cleaned up dring, but 80169088822SSowmini Varadhan * NAPI polling may bring us here. 80269088822SSowmini Varadhan */ 80369088822SSowmini Varadhan err = -ECONNRESET; 80469088822SSowmini Varadhan break; 80569088822SSowmini Varadhan } 80669088822SSowmini Varadhan err = vnet_rx(port, &msgbuf, &npkts, budget); 80769088822SSowmini Varadhan if (npkts >= budget) 80869088822SSowmini Varadhan break; 8098c4ee3e7SSowmini Varadhan if (npkts == 0) 8108c4ee3e7SSowmini Varadhan break; 811e689cf4aSJeff Kirsher } else if (msgbuf.tag.stype == VIO_SUBTYPE_ACK) { 812e689cf4aSJeff Kirsher err = vnet_ack(port, &msgbuf); 813e689cf4aSJeff Kirsher if (err > 0) 814e689cf4aSJeff Kirsher tx_wakeup |= err; 815e689cf4aSJeff Kirsher } else if (msgbuf.tag.stype == VIO_SUBTYPE_NACK) { 816e689cf4aSJeff Kirsher err = vnet_nack(port, &msgbuf); 817e689cf4aSJeff Kirsher } 818e689cf4aSJeff Kirsher } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { 819e689cf4aSJeff Kirsher if (msgbuf.tag.stype_env == VNET_MCAST_INFO) 820e689cf4aSJeff Kirsher err = handle_mcast(port, &msgbuf); 821e689cf4aSJeff Kirsher else 822e689cf4aSJeff Kirsher err = vio_control_pkt_engine(vio, &msgbuf); 823e689cf4aSJeff Kirsher if (err) 824e689cf4aSJeff Kirsher break; 825e689cf4aSJeff Kirsher } else { 826e689cf4aSJeff Kirsher err = vnet_handle_unknown(port, &msgbuf); 827e689cf4aSJeff Kirsher } 828e689cf4aSJeff Kirsher if (err == -ECONNRESET) 829e689cf4aSJeff Kirsher break; 830e689cf4aSJeff Kirsher } 831e689cf4aSJeff Kirsher if (unlikely(tx_wakeup && err != -ECONNRESET)) 832d51bffd1SSowmini Varadhan maybe_tx_wakeup(port); 83369088822SSowmini Varadhan return npkts; 83469088822SSowmini Varadhan } 8351d311ad2SSowmini Varadhan 83669088822SSowmini Varadhan static int vnet_poll(struct napi_struct *napi, int budget) 83769088822SSowmini Varadhan { 83869088822SSowmini Varadhan struct vnet_port *port = container_of(napi, struct vnet_port, napi); 83969088822SSowmini Varadhan struct vio_driver_state *vio = &port->vio; 84069088822SSowmini Varadhan int processed = vnet_event_napi(port, budget); 84169088822SSowmini Varadhan 84269088822SSowmini Varadhan if (processed < budget) { 84369088822SSowmini Varadhan napi_complete(napi); 8447bd68bfdSSowmini Varadhan port->rx_event &= ~LDC_EVENT_DATA_READY; 84569088822SSowmini Varadhan vio_set_intr(vio->vdev->rx_ino, HV_INTR_ENABLED); 84669088822SSowmini Varadhan } 84769088822SSowmini Varadhan return processed; 84869088822SSowmini Varadhan } 84969088822SSowmini Varadhan 85069088822SSowmini Varadhan static void vnet_event(void *arg, int event) 85169088822SSowmini Varadhan { 85269088822SSowmini Varadhan struct vnet_port *port = arg; 85369088822SSowmini Varadhan struct vio_driver_state *vio = &port->vio; 85469088822SSowmini Varadhan 85569088822SSowmini Varadhan port->rx_event |= event; 85669088822SSowmini Varadhan vio_set_intr(vio->vdev->rx_ino, HV_INTR_DISABLED); 85769088822SSowmini Varadhan napi_schedule(&port->napi); 85869088822SSowmini Varadhan 859e689cf4aSJeff Kirsher } 860e689cf4aSJeff Kirsher 861d1015645SSowmini Varadhan static int __vnet_tx_trigger(struct vnet_port *port, u32 start) 862e689cf4aSJeff Kirsher { 863e689cf4aSJeff Kirsher struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 864e689cf4aSJeff Kirsher struct vio_dring_data hdr = { 865e689cf4aSJeff Kirsher .tag = { 866e689cf4aSJeff Kirsher .type = VIO_TYPE_DATA, 867e689cf4aSJeff Kirsher .stype = VIO_SUBTYPE_INFO, 868e689cf4aSJeff Kirsher .stype_env = VIO_DRING_DATA, 869e689cf4aSJeff Kirsher .sid = vio_send_sid(&port->vio), 870e689cf4aSJeff Kirsher }, 871e689cf4aSJeff Kirsher .dring_ident = dr->ident, 872d1015645SSowmini Varadhan .start_idx = start, 873e689cf4aSJeff Kirsher .end_idx = (u32) -1, 874e689cf4aSJeff Kirsher }; 875e689cf4aSJeff Kirsher int err, delay; 876adddc32dSSowmini Varadhan int retries = 0; 877e689cf4aSJeff Kirsher 878d1015645SSowmini Varadhan if (port->stop_rx) { 879d1015645SSowmini Varadhan err = vnet_send_ack(port, 880d1015645SSowmini Varadhan &port->vio.drings[VIO_DRIVER_RX_RING], 881d1015645SSowmini Varadhan port->stop_rx_idx, -1, 882d1015645SSowmini Varadhan VIO_DRING_STOPPED); 883d1015645SSowmini Varadhan if (err <= 0) 884d1015645SSowmini Varadhan return err; 885d1015645SSowmini Varadhan } 886d1015645SSowmini Varadhan 887e689cf4aSJeff Kirsher hdr.seq = dr->snd_nxt; 888e689cf4aSJeff Kirsher delay = 1; 889e689cf4aSJeff Kirsher do { 890e689cf4aSJeff Kirsher err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); 891e689cf4aSJeff Kirsher if (err > 0) { 892e689cf4aSJeff Kirsher dr->snd_nxt++; 893e689cf4aSJeff Kirsher break; 894e689cf4aSJeff Kirsher } 895e689cf4aSJeff Kirsher udelay(delay); 896e689cf4aSJeff Kirsher if ((delay <<= 1) > 128) 897e689cf4aSJeff Kirsher delay = 128; 898adddc32dSSowmini Varadhan if (retries++ > VNET_MAX_RETRIES) 899adddc32dSSowmini Varadhan break; 900e689cf4aSJeff Kirsher } while (err == -EAGAIN); 901e689cf4aSJeff Kirsher 902e689cf4aSJeff Kirsher return err; 903e689cf4aSJeff Kirsher } 904e689cf4aSJeff Kirsher 905e689cf4aSJeff Kirsher struct vnet_port *__tx_port_find(struct vnet *vp, struct sk_buff *skb) 906e689cf4aSJeff Kirsher { 907e689cf4aSJeff Kirsher unsigned int hash = vnet_hashfn(skb->data); 908e689cf4aSJeff Kirsher struct hlist_head *hp = &vp->port_hash[hash]; 909e689cf4aSJeff Kirsher struct vnet_port *port; 910e689cf4aSJeff Kirsher 9112a968dd8SSowmini Varadhan hlist_for_each_entry_rcu(port, hp, hash) { 9128266f5fcSDavid L Stevens if (!port_is_up(port)) 9138266f5fcSDavid L Stevens continue; 9142e42e474SJoe Perches if (ether_addr_equal(port->raddr, skb->data)) 915e689cf4aSJeff Kirsher return port; 916e689cf4aSJeff Kirsher } 9172a968dd8SSowmini Varadhan list_for_each_entry_rcu(port, &vp->port_list, list) { 9188266f5fcSDavid L Stevens if (!port->switch_port) 9198266f5fcSDavid L Stevens continue; 9208266f5fcSDavid L Stevens if (!port_is_up(port)) 9218266f5fcSDavid L Stevens continue; 922e689cf4aSJeff Kirsher return port; 923e689cf4aSJeff Kirsher } 9248266f5fcSDavid L Stevens return NULL; 9258266f5fcSDavid L Stevens } 926e689cf4aSJeff Kirsher 9278e845f4cSDavid L Stevens static struct sk_buff *vnet_clean_tx_ring(struct vnet_port *port, 9288e845f4cSDavid L Stevens unsigned *pending) 9298e845f4cSDavid L Stevens { 9308e845f4cSDavid L Stevens struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 9318e845f4cSDavid L Stevens struct sk_buff *skb = NULL; 9328e845f4cSDavid L Stevens int i, txi; 9338e845f4cSDavid L Stevens 9348e845f4cSDavid L Stevens *pending = 0; 9358e845f4cSDavid L Stevens 9368e2b60cdSDavid L Stevens txi = dr->prod; 9378e845f4cSDavid L Stevens for (i = 0; i < VNET_TX_RING_SIZE; ++i) { 9388e845f4cSDavid L Stevens struct vio_net_desc *d; 9398e845f4cSDavid L Stevens 9408e2b60cdSDavid L Stevens --txi; 9418e2b60cdSDavid L Stevens if (txi < 0) 9428e2b60cdSDavid L Stevens txi = VNET_TX_RING_SIZE-1; 9438e2b60cdSDavid L Stevens 9448e845f4cSDavid L Stevens d = vio_dring_entry(dr, txi); 9458e845f4cSDavid L Stevens 9468e2b60cdSDavid L Stevens if (d->hdr.state == VIO_DESC_READY) { 9478e2b60cdSDavid L Stevens (*pending)++; 9488e2b60cdSDavid L Stevens continue; 9498e2b60cdSDavid L Stevens } 9508e845f4cSDavid L Stevens if (port->tx_bufs[txi].skb) { 9518e2b60cdSDavid L Stevens if (d->hdr.state != VIO_DESC_DONE) 9528e2b60cdSDavid L Stevens pr_notice("invalid ring buffer state %d\n", 9538e2b60cdSDavid L Stevens d->hdr.state); 9548e845f4cSDavid L Stevens BUG_ON(port->tx_bufs[txi].skb->next); 9558e845f4cSDavid L Stevens 9568e845f4cSDavid L Stevens port->tx_bufs[txi].skb->next = skb; 9578e845f4cSDavid L Stevens skb = port->tx_bufs[txi].skb; 9588e845f4cSDavid L Stevens port->tx_bufs[txi].skb = NULL; 9598e845f4cSDavid L Stevens 9608e845f4cSDavid L Stevens ldc_unmap(port->vio.lp, 9618e845f4cSDavid L Stevens port->tx_bufs[txi].cookies, 9628e845f4cSDavid L Stevens port->tx_bufs[txi].ncookies); 9638e2b60cdSDavid L Stevens } else if (d->hdr.state == VIO_DESC_FREE) 9648e845f4cSDavid L Stevens break; 9658e2b60cdSDavid L Stevens d->hdr.state = VIO_DESC_FREE; 9668e845f4cSDavid L Stevens } 9678e845f4cSDavid L Stevens return skb; 9688e845f4cSDavid L Stevens } 9698e845f4cSDavid L Stevens 9708e845f4cSDavid L Stevens static inline void vnet_free_skbs(struct sk_buff *skb) 9718e845f4cSDavid L Stevens { 9728e845f4cSDavid L Stevens struct sk_buff *next; 9738e845f4cSDavid L Stevens 9748e845f4cSDavid L Stevens while (skb) { 9758e845f4cSDavid L Stevens next = skb->next; 9768e845f4cSDavid L Stevens skb->next = NULL; 9778e845f4cSDavid L Stevens dev_kfree_skb(skb); 9788e845f4cSDavid L Stevens skb = next; 9798e845f4cSDavid L Stevens } 9808e845f4cSDavid L Stevens } 9818e845f4cSDavid L Stevens 9828e845f4cSDavid L Stevens static void vnet_clean_timer_expire(unsigned long port0) 9838e845f4cSDavid L Stevens { 9848e845f4cSDavid L Stevens struct vnet_port *port = (struct vnet_port *)port0; 9858e845f4cSDavid L Stevens struct sk_buff *freeskbs; 9868e845f4cSDavid L Stevens unsigned pending; 9878e845f4cSDavid L Stevens 98813b13dd9SSowmini Varadhan netif_tx_lock(port->vp->dev); 9898e845f4cSDavid L Stevens freeskbs = vnet_clean_tx_ring(port, &pending); 99013b13dd9SSowmini Varadhan netif_tx_unlock(port->vp->dev); 9918e845f4cSDavid L Stevens 9928e845f4cSDavid L Stevens vnet_free_skbs(freeskbs); 9938e845f4cSDavid L Stevens 9948e845f4cSDavid L Stevens if (pending) 9958e845f4cSDavid L Stevens (void)mod_timer(&port->clean_timer, 9968e845f4cSDavid L Stevens jiffies + VNET_CLEAN_TIMEOUT); 9978e845f4cSDavid L Stevens else 9988e845f4cSDavid L Stevens del_timer(&port->clean_timer); 9998e845f4cSDavid L Stevens } 10008e845f4cSDavid L Stevens 1001da38c564SDavid L Stevens static inline int vnet_skb_map(struct ldc_channel *lp, struct sk_buff *skb, 1002da38c564SDavid L Stevens struct ldc_trans_cookie *cookies, int ncookies, 1003da38c564SDavid L Stevens unsigned int map_perm) 1004da38c564SDavid L Stevens { 1005da38c564SDavid L Stevens int i, nc, err, blen; 1006da38c564SDavid L Stevens 1007da38c564SDavid L Stevens /* header */ 1008da38c564SDavid L Stevens blen = skb_headlen(skb); 1009da38c564SDavid L Stevens if (blen < ETH_ZLEN) 1010da38c564SDavid L Stevens blen = ETH_ZLEN; 1011da38c564SDavid L Stevens blen += VNET_PACKET_SKIP; 1012da38c564SDavid L Stevens blen += 8 - (blen & 7); 1013da38c564SDavid L Stevens 1014da38c564SDavid L Stevens err = ldc_map_single(lp, skb->data-VNET_PACKET_SKIP, blen, cookies, 1015da38c564SDavid L Stevens ncookies, map_perm); 1016da38c564SDavid L Stevens if (err < 0) 1017da38c564SDavid L Stevens return err; 1018da38c564SDavid L Stevens nc = err; 1019da38c564SDavid L Stevens 1020da38c564SDavid L Stevens for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 1021da38c564SDavid L Stevens skb_frag_t *f = &skb_shinfo(skb)->frags[i]; 1022da38c564SDavid L Stevens u8 *vaddr; 1023da38c564SDavid L Stevens 1024da38c564SDavid L Stevens if (nc < ncookies) { 1025da38c564SDavid L Stevens vaddr = kmap_atomic(skb_frag_page(f)); 1026da38c564SDavid L Stevens blen = skb_frag_size(f); 1027da38c564SDavid L Stevens blen += 8 - (blen & 7); 1028da38c564SDavid L Stevens err = ldc_map_single(lp, vaddr + f->page_offset, 1029da38c564SDavid L Stevens blen, cookies + nc, ncookies - nc, 1030da38c564SDavid L Stevens map_perm); 1031da38c564SDavid L Stevens kunmap_atomic(vaddr); 1032da38c564SDavid L Stevens } else { 1033da38c564SDavid L Stevens err = -EMSGSIZE; 1034da38c564SDavid L Stevens } 1035da38c564SDavid L Stevens 1036da38c564SDavid L Stevens if (err < 0) { 1037da38c564SDavid L Stevens ldc_unmap(lp, cookies, nc); 1038da38c564SDavid L Stevens return err; 1039da38c564SDavid L Stevens } 1040da38c564SDavid L Stevens nc += err; 1041da38c564SDavid L Stevens } 1042da38c564SDavid L Stevens return nc; 1043da38c564SDavid L Stevens } 1044da38c564SDavid L Stevens 1045da38c564SDavid L Stevens static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, int ncookies) 10468e845f4cSDavid L Stevens { 10478e845f4cSDavid L Stevens struct sk_buff *nskb; 1048da38c564SDavid L Stevens int i, len, pad, docopy; 10498e845f4cSDavid L Stevens 10508e845f4cSDavid L Stevens len = skb->len; 10518e845f4cSDavid L Stevens pad = 0; 10528e845f4cSDavid L Stevens if (len < ETH_ZLEN) { 10538e845f4cSDavid L Stevens pad += ETH_ZLEN - skb->len; 10548e845f4cSDavid L Stevens len += pad; 10558e845f4cSDavid L Stevens } 10568e845f4cSDavid L Stevens len += VNET_PACKET_SKIP; 10578e845f4cSDavid L Stevens pad += 8 - (len & 7); 10588e845f4cSDavid L Stevens 1059da38c564SDavid L Stevens /* make sure we have enough cookies and alignment in every frag */ 1060da38c564SDavid L Stevens docopy = skb_shinfo(skb)->nr_frags >= ncookies; 1061da38c564SDavid L Stevens for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 1062da38c564SDavid L Stevens skb_frag_t *f = &skb_shinfo(skb)->frags[i]; 1063da38c564SDavid L Stevens 1064da38c564SDavid L Stevens docopy |= f->page_offset & 7; 1065da38c564SDavid L Stevens } 10668e845f4cSDavid L Stevens if (((unsigned long)skb->data & 7) != VNET_PACKET_SKIP || 10678e845f4cSDavid L Stevens skb_tailroom(skb) < pad || 1068da38c564SDavid L Stevens skb_headroom(skb) < VNET_PACKET_SKIP || docopy) { 10691b6b0a47SDavid L Stevens int start = 0, offset; 10701b6b0a47SDavid L Stevens __wsum csum; 10716d0ba919SDavid L Stevens 1072da38c564SDavid L Stevens len = skb->len > ETH_ZLEN ? skb->len : ETH_ZLEN; 1073da38c564SDavid L Stevens nskb = alloc_and_align_skb(skb->dev, len); 1074da38c564SDavid L Stevens if (nskb == NULL) { 1075da38c564SDavid L Stevens dev_kfree_skb(skb); 1076da38c564SDavid L Stevens return NULL; 1077da38c564SDavid L Stevens } 10788e845f4cSDavid L Stevens skb_reserve(nskb, VNET_PACKET_SKIP); 10796d0ba919SDavid L Stevens 10806d0ba919SDavid L Stevens nskb->protocol = skb->protocol; 10816d0ba919SDavid L Stevens offset = skb_mac_header(skb) - skb->data; 10826d0ba919SDavid L Stevens skb_set_mac_header(nskb, offset); 10836d0ba919SDavid L Stevens offset = skb_network_header(skb) - skb->data; 10846d0ba919SDavid L Stevens skb_set_network_header(nskb, offset); 10856d0ba919SDavid L Stevens offset = skb_transport_header(skb) - skb->data; 10866d0ba919SDavid L Stevens skb_set_transport_header(nskb, offset); 10876d0ba919SDavid L Stevens 10881b6b0a47SDavid L Stevens offset = 0; 10896d0ba919SDavid L Stevens nskb->csum_offset = skb->csum_offset; 10906d0ba919SDavid L Stevens nskb->ip_summed = skb->ip_summed; 10916d0ba919SDavid L Stevens 10921b6b0a47SDavid L Stevens if (skb->ip_summed == CHECKSUM_PARTIAL) 10931b6b0a47SDavid L Stevens start = skb_checksum_start_offset(skb); 10941b6b0a47SDavid L Stevens if (start) { 10951b6b0a47SDavid L Stevens struct iphdr *iph = ip_hdr(nskb); 10961b6b0a47SDavid L Stevens int offset = start + nskb->csum_offset; 10971b6b0a47SDavid L Stevens 10981b6b0a47SDavid L Stevens if (skb_copy_bits(skb, 0, nskb->data, start)) { 10991b6b0a47SDavid L Stevens dev_kfree_skb(nskb); 11001b6b0a47SDavid L Stevens dev_kfree_skb(skb); 11011b6b0a47SDavid L Stevens return NULL; 11021b6b0a47SDavid L Stevens } 11031b6b0a47SDavid L Stevens *(__sum16 *)(skb->data + offset) = 0; 11041b6b0a47SDavid L Stevens csum = skb_copy_and_csum_bits(skb, start, 11051b6b0a47SDavid L Stevens nskb->data + start, 11061b6b0a47SDavid L Stevens skb->len - start, 0); 11071b6b0a47SDavid L Stevens if (iph->protocol == IPPROTO_TCP || 11081b6b0a47SDavid L Stevens iph->protocol == IPPROTO_UDP) { 11091b6b0a47SDavid L Stevens csum = csum_tcpudp_magic(iph->saddr, iph->daddr, 11101b6b0a47SDavid L Stevens skb->len - start, 11111b6b0a47SDavid L Stevens iph->protocol, csum); 11121b6b0a47SDavid L Stevens } 11131b6b0a47SDavid L Stevens *(__sum16 *)(nskb->data + offset) = csum; 11141b6b0a47SDavid L Stevens 11151b6b0a47SDavid L Stevens nskb->ip_summed = CHECKSUM_NONE; 11161b6b0a47SDavid L Stevens } else if (skb_copy_bits(skb, 0, nskb->data, skb->len)) { 11178e845f4cSDavid L Stevens dev_kfree_skb(nskb); 11188e845f4cSDavid L Stevens dev_kfree_skb(skb); 11198e845f4cSDavid L Stevens return NULL; 11208e845f4cSDavid L Stevens } 11218e845f4cSDavid L Stevens (void)skb_put(nskb, skb->len); 11229a72dd4dSDavid L Stevens if (skb_is_gso(skb)) { 11239a72dd4dSDavid L Stevens skb_shinfo(nskb)->gso_size = skb_shinfo(skb)->gso_size; 11249a72dd4dSDavid L Stevens skb_shinfo(nskb)->gso_type = skb_shinfo(skb)->gso_type; 11259a72dd4dSDavid L Stevens } 112644ba582bSDavid L Stevens nskb->queue_mapping = skb->queue_mapping; 11278e845f4cSDavid L Stevens dev_kfree_skb(skb); 11288e845f4cSDavid L Stevens skb = nskb; 11298e845f4cSDavid L Stevens } 11308e845f4cSDavid L Stevens return skb; 11318e845f4cSDavid L Stevens } 11328e845f4cSDavid L Stevens 1133d51bffd1SSowmini Varadhan static u16 1134d51bffd1SSowmini Varadhan vnet_select_queue(struct net_device *dev, struct sk_buff *skb, 1135d51bffd1SSowmini Varadhan void *accel_priv, select_queue_fallback_t fallback) 1136d51bffd1SSowmini Varadhan { 1137d51bffd1SSowmini Varadhan struct vnet *vp = netdev_priv(dev); 1138d51bffd1SSowmini Varadhan struct vnet_port *port = __tx_port_find(vp, skb); 1139d51bffd1SSowmini Varadhan 1140c647cc3fSDavid L Stevens if (port == NULL) 1141c647cc3fSDavid L Stevens return 0; 1142d51bffd1SSowmini Varadhan return port->q_index; 1143d51bffd1SSowmini Varadhan } 1144d51bffd1SSowmini Varadhan 11459a72dd4dSDavid L Stevens static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev); 11469a72dd4dSDavid L Stevens 11479a72dd4dSDavid L Stevens static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb) 11489a72dd4dSDavid L Stevens { 11499a72dd4dSDavid L Stevens struct net_device *dev = port->vp->dev; 11509a72dd4dSDavid L Stevens struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 11519a72dd4dSDavid L Stevens struct sk_buff *segs; 1152368e36edSDavid L Stevens int maclen, datalen; 11539a72dd4dSDavid L Stevens int status; 1154368e36edSDavid L Stevens int gso_size, gso_type, gso_segs; 1155368e36edSDavid L Stevens int hlen = skb_transport_header(skb) - skb_mac_header(skb); 1156368e36edSDavid L Stevens int proto = IPPROTO_IP; 11579a72dd4dSDavid L Stevens 1158368e36edSDavid L Stevens if (skb->protocol == htons(ETH_P_IP)) 1159368e36edSDavid L Stevens proto = ip_hdr(skb)->protocol; 1160368e36edSDavid L Stevens else if (skb->protocol == htons(ETH_P_IPV6)) 1161368e36edSDavid L Stevens proto = ipv6_hdr(skb)->nexthdr; 1162368e36edSDavid L Stevens 1163368e36edSDavid L Stevens if (proto == IPPROTO_TCP) 1164368e36edSDavid L Stevens hlen += tcp_hdr(skb)->doff * 4; 1165368e36edSDavid L Stevens else if (proto == IPPROTO_UDP) 1166368e36edSDavid L Stevens hlen += sizeof(struct udphdr); 1167368e36edSDavid L Stevens else { 1168368e36edSDavid L Stevens pr_err("vnet_handle_offloads GSO with unknown transport " 1169368e36edSDavid L Stevens "protocol %d tproto %d\n", skb->protocol, proto); 1170368e36edSDavid L Stevens hlen = 128; /* XXX */ 1171368e36edSDavid L Stevens } 1172368e36edSDavid L Stevens datalen = port->tsolen - hlen; 1173368e36edSDavid L Stevens 1174368e36edSDavid L Stevens gso_size = skb_shinfo(skb)->gso_size; 1175368e36edSDavid L Stevens gso_type = skb_shinfo(skb)->gso_type; 1176368e36edSDavid L Stevens gso_segs = skb_shinfo(skb)->gso_segs; 1177368e36edSDavid L Stevens 1178368e36edSDavid L Stevens if (port->tso && gso_size < datalen) 1179368e36edSDavid L Stevens gso_segs = DIV_ROUND_UP(skb->len - hlen, datalen); 1180368e36edSDavid L Stevens 1181368e36edSDavid L Stevens if (unlikely(vnet_tx_dring_avail(dr) < gso_segs)) { 11829a72dd4dSDavid L Stevens struct netdev_queue *txq; 11839a72dd4dSDavid L Stevens 11849a72dd4dSDavid L Stevens txq = netdev_get_tx_queue(dev, port->q_index); 11859a72dd4dSDavid L Stevens netif_tx_stop_queue(txq); 11869a72dd4dSDavid L Stevens if (vnet_tx_dring_avail(dr) < skb_shinfo(skb)->gso_segs) 11879a72dd4dSDavid L Stevens return NETDEV_TX_BUSY; 11889a72dd4dSDavid L Stevens netif_tx_wake_queue(txq); 11899a72dd4dSDavid L Stevens } 11909a72dd4dSDavid L Stevens 11919a72dd4dSDavid L Stevens maclen = skb_network_header(skb) - skb_mac_header(skb); 11929a72dd4dSDavid L Stevens skb_pull(skb, maclen); 11939a72dd4dSDavid L Stevens 1194368e36edSDavid L Stevens if (port->tso && gso_size < datalen) { 1195f7d70f74SDavid L Stevens if (skb_unclone(skb, GFP_ATOMIC)) 1196f7d70f74SDavid L Stevens goto out_dropped; 1197f7d70f74SDavid L Stevens 1198368e36edSDavid L Stevens /* segment to TSO size */ 1199368e36edSDavid L Stevens skb_shinfo(skb)->gso_size = datalen; 1200368e36edSDavid L Stevens skb_shinfo(skb)->gso_segs = gso_segs; 12019a72dd4dSDavid L Stevens } 1202f7d70f74SDavid L Stevens segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO); 1203f7d70f74SDavid L Stevens if (IS_ERR(segs)) 1204f7d70f74SDavid L Stevens goto out_dropped; 12059a72dd4dSDavid L Stevens 12069a72dd4dSDavid L Stevens skb_push(skb, maclen); 12079a72dd4dSDavid L Stevens skb_reset_mac_header(skb); 12089a72dd4dSDavid L Stevens 12099a72dd4dSDavid L Stevens status = 0; 12109a72dd4dSDavid L Stevens while (segs) { 12119a72dd4dSDavid L Stevens struct sk_buff *curr = segs; 12129a72dd4dSDavid L Stevens 12139a72dd4dSDavid L Stevens segs = segs->next; 12149a72dd4dSDavid L Stevens curr->next = NULL; 1215368e36edSDavid L Stevens if (port->tso && curr->len > dev->mtu) { 1216368e36edSDavid L Stevens skb_shinfo(curr)->gso_size = gso_size; 1217368e36edSDavid L Stevens skb_shinfo(curr)->gso_type = gso_type; 1218368e36edSDavid L Stevens skb_shinfo(curr)->gso_segs = 1219368e36edSDavid L Stevens DIV_ROUND_UP(curr->len - hlen, gso_size); 1220368e36edSDavid L Stevens } else 1221368e36edSDavid L Stevens skb_shinfo(curr)->gso_size = 0; 12229a72dd4dSDavid L Stevens 12239a72dd4dSDavid L Stevens skb_push(curr, maclen); 12249a72dd4dSDavid L Stevens skb_reset_mac_header(curr); 12259a72dd4dSDavid L Stevens memcpy(skb_mac_header(curr), skb_mac_header(skb), 12269a72dd4dSDavid L Stevens maclen); 12279a72dd4dSDavid L Stevens curr->csum_start = skb_transport_header(curr) - curr->head; 12289a72dd4dSDavid L Stevens if (ip_hdr(curr)->protocol == IPPROTO_TCP) 12299a72dd4dSDavid L Stevens curr->csum_offset = offsetof(struct tcphdr, check); 12309a72dd4dSDavid L Stevens else if (ip_hdr(curr)->protocol == IPPROTO_UDP) 12319a72dd4dSDavid L Stevens curr->csum_offset = offsetof(struct udphdr, check); 12329a72dd4dSDavid L Stevens 12339a72dd4dSDavid L Stevens if (!(status & NETDEV_TX_MASK)) 12349a72dd4dSDavid L Stevens status = vnet_start_xmit(curr, dev); 12359a72dd4dSDavid L Stevens if (status & NETDEV_TX_MASK) 12369a72dd4dSDavid L Stevens dev_kfree_skb_any(curr); 12379a72dd4dSDavid L Stevens } 12389a72dd4dSDavid L Stevens 12399a72dd4dSDavid L Stevens if (!(status & NETDEV_TX_MASK)) 12409a72dd4dSDavid L Stevens dev_kfree_skb_any(skb); 12419a72dd4dSDavid L Stevens return status; 1242f7d70f74SDavid L Stevens out_dropped: 1243f7d70f74SDavid L Stevens dev->stats.tx_dropped++; 1244f7d70f74SDavid L Stevens dev_kfree_skb_any(skb); 1245f7d70f74SDavid L Stevens return NETDEV_TX_OK; 12469a72dd4dSDavid L Stevens } 12479a72dd4dSDavid L Stevens 1248e689cf4aSJeff Kirsher static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) 1249e689cf4aSJeff Kirsher { 1250e689cf4aSJeff Kirsher struct vnet *vp = netdev_priv(dev); 12512a968dd8SSowmini Varadhan struct vnet_port *port = NULL; 1252e689cf4aSJeff Kirsher struct vio_dring_state *dr; 1253e689cf4aSJeff Kirsher struct vio_net_desc *d; 1254e689cf4aSJeff Kirsher unsigned int len; 12558e845f4cSDavid L Stevens struct sk_buff *freeskbs = NULL; 12568e845f4cSDavid L Stevens int i, err, txi; 12578e845f4cSDavid L Stevens unsigned pending = 0; 1258d51bffd1SSowmini Varadhan struct netdev_queue *txq; 1259e689cf4aSJeff Kirsher 12602a968dd8SSowmini Varadhan rcu_read_lock(); 126113b13dd9SSowmini Varadhan port = __tx_port_find(vp, skb); 1262df20286aSSowmini Varadhan if (unlikely(!port)) { 1263df20286aSSowmini Varadhan rcu_read_unlock(); 12648e845f4cSDavid L Stevens goto out_dropped; 1265df20286aSSowmini Varadhan } 12668e845f4cSDavid L Stevens 1267368e36edSDavid L Stevens if (skb_is_gso(skb) && skb->len > port->tsolen) { 12689a72dd4dSDavid L Stevens err = vnet_handle_offloads(port, skb); 12699a72dd4dSDavid L Stevens rcu_read_unlock(); 12709a72dd4dSDavid L Stevens return err; 12719a72dd4dSDavid L Stevens } 12729a72dd4dSDavid L Stevens 1273368e36edSDavid L Stevens if (!skb_is_gso(skb) && skb->len > port->rmtu) { 1274a2b78e9bSDavid L Stevens unsigned long localmtu = port->rmtu - ETH_HLEN; 1275a2b78e9bSDavid L Stevens 1276a2b78e9bSDavid L Stevens if (vio_version_after_eq(&port->vio, 1, 3)) 1277a2b78e9bSDavid L Stevens localmtu -= VLAN_HLEN; 1278a2b78e9bSDavid L Stevens 1279a2b78e9bSDavid L Stevens if (skb->protocol == htons(ETH_P_IP)) { 1280a2b78e9bSDavid L Stevens struct flowi4 fl4; 1281a2b78e9bSDavid L Stevens struct rtable *rt = NULL; 1282a2b78e9bSDavid L Stevens 1283a2b78e9bSDavid L Stevens memset(&fl4, 0, sizeof(fl4)); 1284a2b78e9bSDavid L Stevens fl4.flowi4_oif = dev->ifindex; 1285a2b78e9bSDavid L Stevens fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); 1286a2b78e9bSDavid L Stevens fl4.daddr = ip_hdr(skb)->daddr; 1287a2b78e9bSDavid L Stevens fl4.saddr = ip_hdr(skb)->saddr; 1288a2b78e9bSDavid L Stevens 1289a2b78e9bSDavid L Stevens rt = ip_route_output_key(dev_net(dev), &fl4); 12902a968dd8SSowmini Varadhan rcu_read_unlock(); 1291a2b78e9bSDavid L Stevens if (!IS_ERR(rt)) { 1292a2b78e9bSDavid L Stevens skb_dst_set(skb, &rt->dst); 1293a2b78e9bSDavid L Stevens icmp_send(skb, ICMP_DEST_UNREACH, 1294a2b78e9bSDavid L Stevens ICMP_FRAG_NEEDED, 1295a2b78e9bSDavid L Stevens htonl(localmtu)); 1296a2b78e9bSDavid L Stevens } 1297a2b78e9bSDavid L Stevens } 1298a2b78e9bSDavid L Stevens #if IS_ENABLED(CONFIG_IPV6) 1299a2b78e9bSDavid L Stevens else if (skb->protocol == htons(ETH_P_IPV6)) 1300a2b78e9bSDavid L Stevens icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, localmtu); 1301a2b78e9bSDavid L Stevens #endif 130242db672dSDavid L Stevens goto out_dropped; 1303a2b78e9bSDavid L Stevens } 130442db672dSDavid L Stevens 1305da38c564SDavid L Stevens skb = vnet_skb_shape(skb, 2); 1306da38c564SDavid L Stevens 13079cdfe2c7SDavid L Stevens if (unlikely(!skb)) 1308da38c564SDavid L Stevens goto out_dropped; 1309da38c564SDavid L Stevens 13101b6b0a47SDavid L Stevens if (skb->ip_summed == CHECKSUM_PARTIAL) 13111b6b0a47SDavid L Stevens vnet_fullcsum(skb); 13121b6b0a47SDavid L Stevens 1313e689cf4aSJeff Kirsher dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 1314d51bffd1SSowmini Varadhan i = skb_get_queue_mapping(skb); 1315d51bffd1SSowmini Varadhan txq = netdev_get_tx_queue(dev, i); 1316d0aedcd4SDwight Engen if (unlikely(vnet_tx_dring_avail(dr) < 1)) { 1317d51bffd1SSowmini Varadhan if (!netif_tx_queue_stopped(txq)) { 1318d51bffd1SSowmini Varadhan netif_tx_stop_queue(txq); 1319e689cf4aSJeff Kirsher 1320e689cf4aSJeff Kirsher /* This is a hard error, log it. */ 1321e689cf4aSJeff Kirsher netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); 1322e689cf4aSJeff Kirsher dev->stats.tx_errors++; 1323e689cf4aSJeff Kirsher } 13242a968dd8SSowmini Varadhan rcu_read_unlock(); 1325e689cf4aSJeff Kirsher return NETDEV_TX_BUSY; 1326e689cf4aSJeff Kirsher } 1327e689cf4aSJeff Kirsher 1328e689cf4aSJeff Kirsher d = vio_dring_cur(dr); 1329e689cf4aSJeff Kirsher 13308e845f4cSDavid L Stevens txi = dr->prod; 13318e845f4cSDavid L Stevens 13328e845f4cSDavid L Stevens freeskbs = vnet_clean_tx_ring(port, &pending); 13338e845f4cSDavid L Stevens 13348e845f4cSDavid L Stevens BUG_ON(port->tx_bufs[txi].skb); 1335e689cf4aSJeff Kirsher 1336e689cf4aSJeff Kirsher len = skb->len; 13378e845f4cSDavid L Stevens if (len < ETH_ZLEN) 1338e689cf4aSJeff Kirsher len = ETH_ZLEN; 13398e845f4cSDavid L Stevens 1340da38c564SDavid L Stevens err = vnet_skb_map(port->vio.lp, skb, port->tx_bufs[txi].cookies, 2, 13418e845f4cSDavid L Stevens (LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_RW)); 13428e845f4cSDavid L Stevens if (err < 0) { 13438e845f4cSDavid L Stevens netdev_info(dev, "tx buffer map error %d\n", err); 134413b13dd9SSowmini Varadhan goto out_dropped; 1345e689cf4aSJeff Kirsher } 1346da38c564SDavid L Stevens 1347da38c564SDavid L Stevens port->tx_bufs[txi].skb = skb; 1348da38c564SDavid L Stevens skb = NULL; 13498e845f4cSDavid L Stevens port->tx_bufs[txi].ncookies = err; 1350e689cf4aSJeff Kirsher 13511f6394e3SSowmini Varadhan /* We don't rely on the ACKs to free the skb in vnet_start_xmit(), 13521f6394e3SSowmini Varadhan * thus it is safe to not set VIO_ACK_ENABLE for each transmission: 13531f6394e3SSowmini Varadhan * the protocol itself does not require it as long as the peer 13541f6394e3SSowmini Varadhan * sends a VIO_SUBTYPE_ACK for VIO_DRING_STOPPED. 13551f6394e3SSowmini Varadhan * 13561f6394e3SSowmini Varadhan * An ACK for every packet in the ring is expensive as the 13571f6394e3SSowmini Varadhan * sending of LDC messages is slow and affects performance. 13581f6394e3SSowmini Varadhan */ 13591f6394e3SSowmini Varadhan d->hdr.ack = VIO_ACK_DISABLE; 1360e689cf4aSJeff Kirsher d->size = len; 13618e845f4cSDavid L Stevens d->ncookies = port->tx_bufs[txi].ncookies; 1362e689cf4aSJeff Kirsher for (i = 0; i < d->ncookies; i++) 13638e845f4cSDavid L Stevens d->cookies[i] = port->tx_bufs[txi].cookies[i]; 13646d0ba919SDavid L Stevens if (vio_version_after_eq(&port->vio, 1, 7)) { 13656d0ba919SDavid L Stevens struct vio_net_dext *dext = vio_net_ext(d); 13666d0ba919SDavid L Stevens 13676d0ba919SDavid L Stevens memset(dext, 0, sizeof(*dext)); 1368368e36edSDavid L Stevens if (skb_is_gso(port->tx_bufs[txi].skb)) { 1369368e36edSDavid L Stevens dext->ipv4_lso_mss = skb_shinfo(port->tx_bufs[txi].skb) 1370368e36edSDavid L Stevens ->gso_size; 1371368e36edSDavid L Stevens dext->flags |= VNET_PKT_IPV4_LSO; 1372368e36edSDavid L Stevens } 13736d0ba919SDavid L Stevens if (vio_version_after_eq(&port->vio, 1, 8) && 13746d0ba919SDavid L Stevens !port->switch_port) { 13756d0ba919SDavid L Stevens dext->flags |= VNET_PKT_HCK_IPV4_HDRCKSUM_OK; 13766d0ba919SDavid L Stevens dext->flags |= VNET_PKT_HCK_FULLCKSUM_OK; 13776d0ba919SDavid L Stevens } 13786d0ba919SDavid L Stevens } 1379e689cf4aSJeff Kirsher 1380e689cf4aSJeff Kirsher /* This has to be a non-SMP write barrier because we are writing 1381e689cf4aSJeff Kirsher * to memory which is shared with the peer LDOM. 1382e689cf4aSJeff Kirsher */ 1383e689cf4aSJeff Kirsher wmb(); 1384e689cf4aSJeff Kirsher 1385e689cf4aSJeff Kirsher d->hdr.state = VIO_DESC_READY; 1386e689cf4aSJeff Kirsher 1387d1015645SSowmini Varadhan /* Exactly one ldc "start" trigger (for dr->cons) needs to be sent 1388d1015645SSowmini Varadhan * to notify the consumer that some descriptors are READY. 1389d1015645SSowmini Varadhan * After that "start" trigger, no additional triggers are needed until 1390d1015645SSowmini Varadhan * a DRING_STOPPED is received from the consumer. The dr->cons field 1391d1015645SSowmini Varadhan * (set up by vnet_ack()) has the value of the next dring index 1392d1015645SSowmini Varadhan * that has not yet been ack-ed. We send a "start" trigger here 1393d1015645SSowmini Varadhan * if, and only if, start_cons is true (reset it afterward). Conversely, 1394d1015645SSowmini Varadhan * vnet_ack() should check if the dring corresponding to cons 1395d1015645SSowmini Varadhan * is marked READY, but start_cons was false. 1396d1015645SSowmini Varadhan * If so, vnet_ack() should send out the missed "start" trigger. 1397d1015645SSowmini Varadhan * 1398d1015645SSowmini Varadhan * Note that the wmb() above makes sure the cookies et al. are 1399d1015645SSowmini Varadhan * not globally visible before the VIO_DESC_READY, and that the 1400d1015645SSowmini Varadhan * stores are ordered correctly by the compiler. The consumer will 1401d1015645SSowmini Varadhan * not proceed until the VIO_DESC_READY is visible assuring that 1402d1015645SSowmini Varadhan * the consumer does not observe anything related to descriptors 1403d1015645SSowmini Varadhan * out of order. The HV trap from the LDC start trigger is the 1404d1015645SSowmini Varadhan * producer to consumer announcement that work is available to the 1405d1015645SSowmini Varadhan * consumer 1406d1015645SSowmini Varadhan */ 1407d1015645SSowmini Varadhan if (!port->start_cons) 1408d1015645SSowmini Varadhan goto ldc_start_done; /* previous trigger suffices */ 1409d1015645SSowmini Varadhan 1410d1015645SSowmini Varadhan err = __vnet_tx_trigger(port, dr->cons); 1411e689cf4aSJeff Kirsher if (unlikely(err < 0)) { 1412e689cf4aSJeff Kirsher netdev_info(dev, "TX trigger error %d\n", err); 1413e689cf4aSJeff Kirsher d->hdr.state = VIO_DESC_FREE; 1414bb39b743SDavid L Stevens skb = port->tx_bufs[txi].skb; 1415bb39b743SDavid L Stevens port->tx_bufs[txi].skb = NULL; 1416e689cf4aSJeff Kirsher dev->stats.tx_carrier_errors++; 141713b13dd9SSowmini Varadhan goto out_dropped; 1418e689cf4aSJeff Kirsher } 1419e689cf4aSJeff Kirsher 1420d1015645SSowmini Varadhan ldc_start_done: 1421d1015645SSowmini Varadhan port->start_cons = false; 1422d1015645SSowmini Varadhan 1423e689cf4aSJeff Kirsher dev->stats.tx_packets++; 14248e845f4cSDavid L Stevens dev->stats.tx_bytes += port->tx_bufs[txi].skb->len; 1425e689cf4aSJeff Kirsher 1426e689cf4aSJeff Kirsher dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1); 1427d0aedcd4SDwight Engen if (unlikely(vnet_tx_dring_avail(dr) < 1)) { 1428d51bffd1SSowmini Varadhan netif_tx_stop_queue(txq); 1429e689cf4aSJeff Kirsher if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr)) 1430d51bffd1SSowmini Varadhan netif_tx_wake_queue(txq); 1431e689cf4aSJeff Kirsher } 1432e689cf4aSJeff Kirsher 14332a968dd8SSowmini Varadhan (void)mod_timer(&port->clean_timer, jiffies + VNET_CLEAN_TIMEOUT); 14342a968dd8SSowmini Varadhan rcu_read_unlock(); 1435e689cf4aSJeff Kirsher 14368e845f4cSDavid L Stevens vnet_free_skbs(freeskbs); 14378e845f4cSDavid L Stevens 1438e689cf4aSJeff Kirsher return NETDEV_TX_OK; 1439e689cf4aSJeff Kirsher 1440e689cf4aSJeff Kirsher out_dropped: 14418e845f4cSDavid L Stevens if (pending) 14428e845f4cSDavid L Stevens (void)mod_timer(&port->clean_timer, 14438e845f4cSDavid L Stevens jiffies + VNET_CLEAN_TIMEOUT); 1444a29c9c43SDavid L Stevens else if (port) 14458e845f4cSDavid L Stevens del_timer(&port->clean_timer); 14462a968dd8SSowmini Varadhan if (port) 14472a968dd8SSowmini Varadhan rcu_read_unlock(); 14482a968dd8SSowmini Varadhan if (skb) 14492a968dd8SSowmini Varadhan dev_kfree_skb(skb); 14502a968dd8SSowmini Varadhan vnet_free_skbs(freeskbs); 1451e689cf4aSJeff Kirsher dev->stats.tx_dropped++; 1452e689cf4aSJeff Kirsher return NETDEV_TX_OK; 1453e689cf4aSJeff Kirsher } 1454e689cf4aSJeff Kirsher 1455e689cf4aSJeff Kirsher static void vnet_tx_timeout(struct net_device *dev) 1456e689cf4aSJeff Kirsher { 1457e689cf4aSJeff Kirsher /* XXX Implement me XXX */ 1458e689cf4aSJeff Kirsher } 1459e689cf4aSJeff Kirsher 1460e689cf4aSJeff Kirsher static int vnet_open(struct net_device *dev) 1461e689cf4aSJeff Kirsher { 1462e689cf4aSJeff Kirsher netif_carrier_on(dev); 1463d51bffd1SSowmini Varadhan netif_tx_start_all_queues(dev); 1464e689cf4aSJeff Kirsher 1465e689cf4aSJeff Kirsher return 0; 1466e689cf4aSJeff Kirsher } 1467e689cf4aSJeff Kirsher 1468e689cf4aSJeff Kirsher static int vnet_close(struct net_device *dev) 1469e689cf4aSJeff Kirsher { 1470d51bffd1SSowmini Varadhan netif_tx_stop_all_queues(dev); 1471e689cf4aSJeff Kirsher netif_carrier_off(dev); 1472e689cf4aSJeff Kirsher 1473e689cf4aSJeff Kirsher return 0; 1474e689cf4aSJeff Kirsher } 1475e689cf4aSJeff Kirsher 1476e689cf4aSJeff Kirsher static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr) 1477e689cf4aSJeff Kirsher { 1478e689cf4aSJeff Kirsher struct vnet_mcast_entry *m; 1479e689cf4aSJeff Kirsher 1480e689cf4aSJeff Kirsher for (m = vp->mcast_list; m; m = m->next) { 148100fa4ce9Sdingtianhong if (ether_addr_equal(m->addr, addr)) 1482e689cf4aSJeff Kirsher return m; 1483e689cf4aSJeff Kirsher } 1484e689cf4aSJeff Kirsher return NULL; 1485e689cf4aSJeff Kirsher } 1486e689cf4aSJeff Kirsher 1487e689cf4aSJeff Kirsher static void __update_mc_list(struct vnet *vp, struct net_device *dev) 1488e689cf4aSJeff Kirsher { 1489e689cf4aSJeff Kirsher struct netdev_hw_addr *ha; 1490e689cf4aSJeff Kirsher 1491e689cf4aSJeff Kirsher netdev_for_each_mc_addr(ha, dev) { 1492e689cf4aSJeff Kirsher struct vnet_mcast_entry *m; 1493e689cf4aSJeff Kirsher 1494e689cf4aSJeff Kirsher m = __vnet_mc_find(vp, ha->addr); 1495e689cf4aSJeff Kirsher if (m) { 1496e689cf4aSJeff Kirsher m->hit = 1; 1497e689cf4aSJeff Kirsher continue; 1498e689cf4aSJeff Kirsher } 1499e689cf4aSJeff Kirsher 1500e689cf4aSJeff Kirsher if (!m) { 1501e689cf4aSJeff Kirsher m = kzalloc(sizeof(*m), GFP_ATOMIC); 1502e689cf4aSJeff Kirsher if (!m) 1503e689cf4aSJeff Kirsher continue; 1504e689cf4aSJeff Kirsher memcpy(m->addr, ha->addr, ETH_ALEN); 1505e689cf4aSJeff Kirsher m->hit = 1; 1506e689cf4aSJeff Kirsher 1507e689cf4aSJeff Kirsher m->next = vp->mcast_list; 1508e689cf4aSJeff Kirsher vp->mcast_list = m; 1509e689cf4aSJeff Kirsher } 1510e689cf4aSJeff Kirsher } 1511e689cf4aSJeff Kirsher } 1512e689cf4aSJeff Kirsher 1513e689cf4aSJeff Kirsher static void __send_mc_list(struct vnet *vp, struct vnet_port *port) 1514e689cf4aSJeff Kirsher { 1515e689cf4aSJeff Kirsher struct vio_net_mcast_info info; 1516e689cf4aSJeff Kirsher struct vnet_mcast_entry *m, **pp; 1517e689cf4aSJeff Kirsher int n_addrs; 1518e689cf4aSJeff Kirsher 1519e689cf4aSJeff Kirsher memset(&info, 0, sizeof(info)); 1520e689cf4aSJeff Kirsher 1521e689cf4aSJeff Kirsher info.tag.type = VIO_TYPE_CTRL; 1522e689cf4aSJeff Kirsher info.tag.stype = VIO_SUBTYPE_INFO; 1523e689cf4aSJeff Kirsher info.tag.stype_env = VNET_MCAST_INFO; 1524e689cf4aSJeff Kirsher info.tag.sid = vio_send_sid(&port->vio); 1525e689cf4aSJeff Kirsher info.set = 1; 1526e689cf4aSJeff Kirsher 1527e689cf4aSJeff Kirsher n_addrs = 0; 1528e689cf4aSJeff Kirsher for (m = vp->mcast_list; m; m = m->next) { 1529e689cf4aSJeff Kirsher if (m->sent) 1530e689cf4aSJeff Kirsher continue; 1531e689cf4aSJeff Kirsher m->sent = 1; 1532e689cf4aSJeff Kirsher memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], 1533e689cf4aSJeff Kirsher m->addr, ETH_ALEN); 1534e689cf4aSJeff Kirsher if (++n_addrs == VNET_NUM_MCAST) { 1535e689cf4aSJeff Kirsher info.count = n_addrs; 1536e689cf4aSJeff Kirsher 1537e689cf4aSJeff Kirsher (void) vio_ldc_send(&port->vio, &info, 1538e689cf4aSJeff Kirsher sizeof(info)); 1539e689cf4aSJeff Kirsher n_addrs = 0; 1540e689cf4aSJeff Kirsher } 1541e689cf4aSJeff Kirsher } 1542e689cf4aSJeff Kirsher if (n_addrs) { 1543e689cf4aSJeff Kirsher info.count = n_addrs; 1544e689cf4aSJeff Kirsher (void) vio_ldc_send(&port->vio, &info, sizeof(info)); 1545e689cf4aSJeff Kirsher } 1546e689cf4aSJeff Kirsher 1547e689cf4aSJeff Kirsher info.set = 0; 1548e689cf4aSJeff Kirsher 1549e689cf4aSJeff Kirsher n_addrs = 0; 1550e689cf4aSJeff Kirsher pp = &vp->mcast_list; 1551e689cf4aSJeff Kirsher while ((m = *pp) != NULL) { 1552e689cf4aSJeff Kirsher if (m->hit) { 1553e689cf4aSJeff Kirsher m->hit = 0; 1554e689cf4aSJeff Kirsher pp = &m->next; 1555e689cf4aSJeff Kirsher continue; 1556e689cf4aSJeff Kirsher } 1557e689cf4aSJeff Kirsher 1558e689cf4aSJeff Kirsher memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], 1559e689cf4aSJeff Kirsher m->addr, ETH_ALEN); 1560e689cf4aSJeff Kirsher if (++n_addrs == VNET_NUM_MCAST) { 1561e689cf4aSJeff Kirsher info.count = n_addrs; 1562e689cf4aSJeff Kirsher (void) vio_ldc_send(&port->vio, &info, 1563e689cf4aSJeff Kirsher sizeof(info)); 1564e689cf4aSJeff Kirsher n_addrs = 0; 1565e689cf4aSJeff Kirsher } 1566e689cf4aSJeff Kirsher 1567e689cf4aSJeff Kirsher *pp = m->next; 1568e689cf4aSJeff Kirsher kfree(m); 1569e689cf4aSJeff Kirsher } 1570e689cf4aSJeff Kirsher if (n_addrs) { 1571e689cf4aSJeff Kirsher info.count = n_addrs; 1572e689cf4aSJeff Kirsher (void) vio_ldc_send(&port->vio, &info, sizeof(info)); 1573e689cf4aSJeff Kirsher } 1574e689cf4aSJeff Kirsher } 1575e689cf4aSJeff Kirsher 1576e689cf4aSJeff Kirsher static void vnet_set_rx_mode(struct net_device *dev) 1577e689cf4aSJeff Kirsher { 1578e689cf4aSJeff Kirsher struct vnet *vp = netdev_priv(dev); 1579e689cf4aSJeff Kirsher struct vnet_port *port; 1580e689cf4aSJeff Kirsher 15812a968dd8SSowmini Varadhan rcu_read_lock(); 15822a968dd8SSowmini Varadhan list_for_each_entry_rcu(port, &vp->port_list, list) { 1583e689cf4aSJeff Kirsher 1584e689cf4aSJeff Kirsher if (port->switch_port) { 1585e689cf4aSJeff Kirsher __update_mc_list(vp, dev); 1586e689cf4aSJeff Kirsher __send_mc_list(vp, port); 15872a968dd8SSowmini Varadhan break; 1588e689cf4aSJeff Kirsher } 1589e689cf4aSJeff Kirsher } 15902a968dd8SSowmini Varadhan rcu_read_unlock(); 1591e689cf4aSJeff Kirsher } 1592e689cf4aSJeff Kirsher 1593e689cf4aSJeff Kirsher static int vnet_change_mtu(struct net_device *dev, int new_mtu) 1594e689cf4aSJeff Kirsher { 159542db672dSDavid L Stevens if (new_mtu < 68 || new_mtu > 65535) 1596e689cf4aSJeff Kirsher return -EINVAL; 1597e689cf4aSJeff Kirsher 1598e689cf4aSJeff Kirsher dev->mtu = new_mtu; 1599e689cf4aSJeff Kirsher return 0; 1600e689cf4aSJeff Kirsher } 1601e689cf4aSJeff Kirsher 1602e689cf4aSJeff Kirsher static int vnet_set_mac_addr(struct net_device *dev, void *p) 1603e689cf4aSJeff Kirsher { 1604e689cf4aSJeff Kirsher return -EINVAL; 1605e689cf4aSJeff Kirsher } 1606e689cf4aSJeff Kirsher 1607e689cf4aSJeff Kirsher static void vnet_get_drvinfo(struct net_device *dev, 1608e689cf4aSJeff Kirsher struct ethtool_drvinfo *info) 1609e689cf4aSJeff Kirsher { 16107826d43fSJiri Pirko strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); 16117826d43fSJiri Pirko strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); 1612e689cf4aSJeff Kirsher } 1613e689cf4aSJeff Kirsher 1614e689cf4aSJeff Kirsher static u32 vnet_get_msglevel(struct net_device *dev) 1615e689cf4aSJeff Kirsher { 1616e689cf4aSJeff Kirsher struct vnet *vp = netdev_priv(dev); 1617e689cf4aSJeff Kirsher return vp->msg_enable; 1618e689cf4aSJeff Kirsher } 1619e689cf4aSJeff Kirsher 1620e689cf4aSJeff Kirsher static void vnet_set_msglevel(struct net_device *dev, u32 value) 1621e689cf4aSJeff Kirsher { 1622e689cf4aSJeff Kirsher struct vnet *vp = netdev_priv(dev); 1623e689cf4aSJeff Kirsher vp->msg_enable = value; 1624e689cf4aSJeff Kirsher } 1625e689cf4aSJeff Kirsher 1626e689cf4aSJeff Kirsher static const struct ethtool_ops vnet_ethtool_ops = { 1627e689cf4aSJeff Kirsher .get_drvinfo = vnet_get_drvinfo, 1628e689cf4aSJeff Kirsher .get_msglevel = vnet_get_msglevel, 1629e689cf4aSJeff Kirsher .set_msglevel = vnet_set_msglevel, 1630e689cf4aSJeff Kirsher .get_link = ethtool_op_get_link, 1631e689cf4aSJeff Kirsher }; 1632e689cf4aSJeff Kirsher 1633e689cf4aSJeff Kirsher static void vnet_port_free_tx_bufs(struct vnet_port *port) 1634e689cf4aSJeff Kirsher { 1635e689cf4aSJeff Kirsher struct vio_dring_state *dr; 1636e689cf4aSJeff Kirsher int i; 1637e689cf4aSJeff Kirsher 1638e689cf4aSJeff Kirsher dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 163907ac3e70SDavid L Stevens 164007ac3e70SDavid L Stevens if (dr->base == NULL) 164107ac3e70SDavid L Stevens return; 1642e689cf4aSJeff Kirsher 1643e689cf4aSJeff Kirsher for (i = 0; i < VNET_TX_RING_SIZE; i++) { 16448e845f4cSDavid L Stevens struct vio_net_desc *d; 16458e845f4cSDavid L Stevens void *skb = port->tx_bufs[i].skb; 1646e689cf4aSJeff Kirsher 16478e845f4cSDavid L Stevens if (!skb) 1648e689cf4aSJeff Kirsher continue; 1649e689cf4aSJeff Kirsher 16508e845f4cSDavid L Stevens d = vio_dring_entry(dr, i); 16518e845f4cSDavid L Stevens 1652e689cf4aSJeff Kirsher ldc_unmap(port->vio.lp, 1653e689cf4aSJeff Kirsher port->tx_bufs[i].cookies, 1654e689cf4aSJeff Kirsher port->tx_bufs[i].ncookies); 16558e845f4cSDavid L Stevens dev_kfree_skb(skb); 16568e845f4cSDavid L Stevens port->tx_bufs[i].skb = NULL; 16578e845f4cSDavid L Stevens d->hdr.state = VIO_DESC_FREE; 1658e689cf4aSJeff Kirsher } 165907ac3e70SDavid L Stevens ldc_free_exp_dring(port->vio.lp, dr->base, 166007ac3e70SDavid L Stevens (dr->entry_size * dr->num_entries), 166107ac3e70SDavid L Stevens dr->cookies, dr->ncookies); 166207ac3e70SDavid L Stevens dr->base = NULL; 166307ac3e70SDavid L Stevens dr->entry_size = 0; 166407ac3e70SDavid L Stevens dr->num_entries = 0; 166507ac3e70SDavid L Stevens dr->pending = 0; 166607ac3e70SDavid L Stevens dr->ncookies = 0; 1667e689cf4aSJeff Kirsher } 1668e689cf4aSJeff Kirsher 16698e2b60cdSDavid L Stevens static void vnet_port_reset(struct vnet_port *port) 16708e2b60cdSDavid L Stevens { 16718e2b60cdSDavid L Stevens del_timer(&port->clean_timer); 16728e2b60cdSDavid L Stevens vnet_port_free_tx_bufs(port); 16738e2b60cdSDavid L Stevens port->rmtu = 0; 16748e2b60cdSDavid L Stevens port->tso = true; 16758e2b60cdSDavid L Stevens port->tsolen = 0; 16768e2b60cdSDavid L Stevens } 16778e2b60cdSDavid L Stevens 1678d6732489SDavid L Stevens static int vnet_port_alloc_tx_ring(struct vnet_port *port) 1679e689cf4aSJeff Kirsher { 1680e689cf4aSJeff Kirsher struct vio_dring_state *dr; 16816d0ba919SDavid L Stevens unsigned long len, elen; 1682e689cf4aSJeff Kirsher int i, err, ncookies; 1683e689cf4aSJeff Kirsher void *dring; 1684e689cf4aSJeff Kirsher 1685e689cf4aSJeff Kirsher dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 1686e689cf4aSJeff Kirsher 16876d0ba919SDavid L Stevens elen = sizeof(struct vio_net_desc) + 16886d0ba919SDavid L Stevens sizeof(struct ldc_trans_cookie) * 2; 16896d0ba919SDavid L Stevens if (vio_version_after_eq(&port->vio, 1, 7)) 16906d0ba919SDavid L Stevens elen += sizeof(struct vio_net_dext); 16916d0ba919SDavid L Stevens len = VNET_TX_RING_SIZE * elen; 1692e689cf4aSJeff Kirsher 1693e689cf4aSJeff Kirsher ncookies = VIO_MAX_RING_COOKIES; 1694e689cf4aSJeff Kirsher dring = ldc_alloc_exp_dring(port->vio.lp, len, 1695e689cf4aSJeff Kirsher dr->cookies, &ncookies, 1696e689cf4aSJeff Kirsher (LDC_MAP_SHADOW | 1697e689cf4aSJeff Kirsher LDC_MAP_DIRECT | 1698e689cf4aSJeff Kirsher LDC_MAP_RW)); 1699e689cf4aSJeff Kirsher if (IS_ERR(dring)) { 1700e689cf4aSJeff Kirsher err = PTR_ERR(dring); 1701e689cf4aSJeff Kirsher goto err_out; 1702e689cf4aSJeff Kirsher } 1703e689cf4aSJeff Kirsher 1704e689cf4aSJeff Kirsher dr->base = dring; 17056d0ba919SDavid L Stevens dr->entry_size = elen; 1706e689cf4aSJeff Kirsher dr->num_entries = VNET_TX_RING_SIZE; 1707e689cf4aSJeff Kirsher dr->prod = dr->cons = 0; 1708d1015645SSowmini Varadhan port->start_cons = true; /* need an initial trigger */ 1709e689cf4aSJeff Kirsher dr->pending = VNET_TX_RING_SIZE; 1710e689cf4aSJeff Kirsher dr->ncookies = ncookies; 1711e689cf4aSJeff Kirsher 17128e845f4cSDavid L Stevens for (i = 0; i < VNET_TX_RING_SIZE; ++i) { 17138e845f4cSDavid L Stevens struct vio_net_desc *d; 17148e845f4cSDavid L Stevens 17158e845f4cSDavid L Stevens d = vio_dring_entry(dr, i); 17168e845f4cSDavid L Stevens d->hdr.state = VIO_DESC_FREE; 17178e845f4cSDavid L Stevens } 1718e689cf4aSJeff Kirsher return 0; 1719e689cf4aSJeff Kirsher 1720e689cf4aSJeff Kirsher err_out: 1721e689cf4aSJeff Kirsher vnet_port_free_tx_bufs(port); 1722e689cf4aSJeff Kirsher 1723e689cf4aSJeff Kirsher return err; 1724e689cf4aSJeff Kirsher } 1725e689cf4aSJeff Kirsher 172669088822SSowmini Varadhan #ifdef CONFIG_NET_POLL_CONTROLLER 172769088822SSowmini Varadhan static void vnet_poll_controller(struct net_device *dev) 172869088822SSowmini Varadhan { 172969088822SSowmini Varadhan struct vnet *vp = netdev_priv(dev); 173069088822SSowmini Varadhan struct vnet_port *port; 173169088822SSowmini Varadhan unsigned long flags; 173269088822SSowmini Varadhan 173369088822SSowmini Varadhan spin_lock_irqsave(&vp->lock, flags); 173469088822SSowmini Varadhan if (!list_empty(&vp->port_list)) { 173569088822SSowmini Varadhan port = list_entry(vp->port_list.next, struct vnet_port, list); 173669088822SSowmini Varadhan napi_schedule(&port->napi); 173769088822SSowmini Varadhan } 173869088822SSowmini Varadhan spin_unlock_irqrestore(&vp->lock, flags); 173969088822SSowmini Varadhan } 174069088822SSowmini Varadhan #endif 1741e689cf4aSJeff Kirsher static LIST_HEAD(vnet_list); 1742e689cf4aSJeff Kirsher static DEFINE_MUTEX(vnet_list_mutex); 1743e689cf4aSJeff Kirsher 1744e689cf4aSJeff Kirsher static const struct net_device_ops vnet_ops = { 1745e689cf4aSJeff Kirsher .ndo_open = vnet_open, 1746e689cf4aSJeff Kirsher .ndo_stop = vnet_close, 1747afc4b13dSJiri Pirko .ndo_set_rx_mode = vnet_set_rx_mode, 1748e689cf4aSJeff Kirsher .ndo_set_mac_address = vnet_set_mac_addr, 1749e689cf4aSJeff Kirsher .ndo_validate_addr = eth_validate_addr, 1750e689cf4aSJeff Kirsher .ndo_tx_timeout = vnet_tx_timeout, 1751e689cf4aSJeff Kirsher .ndo_change_mtu = vnet_change_mtu, 1752e689cf4aSJeff Kirsher .ndo_start_xmit = vnet_start_xmit, 1753d51bffd1SSowmini Varadhan .ndo_select_queue = vnet_select_queue, 175469088822SSowmini Varadhan #ifdef CONFIG_NET_POLL_CONTROLLER 175569088822SSowmini Varadhan .ndo_poll_controller = vnet_poll_controller, 175669088822SSowmini Varadhan #endif 1757e689cf4aSJeff Kirsher }; 1758e689cf4aSJeff Kirsher 1759f73d12bdSBill Pemberton static struct vnet *vnet_new(const u64 *local_mac) 1760e689cf4aSJeff Kirsher { 1761e689cf4aSJeff Kirsher struct net_device *dev; 1762e689cf4aSJeff Kirsher struct vnet *vp; 1763e689cf4aSJeff Kirsher int err, i; 1764e689cf4aSJeff Kirsher 1765d51bffd1SSowmini Varadhan dev = alloc_etherdev_mqs(sizeof(*vp), VNET_MAX_TXQS, 1); 176641de8d4cSJoe Perches if (!dev) 1767e689cf4aSJeff Kirsher return ERR_PTR(-ENOMEM); 17688e845f4cSDavid L Stevens dev->needed_headroom = VNET_PACKET_SKIP + 8; 17698e845f4cSDavid L Stevens dev->needed_tailroom = 8; 1770e689cf4aSJeff Kirsher 1771e689cf4aSJeff Kirsher for (i = 0; i < ETH_ALEN; i++) 1772e689cf4aSJeff Kirsher dev->dev_addr[i] = (*local_mac >> (5 - i) * 8) & 0xff; 1773e689cf4aSJeff Kirsher 1774e689cf4aSJeff Kirsher vp = netdev_priv(dev); 1775e689cf4aSJeff Kirsher 1776e689cf4aSJeff Kirsher spin_lock_init(&vp->lock); 1777e689cf4aSJeff Kirsher vp->dev = dev; 1778e689cf4aSJeff Kirsher 1779e689cf4aSJeff Kirsher INIT_LIST_HEAD(&vp->port_list); 1780e689cf4aSJeff Kirsher for (i = 0; i < VNET_PORT_HASH_SIZE; i++) 1781e689cf4aSJeff Kirsher INIT_HLIST_HEAD(&vp->port_hash[i]); 1782e689cf4aSJeff Kirsher INIT_LIST_HEAD(&vp->list); 1783e689cf4aSJeff Kirsher vp->local_mac = *local_mac; 1784e689cf4aSJeff Kirsher 1785e689cf4aSJeff Kirsher dev->netdev_ops = &vnet_ops; 1786e689cf4aSJeff Kirsher dev->ethtool_ops = &vnet_ethtool_ops; 1787e689cf4aSJeff Kirsher dev->watchdog_timeo = VNET_TX_TIMEOUT; 1788e689cf4aSJeff Kirsher 1789368e36edSDavid L Stevens dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GSO_SOFTWARE | 17909a72dd4dSDavid L Stevens NETIF_F_HW_CSUM | NETIF_F_SG; 1791da38c564SDavid L Stevens dev->features = dev->hw_features; 1792da38c564SDavid L Stevens 1793e689cf4aSJeff Kirsher err = register_netdev(dev); 1794e689cf4aSJeff Kirsher if (err) { 1795e689cf4aSJeff Kirsher pr_err("Cannot register net device, aborting\n"); 1796e689cf4aSJeff Kirsher goto err_out_free_dev; 1797e689cf4aSJeff Kirsher } 1798e689cf4aSJeff Kirsher 1799e689cf4aSJeff Kirsher netdev_info(dev, "Sun LDOM vnet %pM\n", dev->dev_addr); 1800e689cf4aSJeff Kirsher 1801e689cf4aSJeff Kirsher list_add(&vp->list, &vnet_list); 1802e689cf4aSJeff Kirsher 1803e689cf4aSJeff Kirsher return vp; 1804e689cf4aSJeff Kirsher 1805e689cf4aSJeff Kirsher err_out_free_dev: 1806e689cf4aSJeff Kirsher free_netdev(dev); 1807e689cf4aSJeff Kirsher 1808e689cf4aSJeff Kirsher return ERR_PTR(err); 1809e689cf4aSJeff Kirsher } 1810e689cf4aSJeff Kirsher 1811f73d12bdSBill Pemberton static struct vnet *vnet_find_or_create(const u64 *local_mac) 1812e689cf4aSJeff Kirsher { 1813e689cf4aSJeff Kirsher struct vnet *iter, *vp; 1814e689cf4aSJeff Kirsher 1815e689cf4aSJeff Kirsher mutex_lock(&vnet_list_mutex); 1816e689cf4aSJeff Kirsher vp = NULL; 1817e689cf4aSJeff Kirsher list_for_each_entry(iter, &vnet_list, list) { 1818e689cf4aSJeff Kirsher if (iter->local_mac == *local_mac) { 1819e689cf4aSJeff Kirsher vp = iter; 1820e689cf4aSJeff Kirsher break; 1821e689cf4aSJeff Kirsher } 1822e689cf4aSJeff Kirsher } 1823e689cf4aSJeff Kirsher if (!vp) 1824e689cf4aSJeff Kirsher vp = vnet_new(local_mac); 1825e689cf4aSJeff Kirsher mutex_unlock(&vnet_list_mutex); 1826e689cf4aSJeff Kirsher 1827e689cf4aSJeff Kirsher return vp; 1828e689cf4aSJeff Kirsher } 1829e689cf4aSJeff Kirsher 1830a4b70a07SSowmini Varadhan static void vnet_cleanup(void) 1831a4b70a07SSowmini Varadhan { 1832a4b70a07SSowmini Varadhan struct vnet *vp; 1833a4b70a07SSowmini Varadhan struct net_device *dev; 1834a4b70a07SSowmini Varadhan 1835a4b70a07SSowmini Varadhan mutex_lock(&vnet_list_mutex); 1836a4b70a07SSowmini Varadhan while (!list_empty(&vnet_list)) { 1837a4b70a07SSowmini Varadhan vp = list_first_entry(&vnet_list, struct vnet, list); 1838a4b70a07SSowmini Varadhan list_del(&vp->list); 1839a4b70a07SSowmini Varadhan dev = vp->dev; 1840a4b70a07SSowmini Varadhan /* vio_unregister_driver() should have cleaned up port_list */ 1841a4b70a07SSowmini Varadhan BUG_ON(!list_empty(&vp->port_list)); 1842a4b70a07SSowmini Varadhan unregister_netdev(dev); 1843a4b70a07SSowmini Varadhan free_netdev(dev); 1844a4b70a07SSowmini Varadhan } 1845a4b70a07SSowmini Varadhan mutex_unlock(&vnet_list_mutex); 1846a4b70a07SSowmini Varadhan } 1847a4b70a07SSowmini Varadhan 1848e689cf4aSJeff Kirsher static const char *local_mac_prop = "local-mac-address"; 1849e689cf4aSJeff Kirsher 1850f73d12bdSBill Pemberton static struct vnet *vnet_find_parent(struct mdesc_handle *hp, 1851e689cf4aSJeff Kirsher u64 port_node) 1852e689cf4aSJeff Kirsher { 1853e689cf4aSJeff Kirsher const u64 *local_mac = NULL; 1854e689cf4aSJeff Kirsher u64 a; 1855e689cf4aSJeff Kirsher 1856e689cf4aSJeff Kirsher mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) { 1857e689cf4aSJeff Kirsher u64 target = mdesc_arc_target(hp, a); 1858e689cf4aSJeff Kirsher const char *name; 1859e689cf4aSJeff Kirsher 1860e689cf4aSJeff Kirsher name = mdesc_get_property(hp, target, "name", NULL); 1861e689cf4aSJeff Kirsher if (!name || strcmp(name, "network")) 1862e689cf4aSJeff Kirsher continue; 1863e689cf4aSJeff Kirsher 1864e689cf4aSJeff Kirsher local_mac = mdesc_get_property(hp, target, 1865e689cf4aSJeff Kirsher local_mac_prop, NULL); 1866e689cf4aSJeff Kirsher if (local_mac) 1867e689cf4aSJeff Kirsher break; 1868e689cf4aSJeff Kirsher } 1869e689cf4aSJeff Kirsher if (!local_mac) 1870e689cf4aSJeff Kirsher return ERR_PTR(-ENODEV); 1871e689cf4aSJeff Kirsher 1872e689cf4aSJeff Kirsher return vnet_find_or_create(local_mac); 1873e689cf4aSJeff Kirsher } 1874e689cf4aSJeff Kirsher 1875e689cf4aSJeff Kirsher static struct ldc_channel_config vnet_ldc_cfg = { 1876e689cf4aSJeff Kirsher .event = vnet_event, 1877e689cf4aSJeff Kirsher .mtu = 64, 1878e689cf4aSJeff Kirsher .mode = LDC_MODE_UNRELIABLE, 1879e689cf4aSJeff Kirsher }; 1880e689cf4aSJeff Kirsher 1881e689cf4aSJeff Kirsher static struct vio_driver_ops vnet_vio_ops = { 1882e689cf4aSJeff Kirsher .send_attr = vnet_send_attr, 1883e689cf4aSJeff Kirsher .handle_attr = vnet_handle_attr, 1884e689cf4aSJeff Kirsher .handshake_complete = vnet_handshake_complete, 1885e689cf4aSJeff Kirsher }; 1886e689cf4aSJeff Kirsher 1887f73d12bdSBill Pemberton static void print_version(void) 1888e689cf4aSJeff Kirsher { 1889e689cf4aSJeff Kirsher printk_once(KERN_INFO "%s", version); 1890e689cf4aSJeff Kirsher } 1891e689cf4aSJeff Kirsher 1892e689cf4aSJeff Kirsher const char *remote_macaddr_prop = "remote-mac-address"; 1893e689cf4aSJeff Kirsher 1894d51bffd1SSowmini Varadhan static void 1895d51bffd1SSowmini Varadhan vnet_port_add_txq(struct vnet_port *port) 1896d51bffd1SSowmini Varadhan { 1897d51bffd1SSowmini Varadhan struct vnet *vp = port->vp; 1898d51bffd1SSowmini Varadhan int n; 1899d51bffd1SSowmini Varadhan 1900d51bffd1SSowmini Varadhan n = vp->nports++; 1901d51bffd1SSowmini Varadhan n = n & (VNET_MAX_TXQS - 1); 1902d51bffd1SSowmini Varadhan port->q_index = n; 1903d51bffd1SSowmini Varadhan netif_tx_wake_queue(netdev_get_tx_queue(vp->dev, port->q_index)); 1904d51bffd1SSowmini Varadhan } 1905d51bffd1SSowmini Varadhan 1906d51bffd1SSowmini Varadhan static void 1907d51bffd1SSowmini Varadhan vnet_port_rm_txq(struct vnet_port *port) 1908d51bffd1SSowmini Varadhan { 1909d51bffd1SSowmini Varadhan port->vp->nports--; 1910d51bffd1SSowmini Varadhan netif_tx_stop_queue(netdev_get_tx_queue(port->vp->dev, port->q_index)); 1911d51bffd1SSowmini Varadhan } 1912d51bffd1SSowmini Varadhan 19131dd06ae8SGreg Kroah-Hartman static int vnet_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) 1914e689cf4aSJeff Kirsher { 1915e689cf4aSJeff Kirsher struct mdesc_handle *hp; 1916e689cf4aSJeff Kirsher struct vnet_port *port; 1917e689cf4aSJeff Kirsher unsigned long flags; 1918e689cf4aSJeff Kirsher struct vnet *vp; 1919e689cf4aSJeff Kirsher const u64 *rmac; 1920e689cf4aSJeff Kirsher int len, i, err, switch_port; 1921e689cf4aSJeff Kirsher 1922e689cf4aSJeff Kirsher print_version(); 1923e689cf4aSJeff Kirsher 1924e689cf4aSJeff Kirsher hp = mdesc_grab(); 1925e689cf4aSJeff Kirsher 1926e689cf4aSJeff Kirsher vp = vnet_find_parent(hp, vdev->mp); 1927e689cf4aSJeff Kirsher if (IS_ERR(vp)) { 1928e689cf4aSJeff Kirsher pr_err("Cannot find port parent vnet\n"); 1929e689cf4aSJeff Kirsher err = PTR_ERR(vp); 1930e689cf4aSJeff Kirsher goto err_out_put_mdesc; 1931e689cf4aSJeff Kirsher } 1932e689cf4aSJeff Kirsher 1933e689cf4aSJeff Kirsher rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); 1934e689cf4aSJeff Kirsher err = -ENODEV; 1935e689cf4aSJeff Kirsher if (!rmac) { 1936e689cf4aSJeff Kirsher pr_err("Port lacks %s property\n", remote_macaddr_prop); 1937e689cf4aSJeff Kirsher goto err_out_put_mdesc; 1938e689cf4aSJeff Kirsher } 1939e689cf4aSJeff Kirsher 1940e689cf4aSJeff Kirsher port = kzalloc(sizeof(*port), GFP_KERNEL); 1941e689cf4aSJeff Kirsher err = -ENOMEM; 1942e404decbSJoe Perches if (!port) 1943e689cf4aSJeff Kirsher goto err_out_put_mdesc; 1944e689cf4aSJeff Kirsher 1945e689cf4aSJeff Kirsher for (i = 0; i < ETH_ALEN; i++) 1946e689cf4aSJeff Kirsher port->raddr[i] = (*rmac >> (5 - i) * 8) & 0xff; 1947e689cf4aSJeff Kirsher 1948e689cf4aSJeff Kirsher port->vp = vp; 1949e689cf4aSJeff Kirsher 1950e689cf4aSJeff Kirsher err = vio_driver_init(&port->vio, vdev, VDEV_NETWORK, 1951e689cf4aSJeff Kirsher vnet_versions, ARRAY_SIZE(vnet_versions), 1952e689cf4aSJeff Kirsher &vnet_vio_ops, vp->dev->name); 1953e689cf4aSJeff Kirsher if (err) 1954e689cf4aSJeff Kirsher goto err_out_free_port; 1955e689cf4aSJeff Kirsher 1956e689cf4aSJeff Kirsher err = vio_ldc_alloc(&port->vio, &vnet_ldc_cfg, port); 1957e689cf4aSJeff Kirsher if (err) 1958e689cf4aSJeff Kirsher goto err_out_free_port; 1959e689cf4aSJeff Kirsher 196069088822SSowmini Varadhan netif_napi_add(port->vp->dev, &port->napi, vnet_poll, NAPI_POLL_WEIGHT); 196169088822SSowmini Varadhan 1962e689cf4aSJeff Kirsher INIT_HLIST_NODE(&port->hash); 1963e689cf4aSJeff Kirsher INIT_LIST_HEAD(&port->list); 1964e689cf4aSJeff Kirsher 1965e689cf4aSJeff Kirsher switch_port = 0; 1966e689cf4aSJeff Kirsher if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) 1967e689cf4aSJeff Kirsher switch_port = 1; 1968e689cf4aSJeff Kirsher port->switch_port = switch_port; 1969368e36edSDavid L Stevens port->tso = true; 1970368e36edSDavid L Stevens port->tsolen = 0; 1971e689cf4aSJeff Kirsher 1972e689cf4aSJeff Kirsher spin_lock_irqsave(&vp->lock, flags); 1973e689cf4aSJeff Kirsher if (switch_port) 19742a968dd8SSowmini Varadhan list_add_rcu(&port->list, &vp->port_list); 1975e689cf4aSJeff Kirsher else 19762a968dd8SSowmini Varadhan list_add_tail_rcu(&port->list, &vp->port_list); 19772a968dd8SSowmini Varadhan hlist_add_head_rcu(&port->hash, 19782a968dd8SSowmini Varadhan &vp->port_hash[vnet_hashfn(port->raddr)]); 1979d51bffd1SSowmini Varadhan vnet_port_add_txq(port); 1980e689cf4aSJeff Kirsher spin_unlock_irqrestore(&vp->lock, flags); 1981e689cf4aSJeff Kirsher 1982e689cf4aSJeff Kirsher dev_set_drvdata(&vdev->dev, port); 1983e689cf4aSJeff Kirsher 1984e689cf4aSJeff Kirsher pr_info("%s: PORT ( remote-mac %pM%s )\n", 1985e689cf4aSJeff Kirsher vp->dev->name, port->raddr, switch_port ? " switch-port" : ""); 1986e689cf4aSJeff Kirsher 19878e845f4cSDavid L Stevens setup_timer(&port->clean_timer, vnet_clean_timer_expire, 19888e845f4cSDavid L Stevens (unsigned long)port); 19898e845f4cSDavid L Stevens 199069088822SSowmini Varadhan napi_enable(&port->napi); 1991e689cf4aSJeff Kirsher vio_port_up(&port->vio); 1992e689cf4aSJeff Kirsher 1993e689cf4aSJeff Kirsher mdesc_release(hp); 1994e689cf4aSJeff Kirsher 1995e689cf4aSJeff Kirsher return 0; 1996e689cf4aSJeff Kirsher 1997e689cf4aSJeff Kirsher err_out_free_port: 1998e689cf4aSJeff Kirsher kfree(port); 1999e689cf4aSJeff Kirsher 2000e689cf4aSJeff Kirsher err_out_put_mdesc: 2001e689cf4aSJeff Kirsher mdesc_release(hp); 2002e689cf4aSJeff Kirsher return err; 2003e689cf4aSJeff Kirsher } 2004e689cf4aSJeff Kirsher 2005e689cf4aSJeff Kirsher static int vnet_port_remove(struct vio_dev *vdev) 2006e689cf4aSJeff Kirsher { 2007e689cf4aSJeff Kirsher struct vnet_port *port = dev_get_drvdata(&vdev->dev); 2008e689cf4aSJeff Kirsher 2009e689cf4aSJeff Kirsher if (port) { 2010e689cf4aSJeff Kirsher 2011e689cf4aSJeff Kirsher del_timer_sync(&port->vio.timer); 2012e689cf4aSJeff Kirsher 201369088822SSowmini Varadhan napi_disable(&port->napi); 2014e689cf4aSJeff Kirsher 20152a968dd8SSowmini Varadhan list_del_rcu(&port->list); 20162a968dd8SSowmini Varadhan hlist_del_rcu(&port->hash); 20172a968dd8SSowmini Varadhan 20182a968dd8SSowmini Varadhan synchronize_rcu(); 20192a968dd8SSowmini Varadhan del_timer_sync(&port->clean_timer); 2020d51bffd1SSowmini Varadhan vnet_port_rm_txq(port); 202169088822SSowmini Varadhan netif_napi_del(&port->napi); 2022e689cf4aSJeff Kirsher vnet_port_free_tx_bufs(port); 2023e689cf4aSJeff Kirsher vio_ldc_free(&port->vio); 2024e689cf4aSJeff Kirsher 2025e689cf4aSJeff Kirsher dev_set_drvdata(&vdev->dev, NULL); 2026e689cf4aSJeff Kirsher 2027e689cf4aSJeff Kirsher kfree(port); 2028aabb9875SDave Kleikamp 2029e689cf4aSJeff Kirsher } 2030e689cf4aSJeff Kirsher return 0; 2031e689cf4aSJeff Kirsher } 2032e689cf4aSJeff Kirsher 2033e689cf4aSJeff Kirsher static const struct vio_device_id vnet_port_match[] = { 2034e689cf4aSJeff Kirsher { 2035e689cf4aSJeff Kirsher .type = "vnet-port", 2036e689cf4aSJeff Kirsher }, 2037e689cf4aSJeff Kirsher {}, 2038e689cf4aSJeff Kirsher }; 2039e689cf4aSJeff Kirsher MODULE_DEVICE_TABLE(vio, vnet_port_match); 2040e689cf4aSJeff Kirsher 2041e689cf4aSJeff Kirsher static struct vio_driver vnet_port_driver = { 2042e689cf4aSJeff Kirsher .id_table = vnet_port_match, 2043e689cf4aSJeff Kirsher .probe = vnet_port_probe, 2044e689cf4aSJeff Kirsher .remove = vnet_port_remove, 2045e689cf4aSJeff Kirsher .name = "vnet_port", 2046e689cf4aSJeff Kirsher }; 2047e689cf4aSJeff Kirsher 2048e689cf4aSJeff Kirsher static int __init vnet_init(void) 2049e689cf4aSJeff Kirsher { 2050e689cf4aSJeff Kirsher return vio_register_driver(&vnet_port_driver); 2051e689cf4aSJeff Kirsher } 2052e689cf4aSJeff Kirsher 2053e689cf4aSJeff Kirsher static void __exit vnet_exit(void) 2054e689cf4aSJeff Kirsher { 2055e689cf4aSJeff Kirsher vio_unregister_driver(&vnet_port_driver); 2056a4b70a07SSowmini Varadhan vnet_cleanup(); 2057e689cf4aSJeff Kirsher } 2058e689cf4aSJeff Kirsher 2059e689cf4aSJeff Kirsher module_init(vnet_init); 2060e689cf4aSJeff Kirsher module_exit(vnet_exit); 2061