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> 18*e4defc77SDavid L Stevens #include <linux/if_vlan.h> 19e689cf4aSJeff Kirsher 20e689cf4aSJeff Kirsher #include <asm/vio.h> 21e689cf4aSJeff Kirsher #include <asm/ldc.h> 22e689cf4aSJeff Kirsher 23e689cf4aSJeff Kirsher #include "sunvnet.h" 24e689cf4aSJeff Kirsher 25e689cf4aSJeff Kirsher #define DRV_MODULE_NAME "sunvnet" 26e689cf4aSJeff Kirsher #define DRV_MODULE_VERSION "1.0" 27e689cf4aSJeff Kirsher #define DRV_MODULE_RELDATE "June 25, 2007" 28e689cf4aSJeff Kirsher 29f73d12bdSBill Pemberton static char version[] = 30e689cf4aSJeff Kirsher DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 31e689cf4aSJeff Kirsher MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 32e689cf4aSJeff Kirsher MODULE_DESCRIPTION("Sun LDOM virtual network driver"); 33e689cf4aSJeff Kirsher MODULE_LICENSE("GPL"); 34e689cf4aSJeff Kirsher MODULE_VERSION(DRV_MODULE_VERSION); 35e689cf4aSJeff Kirsher 36adddc32dSSowmini Varadhan /* Heuristic for the number of times to exponentially backoff and 37adddc32dSSowmini Varadhan * retry sending an LDC trigger when EAGAIN is encountered 38adddc32dSSowmini Varadhan */ 39adddc32dSSowmini Varadhan #define VNET_MAX_RETRIES 10 40adddc32dSSowmini Varadhan 41d1015645SSowmini Varadhan static int __vnet_tx_trigger(struct vnet_port *port, u32 start); 42d1015645SSowmini Varadhan 43e689cf4aSJeff Kirsher /* Ordered from largest major to lowest */ 44e689cf4aSJeff Kirsher static struct vio_version vnet_versions[] = { 45*e4defc77SDavid L Stevens { .major = 1, .minor = 6 }, 46e689cf4aSJeff Kirsher { .major = 1, .minor = 0 }, 47e689cf4aSJeff Kirsher }; 48e689cf4aSJeff Kirsher 49e689cf4aSJeff Kirsher static inline u32 vnet_tx_dring_avail(struct vio_dring_state *dr) 50e689cf4aSJeff Kirsher { 51e689cf4aSJeff Kirsher return vio_dring_avail(dr, VNET_TX_RING_SIZE); 52e689cf4aSJeff Kirsher } 53e689cf4aSJeff Kirsher 54e689cf4aSJeff Kirsher static int vnet_handle_unknown(struct vnet_port *port, void *arg) 55e689cf4aSJeff Kirsher { 56e689cf4aSJeff Kirsher struct vio_msg_tag *pkt = arg; 57e689cf4aSJeff Kirsher 58e689cf4aSJeff Kirsher pr_err("Received unknown msg [%02x:%02x:%04x:%08x]\n", 59e689cf4aSJeff Kirsher pkt->type, pkt->stype, pkt->stype_env, pkt->sid); 60e689cf4aSJeff Kirsher pr_err("Resetting connection\n"); 61e689cf4aSJeff Kirsher 62e689cf4aSJeff Kirsher ldc_disconnect(port->vio.lp); 63e689cf4aSJeff Kirsher 64e689cf4aSJeff Kirsher return -ECONNRESET; 65e689cf4aSJeff Kirsher } 66e689cf4aSJeff Kirsher 67e689cf4aSJeff Kirsher static int vnet_send_attr(struct vio_driver_state *vio) 68e689cf4aSJeff Kirsher { 69e689cf4aSJeff Kirsher struct vnet_port *port = to_vnet_port(vio); 70e689cf4aSJeff Kirsher struct net_device *dev = port->vp->dev; 71e689cf4aSJeff Kirsher struct vio_net_attr_info pkt; 72*e4defc77SDavid L Stevens int framelen = ETH_FRAME_LEN; 73e689cf4aSJeff Kirsher int i; 74e689cf4aSJeff Kirsher 75e689cf4aSJeff Kirsher memset(&pkt, 0, sizeof(pkt)); 76e689cf4aSJeff Kirsher pkt.tag.type = VIO_TYPE_CTRL; 77e689cf4aSJeff Kirsher pkt.tag.stype = VIO_SUBTYPE_INFO; 78e689cf4aSJeff Kirsher pkt.tag.stype_env = VIO_ATTR_INFO; 79e689cf4aSJeff Kirsher pkt.tag.sid = vio_send_sid(vio); 80*e4defc77SDavid L Stevens if (vio_version_before(vio, 1, 2)) 81e689cf4aSJeff Kirsher pkt.xfer_mode = VIO_DRING_MODE; 82*e4defc77SDavid L Stevens else 83*e4defc77SDavid L Stevens pkt.xfer_mode = VIO_NEW_DRING_MODE; 84e689cf4aSJeff Kirsher pkt.addr_type = VNET_ADDR_ETHERMAC; 85e689cf4aSJeff Kirsher pkt.ack_freq = 0; 86e689cf4aSJeff Kirsher for (i = 0; i < 6; i++) 87e689cf4aSJeff Kirsher pkt.addr |= (u64)dev->dev_addr[i] << ((5 - i) * 8); 88*e4defc77SDavid L Stevens if (vio_version_after(vio, 1, 3)) { 89*e4defc77SDavid L Stevens if (port->rmtu) { 90*e4defc77SDavid L Stevens port->rmtu = min(VNET_MAXPACKET, port->rmtu); 91*e4defc77SDavid L Stevens pkt.mtu = port->rmtu; 92*e4defc77SDavid L Stevens } else { 93*e4defc77SDavid L Stevens port->rmtu = VNET_MAXPACKET; 94*e4defc77SDavid L Stevens pkt.mtu = port->rmtu; 95*e4defc77SDavid L Stevens } 96*e4defc77SDavid L Stevens if (vio_version_after_eq(vio, 1, 6)) 97*e4defc77SDavid L Stevens pkt.options = VIO_TX_DRING; 98*e4defc77SDavid L Stevens } else if (vio_version_before(vio, 1, 3)) { 99*e4defc77SDavid L Stevens pkt.mtu = framelen; 100*e4defc77SDavid L Stevens } else { /* v1.3 */ 101*e4defc77SDavid L Stevens pkt.mtu = framelen + VLAN_HLEN; 102*e4defc77SDavid L Stevens } 103*e4defc77SDavid L Stevens 104*e4defc77SDavid L Stevens pkt.plnk_updt = PHYSLINK_UPDATE_NONE; 105*e4defc77SDavid L Stevens pkt.cflags = 0; 106e689cf4aSJeff Kirsher 107e689cf4aSJeff Kirsher viodbg(HS, "SEND NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] " 108*e4defc77SDavid L Stevens "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] " 109*e4defc77SDavid L Stevens "cflags[0x%04x] lso_max[%u]\n", 110e689cf4aSJeff Kirsher pkt.xfer_mode, pkt.addr_type, 111e689cf4aSJeff Kirsher (unsigned long long)pkt.addr, 112*e4defc77SDavid L Stevens pkt.ack_freq, pkt.plnk_updt, pkt.options, 113*e4defc77SDavid L Stevens (unsigned long long)pkt.mtu, pkt.cflags, pkt.ipv4_lso_maxlen); 114*e4defc77SDavid L Stevens 115e689cf4aSJeff Kirsher 116e689cf4aSJeff Kirsher return vio_ldc_send(vio, &pkt, sizeof(pkt)); 117e689cf4aSJeff Kirsher } 118e689cf4aSJeff Kirsher 119e689cf4aSJeff Kirsher static int handle_attr_info(struct vio_driver_state *vio, 120e689cf4aSJeff Kirsher struct vio_net_attr_info *pkt) 121e689cf4aSJeff Kirsher { 122*e4defc77SDavid L Stevens struct vnet_port *port = to_vnet_port(vio); 123*e4defc77SDavid L Stevens u64 localmtu; 124*e4defc77SDavid L Stevens u8 xfer_mode; 125*e4defc77SDavid L Stevens 126*e4defc77SDavid L Stevens viodbg(HS, "GOT NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] " 127*e4defc77SDavid L Stevens "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] " 128*e4defc77SDavid L Stevens " (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n", 129e689cf4aSJeff Kirsher pkt->xfer_mode, pkt->addr_type, 130e689cf4aSJeff Kirsher (unsigned long long)pkt->addr, 131*e4defc77SDavid L Stevens pkt->ack_freq, pkt->plnk_updt, pkt->options, 132*e4defc77SDavid L Stevens (unsigned long long)pkt->mtu, port->rmtu, pkt->cflags, 133*e4defc77SDavid L Stevens pkt->ipv4_lso_maxlen); 134e689cf4aSJeff Kirsher 135e689cf4aSJeff Kirsher pkt->tag.sid = vio_send_sid(vio); 136e689cf4aSJeff Kirsher 137*e4defc77SDavid L Stevens xfer_mode = pkt->xfer_mode; 138*e4defc77SDavid L Stevens /* for version < 1.2, VIO_DRING_MODE = 0x3 and no bitmask */ 139*e4defc77SDavid L Stevens if (vio_version_before(vio, 1, 2) && xfer_mode == VIO_DRING_MODE) 140*e4defc77SDavid L Stevens xfer_mode = VIO_NEW_DRING_MODE; 141*e4defc77SDavid L Stevens 142*e4defc77SDavid L Stevens /* MTU negotiation: 143*e4defc77SDavid L Stevens * < v1.3 - ETH_FRAME_LEN exactly 144*e4defc77SDavid L Stevens * > v1.3 - MIN(pkt.mtu, VNET_MAXPACKET, port->rmtu) and change 145*e4defc77SDavid L Stevens * pkt->mtu for ACK 146*e4defc77SDavid L Stevens * = v1.3 - ETH_FRAME_LEN + VLAN_HLEN exactly 147*e4defc77SDavid L Stevens */ 148*e4defc77SDavid L Stevens if (vio_version_before(vio, 1, 3)) { 149*e4defc77SDavid L Stevens localmtu = ETH_FRAME_LEN; 150*e4defc77SDavid L Stevens } else if (vio_version_after(vio, 1, 3)) { 151*e4defc77SDavid L Stevens localmtu = port->rmtu ? port->rmtu : VNET_MAXPACKET; 152*e4defc77SDavid L Stevens localmtu = min(pkt->mtu, localmtu); 153*e4defc77SDavid L Stevens pkt->mtu = localmtu; 154*e4defc77SDavid L Stevens } else { /* v1.3 */ 155*e4defc77SDavid L Stevens localmtu = ETH_FRAME_LEN + VLAN_HLEN; 156*e4defc77SDavid L Stevens } 157*e4defc77SDavid L Stevens port->rmtu = localmtu; 158*e4defc77SDavid L Stevens 159*e4defc77SDavid L Stevens /* for version >= 1.6, ACK packet mode we support */ 160*e4defc77SDavid L Stevens if (vio_version_after_eq(vio, 1, 6)) { 161*e4defc77SDavid L Stevens pkt->xfer_mode = VIO_NEW_DRING_MODE; 162*e4defc77SDavid L Stevens pkt->options = VIO_TX_DRING; 163*e4defc77SDavid L Stevens } 164*e4defc77SDavid L Stevens 165*e4defc77SDavid L Stevens if (!(xfer_mode | VIO_NEW_DRING_MODE) || 166e689cf4aSJeff Kirsher pkt->addr_type != VNET_ADDR_ETHERMAC || 167*e4defc77SDavid L Stevens pkt->mtu != localmtu) { 168e689cf4aSJeff Kirsher viodbg(HS, "SEND NET ATTR NACK\n"); 169e689cf4aSJeff Kirsher 170e689cf4aSJeff Kirsher pkt->tag.stype = VIO_SUBTYPE_NACK; 171e689cf4aSJeff Kirsher 172e689cf4aSJeff Kirsher (void) vio_ldc_send(vio, pkt, sizeof(*pkt)); 173e689cf4aSJeff Kirsher 174e689cf4aSJeff Kirsher return -ECONNRESET; 175e689cf4aSJeff Kirsher } else { 176*e4defc77SDavid L Stevens viodbg(HS, "SEND NET ATTR ACK xmode[0x%x] atype[0x%x] " 177*e4defc77SDavid L Stevens "addr[%llx] ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] " 178*e4defc77SDavid L Stevens "mtu[%llu] (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n", 179*e4defc77SDavid L Stevens pkt->xfer_mode, pkt->addr_type, 180*e4defc77SDavid L Stevens (unsigned long long)pkt->addr, 181*e4defc77SDavid L Stevens pkt->ack_freq, pkt->plnk_updt, pkt->options, 182*e4defc77SDavid L Stevens (unsigned long long)pkt->mtu, port->rmtu, pkt->cflags, 183*e4defc77SDavid L Stevens pkt->ipv4_lso_maxlen); 184e689cf4aSJeff Kirsher 185e689cf4aSJeff Kirsher pkt->tag.stype = VIO_SUBTYPE_ACK; 186e689cf4aSJeff Kirsher 187e689cf4aSJeff Kirsher return vio_ldc_send(vio, pkt, sizeof(*pkt)); 188e689cf4aSJeff Kirsher } 189e689cf4aSJeff Kirsher 190e689cf4aSJeff Kirsher } 191e689cf4aSJeff Kirsher 192e689cf4aSJeff Kirsher static int handle_attr_ack(struct vio_driver_state *vio, 193e689cf4aSJeff Kirsher struct vio_net_attr_info *pkt) 194e689cf4aSJeff Kirsher { 195e689cf4aSJeff Kirsher viodbg(HS, "GOT NET ATTR ACK\n"); 196e689cf4aSJeff Kirsher 197e689cf4aSJeff Kirsher return 0; 198e689cf4aSJeff Kirsher } 199e689cf4aSJeff Kirsher 200e689cf4aSJeff Kirsher static int handle_attr_nack(struct vio_driver_state *vio, 201e689cf4aSJeff Kirsher struct vio_net_attr_info *pkt) 202e689cf4aSJeff Kirsher { 203e689cf4aSJeff Kirsher viodbg(HS, "GOT NET ATTR NACK\n"); 204e689cf4aSJeff Kirsher 205e689cf4aSJeff Kirsher return -ECONNRESET; 206e689cf4aSJeff Kirsher } 207e689cf4aSJeff Kirsher 208e689cf4aSJeff Kirsher static int vnet_handle_attr(struct vio_driver_state *vio, void *arg) 209e689cf4aSJeff Kirsher { 210e689cf4aSJeff Kirsher struct vio_net_attr_info *pkt = arg; 211e689cf4aSJeff Kirsher 212e689cf4aSJeff Kirsher switch (pkt->tag.stype) { 213e689cf4aSJeff Kirsher case VIO_SUBTYPE_INFO: 214e689cf4aSJeff Kirsher return handle_attr_info(vio, pkt); 215e689cf4aSJeff Kirsher 216e689cf4aSJeff Kirsher case VIO_SUBTYPE_ACK: 217e689cf4aSJeff Kirsher return handle_attr_ack(vio, pkt); 218e689cf4aSJeff Kirsher 219e689cf4aSJeff Kirsher case VIO_SUBTYPE_NACK: 220e689cf4aSJeff Kirsher return handle_attr_nack(vio, pkt); 221e689cf4aSJeff Kirsher 222e689cf4aSJeff Kirsher default: 223e689cf4aSJeff Kirsher return -ECONNRESET; 224e689cf4aSJeff Kirsher } 225e689cf4aSJeff Kirsher } 226e689cf4aSJeff Kirsher 227e689cf4aSJeff Kirsher static void vnet_handshake_complete(struct vio_driver_state *vio) 228e689cf4aSJeff Kirsher { 229e689cf4aSJeff Kirsher struct vio_dring_state *dr; 230e689cf4aSJeff Kirsher 231e689cf4aSJeff Kirsher dr = &vio->drings[VIO_DRIVER_RX_RING]; 232e689cf4aSJeff Kirsher dr->snd_nxt = dr->rcv_nxt = 1; 233e689cf4aSJeff Kirsher 234e689cf4aSJeff Kirsher dr = &vio->drings[VIO_DRIVER_TX_RING]; 235e689cf4aSJeff Kirsher dr->snd_nxt = dr->rcv_nxt = 1; 236e689cf4aSJeff Kirsher } 237e689cf4aSJeff Kirsher 238e689cf4aSJeff Kirsher /* The hypervisor interface that implements copying to/from imported 239e689cf4aSJeff Kirsher * memory from another domain requires that copies are done to 8-byte 240e689cf4aSJeff Kirsher * aligned buffers, and that the lengths of such copies are also 8-byte 241e689cf4aSJeff Kirsher * multiples. 242e689cf4aSJeff Kirsher * 243e689cf4aSJeff Kirsher * So we align skb->data to an 8-byte multiple and pad-out the data 244e689cf4aSJeff Kirsher * area so we can round the copy length up to the next multiple of 245e689cf4aSJeff Kirsher * 8 for the copy. 246e689cf4aSJeff Kirsher * 247e689cf4aSJeff Kirsher * The transmitter puts the actual start of the packet 6 bytes into 248e689cf4aSJeff Kirsher * the buffer it sends over, so that the IP headers after the ethernet 249e689cf4aSJeff Kirsher * header are aligned properly. These 6 bytes are not in the descriptor 250e689cf4aSJeff Kirsher * length, they are simply implied. This offset is represented using 251e689cf4aSJeff Kirsher * the VNET_PACKET_SKIP macro. 252e689cf4aSJeff Kirsher */ 253e689cf4aSJeff Kirsher static struct sk_buff *alloc_and_align_skb(struct net_device *dev, 254e689cf4aSJeff Kirsher unsigned int len) 255e689cf4aSJeff Kirsher { 256e689cf4aSJeff Kirsher struct sk_buff *skb = netdev_alloc_skb(dev, len+VNET_PACKET_SKIP+8+8); 257e689cf4aSJeff Kirsher unsigned long addr, off; 258e689cf4aSJeff Kirsher 259e689cf4aSJeff Kirsher if (unlikely(!skb)) 260e689cf4aSJeff Kirsher return NULL; 261e689cf4aSJeff Kirsher 262e689cf4aSJeff Kirsher addr = (unsigned long) skb->data; 263e689cf4aSJeff Kirsher off = ((addr + 7UL) & ~7UL) - addr; 264e689cf4aSJeff Kirsher if (off) 265e689cf4aSJeff Kirsher skb_reserve(skb, off); 266e689cf4aSJeff Kirsher 267e689cf4aSJeff Kirsher return skb; 268e689cf4aSJeff Kirsher } 269e689cf4aSJeff Kirsher 270e689cf4aSJeff Kirsher static int vnet_rx_one(struct vnet_port *port, unsigned int len, 271e689cf4aSJeff Kirsher struct ldc_trans_cookie *cookies, int ncookies) 272e689cf4aSJeff Kirsher { 273e689cf4aSJeff Kirsher struct net_device *dev = port->vp->dev; 274e689cf4aSJeff Kirsher unsigned int copy_len; 275e689cf4aSJeff Kirsher struct sk_buff *skb; 276e689cf4aSJeff Kirsher int err; 277e689cf4aSJeff Kirsher 278e689cf4aSJeff Kirsher err = -EMSGSIZE; 279*e4defc77SDavid L Stevens if (unlikely(len < ETH_ZLEN || len > port->rmtu)) { 280e689cf4aSJeff Kirsher dev->stats.rx_length_errors++; 281e689cf4aSJeff Kirsher goto out_dropped; 282e689cf4aSJeff Kirsher } 283e689cf4aSJeff Kirsher 284e689cf4aSJeff Kirsher skb = alloc_and_align_skb(dev, len); 285e689cf4aSJeff Kirsher err = -ENOMEM; 286e689cf4aSJeff Kirsher if (unlikely(!skb)) { 287e689cf4aSJeff Kirsher dev->stats.rx_missed_errors++; 288e689cf4aSJeff Kirsher goto out_dropped; 289e689cf4aSJeff Kirsher } 290e689cf4aSJeff Kirsher 291e689cf4aSJeff Kirsher copy_len = (len + VNET_PACKET_SKIP + 7U) & ~7U; 292e689cf4aSJeff Kirsher skb_put(skb, copy_len); 293e689cf4aSJeff Kirsher err = ldc_copy(port->vio.lp, LDC_COPY_IN, 294e689cf4aSJeff Kirsher skb->data, copy_len, 0, 295e689cf4aSJeff Kirsher cookies, ncookies); 296e689cf4aSJeff Kirsher if (unlikely(err < 0)) { 297e689cf4aSJeff Kirsher dev->stats.rx_frame_errors++; 298e689cf4aSJeff Kirsher goto out_free_skb; 299e689cf4aSJeff Kirsher } 300e689cf4aSJeff Kirsher 301e689cf4aSJeff Kirsher skb_pull(skb, VNET_PACKET_SKIP); 302e689cf4aSJeff Kirsher skb_trim(skb, len); 303e689cf4aSJeff Kirsher skb->protocol = eth_type_trans(skb, dev); 304e689cf4aSJeff Kirsher 305e689cf4aSJeff Kirsher dev->stats.rx_packets++; 306e689cf4aSJeff Kirsher dev->stats.rx_bytes += len; 307e689cf4aSJeff Kirsher 308e689cf4aSJeff Kirsher netif_rx(skb); 309e689cf4aSJeff Kirsher 310e689cf4aSJeff Kirsher return 0; 311e689cf4aSJeff Kirsher 312e689cf4aSJeff Kirsher out_free_skb: 313e689cf4aSJeff Kirsher kfree_skb(skb); 314e689cf4aSJeff Kirsher 315e689cf4aSJeff Kirsher out_dropped: 316e689cf4aSJeff Kirsher dev->stats.rx_dropped++; 317e689cf4aSJeff Kirsher return err; 318e689cf4aSJeff Kirsher } 319e689cf4aSJeff Kirsher 320e689cf4aSJeff Kirsher static int vnet_send_ack(struct vnet_port *port, struct vio_dring_state *dr, 321e689cf4aSJeff Kirsher u32 start, u32 end, u8 vio_dring_state) 322e689cf4aSJeff Kirsher { 323e689cf4aSJeff Kirsher struct vio_dring_data hdr = { 324e689cf4aSJeff Kirsher .tag = { 325e689cf4aSJeff Kirsher .type = VIO_TYPE_DATA, 326e689cf4aSJeff Kirsher .stype = VIO_SUBTYPE_ACK, 327e689cf4aSJeff Kirsher .stype_env = VIO_DRING_DATA, 328e689cf4aSJeff Kirsher .sid = vio_send_sid(&port->vio), 329e689cf4aSJeff Kirsher }, 330e689cf4aSJeff Kirsher .dring_ident = dr->ident, 331e689cf4aSJeff Kirsher .start_idx = start, 332e689cf4aSJeff Kirsher .end_idx = end, 333e689cf4aSJeff Kirsher .state = vio_dring_state, 334e689cf4aSJeff Kirsher }; 335e689cf4aSJeff Kirsher int err, delay; 336adddc32dSSowmini Varadhan int retries = 0; 337e689cf4aSJeff Kirsher 338e689cf4aSJeff Kirsher hdr.seq = dr->snd_nxt; 339e689cf4aSJeff Kirsher delay = 1; 340e689cf4aSJeff Kirsher do { 341e689cf4aSJeff Kirsher err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); 342e689cf4aSJeff Kirsher if (err > 0) { 343e689cf4aSJeff Kirsher dr->snd_nxt++; 344e689cf4aSJeff Kirsher break; 345e689cf4aSJeff Kirsher } 346e689cf4aSJeff Kirsher udelay(delay); 347e689cf4aSJeff Kirsher if ((delay <<= 1) > 128) 348e689cf4aSJeff Kirsher delay = 128; 349adddc32dSSowmini Varadhan if (retries++ > VNET_MAX_RETRIES) { 350adddc32dSSowmini Varadhan pr_info("ECONNRESET %x:%x:%x:%x:%x:%x\n", 351adddc32dSSowmini Varadhan port->raddr[0], port->raddr[1], 352adddc32dSSowmini Varadhan port->raddr[2], port->raddr[3], 353adddc32dSSowmini Varadhan port->raddr[4], port->raddr[5]); 354d1015645SSowmini Varadhan break; 355adddc32dSSowmini Varadhan } 356e689cf4aSJeff Kirsher } while (err == -EAGAIN); 357e689cf4aSJeff Kirsher 358d1015645SSowmini Varadhan if (err <= 0 && vio_dring_state == VIO_DRING_STOPPED) { 359d1015645SSowmini Varadhan port->stop_rx_idx = end; 360d1015645SSowmini Varadhan port->stop_rx = true; 361d1015645SSowmini Varadhan } else { 362d1015645SSowmini Varadhan port->stop_rx_idx = 0; 363d1015645SSowmini Varadhan port->stop_rx = false; 364d1015645SSowmini Varadhan } 365d1015645SSowmini Varadhan 366e689cf4aSJeff Kirsher return err; 367e689cf4aSJeff Kirsher } 368e689cf4aSJeff Kirsher 369e689cf4aSJeff Kirsher static u32 next_idx(u32 idx, struct vio_dring_state *dr) 370e689cf4aSJeff Kirsher { 371e689cf4aSJeff Kirsher if (++idx == dr->num_entries) 372e689cf4aSJeff Kirsher idx = 0; 373e689cf4aSJeff Kirsher return idx; 374e689cf4aSJeff Kirsher } 375e689cf4aSJeff Kirsher 376e689cf4aSJeff Kirsher static u32 prev_idx(u32 idx, struct vio_dring_state *dr) 377e689cf4aSJeff Kirsher { 378e689cf4aSJeff Kirsher if (idx == 0) 379e689cf4aSJeff Kirsher idx = dr->num_entries - 1; 380e689cf4aSJeff Kirsher else 381e689cf4aSJeff Kirsher idx--; 382e689cf4aSJeff Kirsher 383e689cf4aSJeff Kirsher return idx; 384e689cf4aSJeff Kirsher } 385e689cf4aSJeff Kirsher 386e689cf4aSJeff Kirsher static struct vio_net_desc *get_rx_desc(struct vnet_port *port, 387e689cf4aSJeff Kirsher struct vio_dring_state *dr, 388e689cf4aSJeff Kirsher u32 index) 389e689cf4aSJeff Kirsher { 390e689cf4aSJeff Kirsher struct vio_net_desc *desc = port->vio.desc_buf; 391e689cf4aSJeff Kirsher int err; 392e689cf4aSJeff Kirsher 393e689cf4aSJeff Kirsher err = ldc_get_dring_entry(port->vio.lp, desc, dr->entry_size, 394e689cf4aSJeff Kirsher (index * dr->entry_size), 395e689cf4aSJeff Kirsher dr->cookies, dr->ncookies); 396e689cf4aSJeff Kirsher if (err < 0) 397e689cf4aSJeff Kirsher return ERR_PTR(err); 398e689cf4aSJeff Kirsher 399e689cf4aSJeff Kirsher return desc; 400e689cf4aSJeff Kirsher } 401e689cf4aSJeff Kirsher 402e689cf4aSJeff Kirsher static int put_rx_desc(struct vnet_port *port, 403e689cf4aSJeff Kirsher struct vio_dring_state *dr, 404e689cf4aSJeff Kirsher struct vio_net_desc *desc, 405e689cf4aSJeff Kirsher u32 index) 406e689cf4aSJeff Kirsher { 407e689cf4aSJeff Kirsher int err; 408e689cf4aSJeff Kirsher 409e689cf4aSJeff Kirsher err = ldc_put_dring_entry(port->vio.lp, desc, dr->entry_size, 410e689cf4aSJeff Kirsher (index * dr->entry_size), 411e689cf4aSJeff Kirsher dr->cookies, dr->ncookies); 412e689cf4aSJeff Kirsher if (err < 0) 413e689cf4aSJeff Kirsher return err; 414e689cf4aSJeff Kirsher 415e689cf4aSJeff Kirsher return 0; 416e689cf4aSJeff Kirsher } 417e689cf4aSJeff Kirsher 418e689cf4aSJeff Kirsher static int vnet_walk_rx_one(struct vnet_port *port, 419e689cf4aSJeff Kirsher struct vio_dring_state *dr, 420e689cf4aSJeff Kirsher u32 index, int *needs_ack) 421e689cf4aSJeff Kirsher { 422e689cf4aSJeff Kirsher struct vio_net_desc *desc = get_rx_desc(port, dr, index); 423e689cf4aSJeff Kirsher struct vio_driver_state *vio = &port->vio; 424e689cf4aSJeff Kirsher int err; 425e689cf4aSJeff Kirsher 426e689cf4aSJeff Kirsher if (IS_ERR(desc)) 427e689cf4aSJeff Kirsher return PTR_ERR(desc); 428e689cf4aSJeff Kirsher 42978dcff7bSDavid L Stevens if (desc->hdr.state != VIO_DESC_READY) 43078dcff7bSDavid L Stevens return 1; 43178dcff7bSDavid L Stevens 43278dcff7bSDavid L Stevens rmb(); 43378dcff7bSDavid L Stevens 434e689cf4aSJeff Kirsher viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%llx:%llx]\n", 435e689cf4aSJeff Kirsher desc->hdr.state, desc->hdr.ack, 436e689cf4aSJeff Kirsher desc->size, desc->ncookies, 437e689cf4aSJeff Kirsher desc->cookies[0].cookie_addr, 438e689cf4aSJeff Kirsher desc->cookies[0].cookie_size); 439e689cf4aSJeff Kirsher 440e689cf4aSJeff Kirsher err = vnet_rx_one(port, desc->size, desc->cookies, desc->ncookies); 441e689cf4aSJeff Kirsher if (err == -ECONNRESET) 442e689cf4aSJeff Kirsher return err; 443e689cf4aSJeff Kirsher desc->hdr.state = VIO_DESC_DONE; 444e689cf4aSJeff Kirsher err = put_rx_desc(port, dr, desc, index); 445e689cf4aSJeff Kirsher if (err < 0) 446e689cf4aSJeff Kirsher return err; 447e689cf4aSJeff Kirsher *needs_ack = desc->hdr.ack; 448e689cf4aSJeff Kirsher return 0; 449e689cf4aSJeff Kirsher } 450e689cf4aSJeff Kirsher 451e689cf4aSJeff Kirsher static int vnet_walk_rx(struct vnet_port *port, struct vio_dring_state *dr, 452e689cf4aSJeff Kirsher u32 start, u32 end) 453e689cf4aSJeff Kirsher { 454e689cf4aSJeff Kirsher struct vio_driver_state *vio = &port->vio; 455e689cf4aSJeff Kirsher int ack_start = -1, ack_end = -1; 456e689cf4aSJeff Kirsher 457e689cf4aSJeff Kirsher end = (end == (u32) -1) ? prev_idx(start, dr) : next_idx(end, dr); 458e689cf4aSJeff Kirsher 459e689cf4aSJeff Kirsher viodbg(DATA, "vnet_walk_rx start[%08x] end[%08x]\n", start, end); 460e689cf4aSJeff Kirsher 461e689cf4aSJeff Kirsher while (start != end) { 462e689cf4aSJeff Kirsher int ack = 0, err = vnet_walk_rx_one(port, dr, start, &ack); 463e689cf4aSJeff Kirsher if (err == -ECONNRESET) 464e689cf4aSJeff Kirsher return err; 465e689cf4aSJeff Kirsher if (err != 0) 466e689cf4aSJeff Kirsher break; 467e689cf4aSJeff Kirsher if (ack_start == -1) 468e689cf4aSJeff Kirsher ack_start = start; 469e689cf4aSJeff Kirsher ack_end = start; 470e689cf4aSJeff Kirsher start = next_idx(start, dr); 471e689cf4aSJeff Kirsher if (ack && start != end) { 472e689cf4aSJeff Kirsher err = vnet_send_ack(port, dr, ack_start, ack_end, 473e689cf4aSJeff Kirsher VIO_DRING_ACTIVE); 474e689cf4aSJeff Kirsher if (err == -ECONNRESET) 475e689cf4aSJeff Kirsher return err; 476e689cf4aSJeff Kirsher ack_start = -1; 477e689cf4aSJeff Kirsher } 478e689cf4aSJeff Kirsher } 479e689cf4aSJeff Kirsher if (unlikely(ack_start == -1)) 480e689cf4aSJeff Kirsher ack_start = ack_end = prev_idx(start, dr); 481e689cf4aSJeff Kirsher return vnet_send_ack(port, dr, ack_start, ack_end, VIO_DRING_STOPPED); 482e689cf4aSJeff Kirsher } 483e689cf4aSJeff Kirsher 484e689cf4aSJeff Kirsher static int vnet_rx(struct vnet_port *port, void *msgbuf) 485e689cf4aSJeff Kirsher { 486e689cf4aSJeff Kirsher struct vio_dring_data *pkt = msgbuf; 487e689cf4aSJeff Kirsher struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_RX_RING]; 488e689cf4aSJeff Kirsher struct vio_driver_state *vio = &port->vio; 489e689cf4aSJeff Kirsher 490e689cf4aSJeff Kirsher viodbg(DATA, "vnet_rx stype_env[%04x] seq[%016llx] rcv_nxt[%016llx]\n", 491e689cf4aSJeff Kirsher pkt->tag.stype_env, pkt->seq, dr->rcv_nxt); 492e689cf4aSJeff Kirsher 493e689cf4aSJeff Kirsher if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) 494e689cf4aSJeff Kirsher return 0; 495e689cf4aSJeff Kirsher if (unlikely(pkt->seq != dr->rcv_nxt)) { 496e689cf4aSJeff Kirsher pr_err("RX out of sequence seq[0x%llx] rcv_nxt[0x%llx]\n", 497e689cf4aSJeff Kirsher pkt->seq, dr->rcv_nxt); 498e689cf4aSJeff Kirsher return 0; 499e689cf4aSJeff Kirsher } 500e689cf4aSJeff Kirsher 501e689cf4aSJeff Kirsher dr->rcv_nxt++; 502e689cf4aSJeff Kirsher 503e689cf4aSJeff Kirsher /* XXX Validate pkt->start_idx and pkt->end_idx XXX */ 504e689cf4aSJeff Kirsher 505e689cf4aSJeff Kirsher return vnet_walk_rx(port, dr, pkt->start_idx, pkt->end_idx); 506e689cf4aSJeff Kirsher } 507e689cf4aSJeff Kirsher 508e689cf4aSJeff Kirsher static int idx_is_pending(struct vio_dring_state *dr, u32 end) 509e689cf4aSJeff Kirsher { 510e689cf4aSJeff Kirsher u32 idx = dr->cons; 511e689cf4aSJeff Kirsher int found = 0; 512e689cf4aSJeff Kirsher 513e689cf4aSJeff Kirsher while (idx != dr->prod) { 514e689cf4aSJeff Kirsher if (idx == end) { 515e689cf4aSJeff Kirsher found = 1; 516e689cf4aSJeff Kirsher break; 517e689cf4aSJeff Kirsher } 518e689cf4aSJeff Kirsher idx = next_idx(idx, dr); 519e689cf4aSJeff Kirsher } 520e689cf4aSJeff Kirsher return found; 521e689cf4aSJeff Kirsher } 522e689cf4aSJeff Kirsher 523e689cf4aSJeff Kirsher static int vnet_ack(struct vnet_port *port, void *msgbuf) 524e689cf4aSJeff Kirsher { 525e689cf4aSJeff Kirsher struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 526e689cf4aSJeff Kirsher struct vio_dring_data *pkt = msgbuf; 527e689cf4aSJeff Kirsher struct net_device *dev; 528e689cf4aSJeff Kirsher struct vnet *vp; 529e689cf4aSJeff Kirsher u32 end; 530d1015645SSowmini Varadhan struct vio_net_desc *desc; 531e689cf4aSJeff Kirsher if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) 532e689cf4aSJeff Kirsher return 0; 533e689cf4aSJeff Kirsher 534e689cf4aSJeff Kirsher end = pkt->end_idx; 535e689cf4aSJeff Kirsher if (unlikely(!idx_is_pending(dr, end))) 536e689cf4aSJeff Kirsher return 0; 537e689cf4aSJeff Kirsher 538d1015645SSowmini Varadhan /* sync for race conditions with vnet_start_xmit() and tell xmit it 539d1015645SSowmini Varadhan * is time to send a trigger. 540d1015645SSowmini Varadhan */ 541e689cf4aSJeff Kirsher dr->cons = next_idx(end, dr); 542d1015645SSowmini Varadhan desc = vio_dring_entry(dr, dr->cons); 543d1015645SSowmini Varadhan if (desc->hdr.state == VIO_DESC_READY && port->start_cons) { 544d1015645SSowmini Varadhan /* vnet_start_xmit() just populated this dring but missed 545d1015645SSowmini Varadhan * sending the "start" LDC message to the consumer. 546d1015645SSowmini Varadhan * Send a "start" trigger on its behalf. 547d1015645SSowmini Varadhan */ 548d1015645SSowmini Varadhan if (__vnet_tx_trigger(port, dr->cons) > 0) 549d1015645SSowmini Varadhan port->start_cons = false; 550d1015645SSowmini Varadhan else 551d1015645SSowmini Varadhan port->start_cons = true; 552d1015645SSowmini Varadhan } else { 553d1015645SSowmini Varadhan port->start_cons = true; 554d1015645SSowmini Varadhan } 555d1015645SSowmini Varadhan 556e689cf4aSJeff Kirsher 557e689cf4aSJeff Kirsher vp = port->vp; 558e689cf4aSJeff Kirsher dev = vp->dev; 559e689cf4aSJeff Kirsher if (unlikely(netif_queue_stopped(dev) && 560e689cf4aSJeff Kirsher vnet_tx_dring_avail(dr) >= VNET_TX_WAKEUP_THRESH(dr))) 561e689cf4aSJeff Kirsher return 1; 562e689cf4aSJeff Kirsher 563e689cf4aSJeff Kirsher return 0; 564e689cf4aSJeff Kirsher } 565e689cf4aSJeff Kirsher 566e689cf4aSJeff Kirsher static int vnet_nack(struct vnet_port *port, void *msgbuf) 567e689cf4aSJeff Kirsher { 568e689cf4aSJeff Kirsher /* XXX just reset or similar XXX */ 569e689cf4aSJeff Kirsher return 0; 570e689cf4aSJeff Kirsher } 571e689cf4aSJeff Kirsher 572e689cf4aSJeff Kirsher static int handle_mcast(struct vnet_port *port, void *msgbuf) 573e689cf4aSJeff Kirsher { 574e689cf4aSJeff Kirsher struct vio_net_mcast_info *pkt = msgbuf; 575e689cf4aSJeff Kirsher 576e689cf4aSJeff Kirsher if (pkt->tag.stype != VIO_SUBTYPE_ACK) 577e689cf4aSJeff Kirsher pr_err("%s: Got unexpected MCAST reply [%02x:%02x:%04x:%08x]\n", 578e689cf4aSJeff Kirsher port->vp->dev->name, 579e689cf4aSJeff Kirsher pkt->tag.type, 580e689cf4aSJeff Kirsher pkt->tag.stype, 581e689cf4aSJeff Kirsher pkt->tag.stype_env, 582e689cf4aSJeff Kirsher pkt->tag.sid); 583e689cf4aSJeff Kirsher 584e689cf4aSJeff Kirsher return 0; 585e689cf4aSJeff Kirsher } 586e689cf4aSJeff Kirsher 5871d311ad2SSowmini Varadhan static void maybe_tx_wakeup(unsigned long param) 588e689cf4aSJeff Kirsher { 5891d311ad2SSowmini Varadhan struct vnet *vp = (struct vnet *)param; 590e689cf4aSJeff Kirsher struct net_device *dev = vp->dev; 591e689cf4aSJeff Kirsher 592e689cf4aSJeff Kirsher netif_tx_lock(dev); 593e689cf4aSJeff Kirsher if (likely(netif_queue_stopped(dev))) { 594e689cf4aSJeff Kirsher struct vnet_port *port; 595e689cf4aSJeff Kirsher int wake = 1; 596e689cf4aSJeff Kirsher 597e689cf4aSJeff Kirsher list_for_each_entry(port, &vp->port_list, list) { 598e689cf4aSJeff Kirsher struct vio_dring_state *dr; 599e689cf4aSJeff Kirsher 600e689cf4aSJeff Kirsher dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 601e689cf4aSJeff Kirsher if (vnet_tx_dring_avail(dr) < 602e689cf4aSJeff Kirsher VNET_TX_WAKEUP_THRESH(dr)) { 603e689cf4aSJeff Kirsher wake = 0; 604e689cf4aSJeff Kirsher break; 605e689cf4aSJeff Kirsher } 606e689cf4aSJeff Kirsher } 607e689cf4aSJeff Kirsher if (wake) 608e689cf4aSJeff Kirsher netif_wake_queue(dev); 609e689cf4aSJeff Kirsher } 610e689cf4aSJeff Kirsher netif_tx_unlock(dev); 611e689cf4aSJeff Kirsher } 612e689cf4aSJeff Kirsher 613e689cf4aSJeff Kirsher static void vnet_event(void *arg, int event) 614e689cf4aSJeff Kirsher { 615e689cf4aSJeff Kirsher struct vnet_port *port = arg; 616e689cf4aSJeff Kirsher struct vio_driver_state *vio = &port->vio; 617e689cf4aSJeff Kirsher unsigned long flags; 618e689cf4aSJeff Kirsher int tx_wakeup, err; 619e689cf4aSJeff Kirsher 620e689cf4aSJeff Kirsher spin_lock_irqsave(&vio->lock, flags); 621e689cf4aSJeff Kirsher 622e689cf4aSJeff Kirsher if (unlikely(event == LDC_EVENT_RESET || 623e689cf4aSJeff Kirsher event == LDC_EVENT_UP)) { 624e689cf4aSJeff Kirsher vio_link_state_change(vio, event); 625e689cf4aSJeff Kirsher spin_unlock_irqrestore(&vio->lock, flags); 626e689cf4aSJeff Kirsher 627*e4defc77SDavid L Stevens if (event == LDC_EVENT_RESET) { 628*e4defc77SDavid L Stevens port->rmtu = 0; 629e689cf4aSJeff Kirsher vio_port_up(vio); 630*e4defc77SDavid L Stevens } 631e689cf4aSJeff Kirsher return; 632e689cf4aSJeff Kirsher } 633e689cf4aSJeff Kirsher 634e689cf4aSJeff Kirsher if (unlikely(event != LDC_EVENT_DATA_READY)) { 635fe3881cfSJoe Perches pr_warn("Unexpected LDC event %d\n", event); 636e689cf4aSJeff Kirsher spin_unlock_irqrestore(&vio->lock, flags); 637e689cf4aSJeff Kirsher return; 638e689cf4aSJeff Kirsher } 639e689cf4aSJeff Kirsher 640e689cf4aSJeff Kirsher tx_wakeup = err = 0; 641e689cf4aSJeff Kirsher while (1) { 642e689cf4aSJeff Kirsher union { 643e689cf4aSJeff Kirsher struct vio_msg_tag tag; 644e689cf4aSJeff Kirsher u64 raw[8]; 645e689cf4aSJeff Kirsher } msgbuf; 646e689cf4aSJeff Kirsher 647e689cf4aSJeff Kirsher err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf)); 648e689cf4aSJeff Kirsher if (unlikely(err < 0)) { 649e689cf4aSJeff Kirsher if (err == -ECONNRESET) 650e689cf4aSJeff Kirsher vio_conn_reset(vio); 651e689cf4aSJeff Kirsher break; 652e689cf4aSJeff Kirsher } 653e689cf4aSJeff Kirsher if (err == 0) 654e689cf4aSJeff Kirsher break; 655e689cf4aSJeff Kirsher viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n", 656e689cf4aSJeff Kirsher msgbuf.tag.type, 657e689cf4aSJeff Kirsher msgbuf.tag.stype, 658e689cf4aSJeff Kirsher msgbuf.tag.stype_env, 659e689cf4aSJeff Kirsher msgbuf.tag.sid); 660e689cf4aSJeff Kirsher err = vio_validate_sid(vio, &msgbuf.tag); 661e689cf4aSJeff Kirsher if (err < 0) 662e689cf4aSJeff Kirsher break; 663e689cf4aSJeff Kirsher 664e689cf4aSJeff Kirsher if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) { 665e689cf4aSJeff Kirsher if (msgbuf.tag.stype == VIO_SUBTYPE_INFO) { 666e689cf4aSJeff Kirsher err = vnet_rx(port, &msgbuf); 667e689cf4aSJeff Kirsher } else if (msgbuf.tag.stype == VIO_SUBTYPE_ACK) { 668e689cf4aSJeff Kirsher err = vnet_ack(port, &msgbuf); 669e689cf4aSJeff Kirsher if (err > 0) 670e689cf4aSJeff Kirsher tx_wakeup |= err; 671e689cf4aSJeff Kirsher } else if (msgbuf.tag.stype == VIO_SUBTYPE_NACK) { 672e689cf4aSJeff Kirsher err = vnet_nack(port, &msgbuf); 673e689cf4aSJeff Kirsher } 674e689cf4aSJeff Kirsher } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { 675e689cf4aSJeff Kirsher if (msgbuf.tag.stype_env == VNET_MCAST_INFO) 676e689cf4aSJeff Kirsher err = handle_mcast(port, &msgbuf); 677e689cf4aSJeff Kirsher else 678e689cf4aSJeff Kirsher err = vio_control_pkt_engine(vio, &msgbuf); 679e689cf4aSJeff Kirsher if (err) 680e689cf4aSJeff Kirsher break; 681e689cf4aSJeff Kirsher } else { 682e689cf4aSJeff Kirsher err = vnet_handle_unknown(port, &msgbuf); 683e689cf4aSJeff Kirsher } 684e689cf4aSJeff Kirsher if (err == -ECONNRESET) 685e689cf4aSJeff Kirsher break; 686e689cf4aSJeff Kirsher } 687e689cf4aSJeff Kirsher spin_unlock(&vio->lock); 6881d311ad2SSowmini Varadhan /* Kick off a tasklet to wake the queue. We cannot call 6891d311ad2SSowmini Varadhan * maybe_tx_wakeup directly here because we could deadlock on 6901d311ad2SSowmini Varadhan * netif_tx_lock() with dev_watchdog() 6911d311ad2SSowmini Varadhan */ 692e689cf4aSJeff Kirsher if (unlikely(tx_wakeup && err != -ECONNRESET)) 6931d311ad2SSowmini Varadhan tasklet_schedule(&port->vp->vnet_tx_wakeup); 6941d311ad2SSowmini Varadhan 695e689cf4aSJeff Kirsher local_irq_restore(flags); 696e689cf4aSJeff Kirsher } 697e689cf4aSJeff Kirsher 698d1015645SSowmini Varadhan static int __vnet_tx_trigger(struct vnet_port *port, u32 start) 699e689cf4aSJeff Kirsher { 700e689cf4aSJeff Kirsher struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 701e689cf4aSJeff Kirsher struct vio_dring_data hdr = { 702e689cf4aSJeff Kirsher .tag = { 703e689cf4aSJeff Kirsher .type = VIO_TYPE_DATA, 704e689cf4aSJeff Kirsher .stype = VIO_SUBTYPE_INFO, 705e689cf4aSJeff Kirsher .stype_env = VIO_DRING_DATA, 706e689cf4aSJeff Kirsher .sid = vio_send_sid(&port->vio), 707e689cf4aSJeff Kirsher }, 708e689cf4aSJeff Kirsher .dring_ident = dr->ident, 709d1015645SSowmini Varadhan .start_idx = start, 710e689cf4aSJeff Kirsher .end_idx = (u32) -1, 711e689cf4aSJeff Kirsher }; 712e689cf4aSJeff Kirsher int err, delay; 713adddc32dSSowmini Varadhan int retries = 0; 714e689cf4aSJeff Kirsher 715d1015645SSowmini Varadhan if (port->stop_rx) { 716d1015645SSowmini Varadhan err = vnet_send_ack(port, 717d1015645SSowmini Varadhan &port->vio.drings[VIO_DRIVER_RX_RING], 718d1015645SSowmini Varadhan port->stop_rx_idx, -1, 719d1015645SSowmini Varadhan VIO_DRING_STOPPED); 720d1015645SSowmini Varadhan if (err <= 0) 721d1015645SSowmini Varadhan return err; 722d1015645SSowmini Varadhan } 723d1015645SSowmini Varadhan 724e689cf4aSJeff Kirsher hdr.seq = dr->snd_nxt; 725e689cf4aSJeff Kirsher delay = 1; 726e689cf4aSJeff Kirsher do { 727e689cf4aSJeff Kirsher err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); 728e689cf4aSJeff Kirsher if (err > 0) { 729e689cf4aSJeff Kirsher dr->snd_nxt++; 730e689cf4aSJeff Kirsher break; 731e689cf4aSJeff Kirsher } 732e689cf4aSJeff Kirsher udelay(delay); 733e689cf4aSJeff Kirsher if ((delay <<= 1) > 128) 734e689cf4aSJeff Kirsher delay = 128; 735adddc32dSSowmini Varadhan if (retries++ > VNET_MAX_RETRIES) 736adddc32dSSowmini Varadhan break; 737e689cf4aSJeff Kirsher } while (err == -EAGAIN); 738e689cf4aSJeff Kirsher 739e689cf4aSJeff Kirsher return err; 740e689cf4aSJeff Kirsher } 741e689cf4aSJeff Kirsher 7428266f5fcSDavid L Stevens static inline bool port_is_up(struct vnet_port *vnet) 7438266f5fcSDavid L Stevens { 7448266f5fcSDavid L Stevens struct vio_driver_state *vio = &vnet->vio; 7458266f5fcSDavid L Stevens 7468266f5fcSDavid L Stevens return !!(vio->hs_state & VIO_HS_COMPLETE); 7478266f5fcSDavid L Stevens } 7488266f5fcSDavid L Stevens 749e689cf4aSJeff Kirsher struct vnet_port *__tx_port_find(struct vnet *vp, struct sk_buff *skb) 750e689cf4aSJeff Kirsher { 751e689cf4aSJeff Kirsher unsigned int hash = vnet_hashfn(skb->data); 752e689cf4aSJeff Kirsher struct hlist_head *hp = &vp->port_hash[hash]; 753e689cf4aSJeff Kirsher struct vnet_port *port; 754e689cf4aSJeff Kirsher 755b67bfe0dSSasha Levin hlist_for_each_entry(port, hp, hash) { 7568266f5fcSDavid L Stevens if (!port_is_up(port)) 7578266f5fcSDavid L Stevens continue; 7582e42e474SJoe Perches if (ether_addr_equal(port->raddr, skb->data)) 759e689cf4aSJeff Kirsher return port; 760e689cf4aSJeff Kirsher } 7618266f5fcSDavid L Stevens list_for_each_entry(port, &vp->port_list, list) { 7628266f5fcSDavid L Stevens if (!port->switch_port) 7638266f5fcSDavid L Stevens continue; 7648266f5fcSDavid L Stevens if (!port_is_up(port)) 7658266f5fcSDavid L Stevens continue; 766e689cf4aSJeff Kirsher return port; 767e689cf4aSJeff Kirsher } 7688266f5fcSDavid L Stevens return NULL; 7698266f5fcSDavid L Stevens } 770e689cf4aSJeff Kirsher 771e689cf4aSJeff Kirsher struct vnet_port *tx_port_find(struct vnet *vp, struct sk_buff *skb) 772e689cf4aSJeff Kirsher { 773e689cf4aSJeff Kirsher struct vnet_port *ret; 774e689cf4aSJeff Kirsher unsigned long flags; 775e689cf4aSJeff Kirsher 776e689cf4aSJeff Kirsher spin_lock_irqsave(&vp->lock, flags); 777e689cf4aSJeff Kirsher ret = __tx_port_find(vp, skb); 778e689cf4aSJeff Kirsher spin_unlock_irqrestore(&vp->lock, flags); 779e689cf4aSJeff Kirsher 780e689cf4aSJeff Kirsher return ret; 781e689cf4aSJeff Kirsher } 782e689cf4aSJeff Kirsher 783e689cf4aSJeff Kirsher static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) 784e689cf4aSJeff Kirsher { 785e689cf4aSJeff Kirsher struct vnet *vp = netdev_priv(dev); 786e689cf4aSJeff Kirsher struct vnet_port *port = tx_port_find(vp, skb); 787e689cf4aSJeff Kirsher struct vio_dring_state *dr; 788e689cf4aSJeff Kirsher struct vio_net_desc *d; 789e689cf4aSJeff Kirsher unsigned long flags; 790e689cf4aSJeff Kirsher unsigned int len; 791e689cf4aSJeff Kirsher void *tx_buf; 792e689cf4aSJeff Kirsher int i, err; 793e689cf4aSJeff Kirsher 794e689cf4aSJeff Kirsher if (unlikely(!port)) 795e689cf4aSJeff Kirsher goto out_dropped; 796e689cf4aSJeff Kirsher 797e689cf4aSJeff Kirsher spin_lock_irqsave(&port->vio.lock, flags); 798e689cf4aSJeff Kirsher 799e689cf4aSJeff Kirsher dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 800e689cf4aSJeff Kirsher if (unlikely(vnet_tx_dring_avail(dr) < 2)) { 801e689cf4aSJeff Kirsher if (!netif_queue_stopped(dev)) { 802e689cf4aSJeff Kirsher netif_stop_queue(dev); 803e689cf4aSJeff Kirsher 804e689cf4aSJeff Kirsher /* This is a hard error, log it. */ 805e689cf4aSJeff Kirsher netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); 806e689cf4aSJeff Kirsher dev->stats.tx_errors++; 807e689cf4aSJeff Kirsher } 808e689cf4aSJeff Kirsher spin_unlock_irqrestore(&port->vio.lock, flags); 809e689cf4aSJeff Kirsher return NETDEV_TX_BUSY; 810e689cf4aSJeff Kirsher } 811e689cf4aSJeff Kirsher 812e689cf4aSJeff Kirsher d = vio_dring_cur(dr); 813e689cf4aSJeff Kirsher 814e689cf4aSJeff Kirsher tx_buf = port->tx_bufs[dr->prod].buf; 815e689cf4aSJeff Kirsher skb_copy_from_linear_data(skb, tx_buf + VNET_PACKET_SKIP, skb->len); 816e689cf4aSJeff Kirsher 817e689cf4aSJeff Kirsher len = skb->len; 818e689cf4aSJeff Kirsher if (len < ETH_ZLEN) { 819e689cf4aSJeff Kirsher len = ETH_ZLEN; 820e689cf4aSJeff Kirsher memset(tx_buf+VNET_PACKET_SKIP+skb->len, 0, len - skb->len); 821e689cf4aSJeff Kirsher } 822e689cf4aSJeff Kirsher 8231f6394e3SSowmini Varadhan /* We don't rely on the ACKs to free the skb in vnet_start_xmit(), 8241f6394e3SSowmini Varadhan * thus it is safe to not set VIO_ACK_ENABLE for each transmission: 8251f6394e3SSowmini Varadhan * the protocol itself does not require it as long as the peer 8261f6394e3SSowmini Varadhan * sends a VIO_SUBTYPE_ACK for VIO_DRING_STOPPED. 8271f6394e3SSowmini Varadhan * 8281f6394e3SSowmini Varadhan * An ACK for every packet in the ring is expensive as the 8291f6394e3SSowmini Varadhan * sending of LDC messages is slow and affects performance. 8301f6394e3SSowmini Varadhan */ 8311f6394e3SSowmini Varadhan d->hdr.ack = VIO_ACK_DISABLE; 832e689cf4aSJeff Kirsher d->size = len; 833e689cf4aSJeff Kirsher d->ncookies = port->tx_bufs[dr->prod].ncookies; 834e689cf4aSJeff Kirsher for (i = 0; i < d->ncookies; i++) 835e689cf4aSJeff Kirsher d->cookies[i] = port->tx_bufs[dr->prod].cookies[i]; 836e689cf4aSJeff Kirsher 837e689cf4aSJeff Kirsher /* This has to be a non-SMP write barrier because we are writing 838e689cf4aSJeff Kirsher * to memory which is shared with the peer LDOM. 839e689cf4aSJeff Kirsher */ 840e689cf4aSJeff Kirsher wmb(); 841e689cf4aSJeff Kirsher 842e689cf4aSJeff Kirsher d->hdr.state = VIO_DESC_READY; 843e689cf4aSJeff Kirsher 844d1015645SSowmini Varadhan /* Exactly one ldc "start" trigger (for dr->cons) needs to be sent 845d1015645SSowmini Varadhan * to notify the consumer that some descriptors are READY. 846d1015645SSowmini Varadhan * After that "start" trigger, no additional triggers are needed until 847d1015645SSowmini Varadhan * a DRING_STOPPED is received from the consumer. The dr->cons field 848d1015645SSowmini Varadhan * (set up by vnet_ack()) has the value of the next dring index 849d1015645SSowmini Varadhan * that has not yet been ack-ed. We send a "start" trigger here 850d1015645SSowmini Varadhan * if, and only if, start_cons is true (reset it afterward). Conversely, 851d1015645SSowmini Varadhan * vnet_ack() should check if the dring corresponding to cons 852d1015645SSowmini Varadhan * is marked READY, but start_cons was false. 853d1015645SSowmini Varadhan * If so, vnet_ack() should send out the missed "start" trigger. 854d1015645SSowmini Varadhan * 855d1015645SSowmini Varadhan * Note that the wmb() above makes sure the cookies et al. are 856d1015645SSowmini Varadhan * not globally visible before the VIO_DESC_READY, and that the 857d1015645SSowmini Varadhan * stores are ordered correctly by the compiler. The consumer will 858d1015645SSowmini Varadhan * not proceed until the VIO_DESC_READY is visible assuring that 859d1015645SSowmini Varadhan * the consumer does not observe anything related to descriptors 860d1015645SSowmini Varadhan * out of order. The HV trap from the LDC start trigger is the 861d1015645SSowmini Varadhan * producer to consumer announcement that work is available to the 862d1015645SSowmini Varadhan * consumer 863d1015645SSowmini Varadhan */ 864d1015645SSowmini Varadhan if (!port->start_cons) 865d1015645SSowmini Varadhan goto ldc_start_done; /* previous trigger suffices */ 866d1015645SSowmini Varadhan 867d1015645SSowmini Varadhan err = __vnet_tx_trigger(port, dr->cons); 868e689cf4aSJeff Kirsher if (unlikely(err < 0)) { 869e689cf4aSJeff Kirsher netdev_info(dev, "TX trigger error %d\n", err); 870e689cf4aSJeff Kirsher d->hdr.state = VIO_DESC_FREE; 871e689cf4aSJeff Kirsher dev->stats.tx_carrier_errors++; 872e689cf4aSJeff Kirsher goto out_dropped_unlock; 873e689cf4aSJeff Kirsher } 874e689cf4aSJeff Kirsher 875d1015645SSowmini Varadhan ldc_start_done: 876d1015645SSowmini Varadhan port->start_cons = false; 877d1015645SSowmini Varadhan 878e689cf4aSJeff Kirsher dev->stats.tx_packets++; 879e689cf4aSJeff Kirsher dev->stats.tx_bytes += skb->len; 880e689cf4aSJeff Kirsher 881e689cf4aSJeff Kirsher dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1); 882e689cf4aSJeff Kirsher if (unlikely(vnet_tx_dring_avail(dr) < 2)) { 883e689cf4aSJeff Kirsher netif_stop_queue(dev); 884e689cf4aSJeff Kirsher if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr)) 885e689cf4aSJeff Kirsher netif_wake_queue(dev); 886e689cf4aSJeff Kirsher } 887e689cf4aSJeff Kirsher 888e689cf4aSJeff Kirsher spin_unlock_irqrestore(&port->vio.lock, flags); 889e689cf4aSJeff Kirsher 890e689cf4aSJeff Kirsher dev_kfree_skb(skb); 891e689cf4aSJeff Kirsher 892e689cf4aSJeff Kirsher return NETDEV_TX_OK; 893e689cf4aSJeff Kirsher 894e689cf4aSJeff Kirsher out_dropped_unlock: 895e689cf4aSJeff Kirsher spin_unlock_irqrestore(&port->vio.lock, flags); 896e689cf4aSJeff Kirsher 897e689cf4aSJeff Kirsher out_dropped: 898e689cf4aSJeff Kirsher dev_kfree_skb(skb); 899e689cf4aSJeff Kirsher dev->stats.tx_dropped++; 900e689cf4aSJeff Kirsher return NETDEV_TX_OK; 901e689cf4aSJeff Kirsher } 902e689cf4aSJeff Kirsher 903e689cf4aSJeff Kirsher static void vnet_tx_timeout(struct net_device *dev) 904e689cf4aSJeff Kirsher { 905e689cf4aSJeff Kirsher /* XXX Implement me XXX */ 906e689cf4aSJeff Kirsher } 907e689cf4aSJeff Kirsher 908e689cf4aSJeff Kirsher static int vnet_open(struct net_device *dev) 909e689cf4aSJeff Kirsher { 910e689cf4aSJeff Kirsher netif_carrier_on(dev); 911e689cf4aSJeff Kirsher netif_start_queue(dev); 912e689cf4aSJeff Kirsher 913e689cf4aSJeff Kirsher return 0; 914e689cf4aSJeff Kirsher } 915e689cf4aSJeff Kirsher 916e689cf4aSJeff Kirsher static int vnet_close(struct net_device *dev) 917e689cf4aSJeff Kirsher { 918e689cf4aSJeff Kirsher netif_stop_queue(dev); 919e689cf4aSJeff Kirsher netif_carrier_off(dev); 920e689cf4aSJeff Kirsher 921e689cf4aSJeff Kirsher return 0; 922e689cf4aSJeff Kirsher } 923e689cf4aSJeff Kirsher 924e689cf4aSJeff Kirsher static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr) 925e689cf4aSJeff Kirsher { 926e689cf4aSJeff Kirsher struct vnet_mcast_entry *m; 927e689cf4aSJeff Kirsher 928e689cf4aSJeff Kirsher for (m = vp->mcast_list; m; m = m->next) { 92900fa4ce9Sdingtianhong if (ether_addr_equal(m->addr, addr)) 930e689cf4aSJeff Kirsher return m; 931e689cf4aSJeff Kirsher } 932e689cf4aSJeff Kirsher return NULL; 933e689cf4aSJeff Kirsher } 934e689cf4aSJeff Kirsher 935e689cf4aSJeff Kirsher static void __update_mc_list(struct vnet *vp, struct net_device *dev) 936e689cf4aSJeff Kirsher { 937e689cf4aSJeff Kirsher struct netdev_hw_addr *ha; 938e689cf4aSJeff Kirsher 939e689cf4aSJeff Kirsher netdev_for_each_mc_addr(ha, dev) { 940e689cf4aSJeff Kirsher struct vnet_mcast_entry *m; 941e689cf4aSJeff Kirsher 942e689cf4aSJeff Kirsher m = __vnet_mc_find(vp, ha->addr); 943e689cf4aSJeff Kirsher if (m) { 944e689cf4aSJeff Kirsher m->hit = 1; 945e689cf4aSJeff Kirsher continue; 946e689cf4aSJeff Kirsher } 947e689cf4aSJeff Kirsher 948e689cf4aSJeff Kirsher if (!m) { 949e689cf4aSJeff Kirsher m = kzalloc(sizeof(*m), GFP_ATOMIC); 950e689cf4aSJeff Kirsher if (!m) 951e689cf4aSJeff Kirsher continue; 952e689cf4aSJeff Kirsher memcpy(m->addr, ha->addr, ETH_ALEN); 953e689cf4aSJeff Kirsher m->hit = 1; 954e689cf4aSJeff Kirsher 955e689cf4aSJeff Kirsher m->next = vp->mcast_list; 956e689cf4aSJeff Kirsher vp->mcast_list = m; 957e689cf4aSJeff Kirsher } 958e689cf4aSJeff Kirsher } 959e689cf4aSJeff Kirsher } 960e689cf4aSJeff Kirsher 961e689cf4aSJeff Kirsher static void __send_mc_list(struct vnet *vp, struct vnet_port *port) 962e689cf4aSJeff Kirsher { 963e689cf4aSJeff Kirsher struct vio_net_mcast_info info; 964e689cf4aSJeff Kirsher struct vnet_mcast_entry *m, **pp; 965e689cf4aSJeff Kirsher int n_addrs; 966e689cf4aSJeff Kirsher 967e689cf4aSJeff Kirsher memset(&info, 0, sizeof(info)); 968e689cf4aSJeff Kirsher 969e689cf4aSJeff Kirsher info.tag.type = VIO_TYPE_CTRL; 970e689cf4aSJeff Kirsher info.tag.stype = VIO_SUBTYPE_INFO; 971e689cf4aSJeff Kirsher info.tag.stype_env = VNET_MCAST_INFO; 972e689cf4aSJeff Kirsher info.tag.sid = vio_send_sid(&port->vio); 973e689cf4aSJeff Kirsher info.set = 1; 974e689cf4aSJeff Kirsher 975e689cf4aSJeff Kirsher n_addrs = 0; 976e689cf4aSJeff Kirsher for (m = vp->mcast_list; m; m = m->next) { 977e689cf4aSJeff Kirsher if (m->sent) 978e689cf4aSJeff Kirsher continue; 979e689cf4aSJeff Kirsher m->sent = 1; 980e689cf4aSJeff Kirsher memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], 981e689cf4aSJeff Kirsher m->addr, ETH_ALEN); 982e689cf4aSJeff Kirsher if (++n_addrs == VNET_NUM_MCAST) { 983e689cf4aSJeff Kirsher info.count = n_addrs; 984e689cf4aSJeff Kirsher 985e689cf4aSJeff Kirsher (void) vio_ldc_send(&port->vio, &info, 986e689cf4aSJeff Kirsher sizeof(info)); 987e689cf4aSJeff Kirsher n_addrs = 0; 988e689cf4aSJeff Kirsher } 989e689cf4aSJeff Kirsher } 990e689cf4aSJeff Kirsher if (n_addrs) { 991e689cf4aSJeff Kirsher info.count = n_addrs; 992e689cf4aSJeff Kirsher (void) vio_ldc_send(&port->vio, &info, sizeof(info)); 993e689cf4aSJeff Kirsher } 994e689cf4aSJeff Kirsher 995e689cf4aSJeff Kirsher info.set = 0; 996e689cf4aSJeff Kirsher 997e689cf4aSJeff Kirsher n_addrs = 0; 998e689cf4aSJeff Kirsher pp = &vp->mcast_list; 999e689cf4aSJeff Kirsher while ((m = *pp) != NULL) { 1000e689cf4aSJeff Kirsher if (m->hit) { 1001e689cf4aSJeff Kirsher m->hit = 0; 1002e689cf4aSJeff Kirsher pp = &m->next; 1003e689cf4aSJeff Kirsher continue; 1004e689cf4aSJeff Kirsher } 1005e689cf4aSJeff Kirsher 1006e689cf4aSJeff Kirsher memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], 1007e689cf4aSJeff Kirsher m->addr, ETH_ALEN); 1008e689cf4aSJeff Kirsher if (++n_addrs == VNET_NUM_MCAST) { 1009e689cf4aSJeff Kirsher info.count = n_addrs; 1010e689cf4aSJeff Kirsher (void) vio_ldc_send(&port->vio, &info, 1011e689cf4aSJeff Kirsher sizeof(info)); 1012e689cf4aSJeff Kirsher n_addrs = 0; 1013e689cf4aSJeff Kirsher } 1014e689cf4aSJeff Kirsher 1015e689cf4aSJeff Kirsher *pp = m->next; 1016e689cf4aSJeff Kirsher kfree(m); 1017e689cf4aSJeff Kirsher } 1018e689cf4aSJeff Kirsher if (n_addrs) { 1019e689cf4aSJeff Kirsher info.count = n_addrs; 1020e689cf4aSJeff Kirsher (void) vio_ldc_send(&port->vio, &info, sizeof(info)); 1021e689cf4aSJeff Kirsher } 1022e689cf4aSJeff Kirsher } 1023e689cf4aSJeff Kirsher 1024e689cf4aSJeff Kirsher static void vnet_set_rx_mode(struct net_device *dev) 1025e689cf4aSJeff Kirsher { 1026e689cf4aSJeff Kirsher struct vnet *vp = netdev_priv(dev); 1027e689cf4aSJeff Kirsher struct vnet_port *port; 1028e689cf4aSJeff Kirsher unsigned long flags; 1029e689cf4aSJeff Kirsher 1030e689cf4aSJeff Kirsher spin_lock_irqsave(&vp->lock, flags); 1031e689cf4aSJeff Kirsher if (!list_empty(&vp->port_list)) { 1032e689cf4aSJeff Kirsher port = list_entry(vp->port_list.next, struct vnet_port, list); 1033e689cf4aSJeff Kirsher 1034e689cf4aSJeff Kirsher if (port->switch_port) { 1035e689cf4aSJeff Kirsher __update_mc_list(vp, dev); 1036e689cf4aSJeff Kirsher __send_mc_list(vp, port); 1037e689cf4aSJeff Kirsher } 1038e689cf4aSJeff Kirsher } 1039e689cf4aSJeff Kirsher spin_unlock_irqrestore(&vp->lock, flags); 1040e689cf4aSJeff Kirsher } 1041e689cf4aSJeff Kirsher 1042e689cf4aSJeff Kirsher static int vnet_change_mtu(struct net_device *dev, int new_mtu) 1043e689cf4aSJeff Kirsher { 1044e689cf4aSJeff Kirsher if (new_mtu != ETH_DATA_LEN) 1045e689cf4aSJeff Kirsher return -EINVAL; 1046e689cf4aSJeff Kirsher 1047e689cf4aSJeff Kirsher dev->mtu = new_mtu; 1048e689cf4aSJeff Kirsher return 0; 1049e689cf4aSJeff Kirsher } 1050e689cf4aSJeff Kirsher 1051e689cf4aSJeff Kirsher static int vnet_set_mac_addr(struct net_device *dev, void *p) 1052e689cf4aSJeff Kirsher { 1053e689cf4aSJeff Kirsher return -EINVAL; 1054e689cf4aSJeff Kirsher } 1055e689cf4aSJeff Kirsher 1056e689cf4aSJeff Kirsher static void vnet_get_drvinfo(struct net_device *dev, 1057e689cf4aSJeff Kirsher struct ethtool_drvinfo *info) 1058e689cf4aSJeff Kirsher { 10597826d43fSJiri Pirko strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); 10607826d43fSJiri Pirko strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); 1061e689cf4aSJeff Kirsher } 1062e689cf4aSJeff Kirsher 1063e689cf4aSJeff Kirsher static u32 vnet_get_msglevel(struct net_device *dev) 1064e689cf4aSJeff Kirsher { 1065e689cf4aSJeff Kirsher struct vnet *vp = netdev_priv(dev); 1066e689cf4aSJeff Kirsher return vp->msg_enable; 1067e689cf4aSJeff Kirsher } 1068e689cf4aSJeff Kirsher 1069e689cf4aSJeff Kirsher static void vnet_set_msglevel(struct net_device *dev, u32 value) 1070e689cf4aSJeff Kirsher { 1071e689cf4aSJeff Kirsher struct vnet *vp = netdev_priv(dev); 1072e689cf4aSJeff Kirsher vp->msg_enable = value; 1073e689cf4aSJeff Kirsher } 1074e689cf4aSJeff Kirsher 1075e689cf4aSJeff Kirsher static const struct ethtool_ops vnet_ethtool_ops = { 1076e689cf4aSJeff Kirsher .get_drvinfo = vnet_get_drvinfo, 1077e689cf4aSJeff Kirsher .get_msglevel = vnet_get_msglevel, 1078e689cf4aSJeff Kirsher .set_msglevel = vnet_set_msglevel, 1079e689cf4aSJeff Kirsher .get_link = ethtool_op_get_link, 1080e689cf4aSJeff Kirsher }; 1081e689cf4aSJeff Kirsher 1082e689cf4aSJeff Kirsher static void vnet_port_free_tx_bufs(struct vnet_port *port) 1083e689cf4aSJeff Kirsher { 1084e689cf4aSJeff Kirsher struct vio_dring_state *dr; 1085e689cf4aSJeff Kirsher int i; 1086e689cf4aSJeff Kirsher 1087e689cf4aSJeff Kirsher dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 1088e689cf4aSJeff Kirsher if (dr->base) { 1089e689cf4aSJeff Kirsher ldc_free_exp_dring(port->vio.lp, dr->base, 1090e689cf4aSJeff Kirsher (dr->entry_size * dr->num_entries), 1091e689cf4aSJeff Kirsher dr->cookies, dr->ncookies); 1092e689cf4aSJeff Kirsher dr->base = NULL; 1093e689cf4aSJeff Kirsher dr->entry_size = 0; 1094e689cf4aSJeff Kirsher dr->num_entries = 0; 1095e689cf4aSJeff Kirsher dr->pending = 0; 1096e689cf4aSJeff Kirsher dr->ncookies = 0; 1097e689cf4aSJeff Kirsher } 1098e689cf4aSJeff Kirsher 1099e689cf4aSJeff Kirsher for (i = 0; i < VNET_TX_RING_SIZE; i++) { 1100e689cf4aSJeff Kirsher void *buf = port->tx_bufs[i].buf; 1101e689cf4aSJeff Kirsher 1102e689cf4aSJeff Kirsher if (!buf) 1103e689cf4aSJeff Kirsher continue; 1104e689cf4aSJeff Kirsher 1105e689cf4aSJeff Kirsher ldc_unmap(port->vio.lp, 1106e689cf4aSJeff Kirsher port->tx_bufs[i].cookies, 1107e689cf4aSJeff Kirsher port->tx_bufs[i].ncookies); 1108e689cf4aSJeff Kirsher 1109e689cf4aSJeff Kirsher kfree(buf); 1110e689cf4aSJeff Kirsher port->tx_bufs[i].buf = NULL; 1111e689cf4aSJeff Kirsher } 1112e689cf4aSJeff Kirsher } 1113e689cf4aSJeff Kirsher 1114f73d12bdSBill Pemberton static int vnet_port_alloc_tx_bufs(struct vnet_port *port) 1115e689cf4aSJeff Kirsher { 1116e689cf4aSJeff Kirsher struct vio_dring_state *dr; 1117e689cf4aSJeff Kirsher unsigned long len; 1118e689cf4aSJeff Kirsher int i, err, ncookies; 1119e689cf4aSJeff Kirsher void *dring; 1120e689cf4aSJeff Kirsher 1121e689cf4aSJeff Kirsher for (i = 0; i < VNET_TX_RING_SIZE; i++) { 1122*e4defc77SDavid L Stevens void *buf = kzalloc(VNET_MAXPACKET + 8, GFP_KERNEL); 1123*e4defc77SDavid L Stevens int map_len = (VNET_MAXPACKET + 7) & ~7; 1124e689cf4aSJeff Kirsher 1125e689cf4aSJeff Kirsher err = -ENOMEM; 1126e404decbSJoe Perches if (!buf) 1127e689cf4aSJeff Kirsher goto err_out; 1128e404decbSJoe Perches 1129e689cf4aSJeff Kirsher err = -EFAULT; 1130e689cf4aSJeff Kirsher if ((unsigned long)buf & (8UL - 1)) { 1131e689cf4aSJeff Kirsher pr_err("TX buffer misaligned\n"); 1132e689cf4aSJeff Kirsher kfree(buf); 1133e689cf4aSJeff Kirsher goto err_out; 1134e689cf4aSJeff Kirsher } 1135e689cf4aSJeff Kirsher 1136e689cf4aSJeff Kirsher err = ldc_map_single(port->vio.lp, buf, map_len, 1137e689cf4aSJeff Kirsher port->tx_bufs[i].cookies, 2, 1138e689cf4aSJeff Kirsher (LDC_MAP_SHADOW | 1139e689cf4aSJeff Kirsher LDC_MAP_DIRECT | 1140e689cf4aSJeff Kirsher LDC_MAP_RW)); 1141e689cf4aSJeff Kirsher if (err < 0) { 1142e689cf4aSJeff Kirsher kfree(buf); 1143e689cf4aSJeff Kirsher goto err_out; 1144e689cf4aSJeff Kirsher } 1145e689cf4aSJeff Kirsher port->tx_bufs[i].buf = buf; 1146e689cf4aSJeff Kirsher port->tx_bufs[i].ncookies = err; 1147e689cf4aSJeff Kirsher } 1148e689cf4aSJeff Kirsher 1149e689cf4aSJeff Kirsher dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 1150e689cf4aSJeff Kirsher 1151e689cf4aSJeff Kirsher len = (VNET_TX_RING_SIZE * 1152e689cf4aSJeff Kirsher (sizeof(struct vio_net_desc) + 1153e689cf4aSJeff Kirsher (sizeof(struct ldc_trans_cookie) * 2))); 1154e689cf4aSJeff Kirsher 1155e689cf4aSJeff Kirsher ncookies = VIO_MAX_RING_COOKIES; 1156e689cf4aSJeff Kirsher dring = ldc_alloc_exp_dring(port->vio.lp, len, 1157e689cf4aSJeff Kirsher dr->cookies, &ncookies, 1158e689cf4aSJeff Kirsher (LDC_MAP_SHADOW | 1159e689cf4aSJeff Kirsher LDC_MAP_DIRECT | 1160e689cf4aSJeff Kirsher LDC_MAP_RW)); 1161e689cf4aSJeff Kirsher if (IS_ERR(dring)) { 1162e689cf4aSJeff Kirsher err = PTR_ERR(dring); 1163e689cf4aSJeff Kirsher goto err_out; 1164e689cf4aSJeff Kirsher } 1165e689cf4aSJeff Kirsher 1166e689cf4aSJeff Kirsher dr->base = dring; 1167e689cf4aSJeff Kirsher dr->entry_size = (sizeof(struct vio_net_desc) + 1168e689cf4aSJeff Kirsher (sizeof(struct ldc_trans_cookie) * 2)); 1169e689cf4aSJeff Kirsher dr->num_entries = VNET_TX_RING_SIZE; 1170e689cf4aSJeff Kirsher dr->prod = dr->cons = 0; 1171d1015645SSowmini Varadhan port->start_cons = true; /* need an initial trigger */ 1172e689cf4aSJeff Kirsher dr->pending = VNET_TX_RING_SIZE; 1173e689cf4aSJeff Kirsher dr->ncookies = ncookies; 1174e689cf4aSJeff Kirsher 1175e689cf4aSJeff Kirsher return 0; 1176e689cf4aSJeff Kirsher 1177e689cf4aSJeff Kirsher err_out: 1178e689cf4aSJeff Kirsher vnet_port_free_tx_bufs(port); 1179e689cf4aSJeff Kirsher 1180e689cf4aSJeff Kirsher return err; 1181e689cf4aSJeff Kirsher } 1182e689cf4aSJeff Kirsher 1183e689cf4aSJeff Kirsher static LIST_HEAD(vnet_list); 1184e689cf4aSJeff Kirsher static DEFINE_MUTEX(vnet_list_mutex); 1185e689cf4aSJeff Kirsher 1186e689cf4aSJeff Kirsher static const struct net_device_ops vnet_ops = { 1187e689cf4aSJeff Kirsher .ndo_open = vnet_open, 1188e689cf4aSJeff Kirsher .ndo_stop = vnet_close, 1189afc4b13dSJiri Pirko .ndo_set_rx_mode = vnet_set_rx_mode, 1190e689cf4aSJeff Kirsher .ndo_set_mac_address = vnet_set_mac_addr, 1191e689cf4aSJeff Kirsher .ndo_validate_addr = eth_validate_addr, 1192e689cf4aSJeff Kirsher .ndo_tx_timeout = vnet_tx_timeout, 1193e689cf4aSJeff Kirsher .ndo_change_mtu = vnet_change_mtu, 1194e689cf4aSJeff Kirsher .ndo_start_xmit = vnet_start_xmit, 1195e689cf4aSJeff Kirsher }; 1196e689cf4aSJeff Kirsher 1197f73d12bdSBill Pemberton static struct vnet *vnet_new(const u64 *local_mac) 1198e689cf4aSJeff Kirsher { 1199e689cf4aSJeff Kirsher struct net_device *dev; 1200e689cf4aSJeff Kirsher struct vnet *vp; 1201e689cf4aSJeff Kirsher int err, i; 1202e689cf4aSJeff Kirsher 1203e689cf4aSJeff Kirsher dev = alloc_etherdev(sizeof(*vp)); 120441de8d4cSJoe Perches if (!dev) 1205e689cf4aSJeff Kirsher return ERR_PTR(-ENOMEM); 1206e689cf4aSJeff Kirsher 1207e689cf4aSJeff Kirsher for (i = 0; i < ETH_ALEN; i++) 1208e689cf4aSJeff Kirsher dev->dev_addr[i] = (*local_mac >> (5 - i) * 8) & 0xff; 1209e689cf4aSJeff Kirsher 1210e689cf4aSJeff Kirsher vp = netdev_priv(dev); 1211e689cf4aSJeff Kirsher 1212e689cf4aSJeff Kirsher spin_lock_init(&vp->lock); 12131d311ad2SSowmini Varadhan tasklet_init(&vp->vnet_tx_wakeup, maybe_tx_wakeup, (unsigned long)vp); 1214e689cf4aSJeff Kirsher vp->dev = dev; 1215e689cf4aSJeff Kirsher 1216e689cf4aSJeff Kirsher INIT_LIST_HEAD(&vp->port_list); 1217e689cf4aSJeff Kirsher for (i = 0; i < VNET_PORT_HASH_SIZE; i++) 1218e689cf4aSJeff Kirsher INIT_HLIST_HEAD(&vp->port_hash[i]); 1219e689cf4aSJeff Kirsher INIT_LIST_HEAD(&vp->list); 1220e689cf4aSJeff Kirsher vp->local_mac = *local_mac; 1221e689cf4aSJeff Kirsher 1222e689cf4aSJeff Kirsher dev->netdev_ops = &vnet_ops; 1223e689cf4aSJeff Kirsher dev->ethtool_ops = &vnet_ethtool_ops; 1224e689cf4aSJeff Kirsher dev->watchdog_timeo = VNET_TX_TIMEOUT; 1225e689cf4aSJeff Kirsher 1226e689cf4aSJeff Kirsher err = register_netdev(dev); 1227e689cf4aSJeff Kirsher if (err) { 1228e689cf4aSJeff Kirsher pr_err("Cannot register net device, aborting\n"); 1229e689cf4aSJeff Kirsher goto err_out_free_dev; 1230e689cf4aSJeff Kirsher } 1231e689cf4aSJeff Kirsher 1232e689cf4aSJeff Kirsher netdev_info(dev, "Sun LDOM vnet %pM\n", dev->dev_addr); 1233e689cf4aSJeff Kirsher 1234e689cf4aSJeff Kirsher list_add(&vp->list, &vnet_list); 1235e689cf4aSJeff Kirsher 1236e689cf4aSJeff Kirsher return vp; 1237e689cf4aSJeff Kirsher 1238e689cf4aSJeff Kirsher err_out_free_dev: 1239e689cf4aSJeff Kirsher free_netdev(dev); 1240e689cf4aSJeff Kirsher 1241e689cf4aSJeff Kirsher return ERR_PTR(err); 1242e689cf4aSJeff Kirsher } 1243e689cf4aSJeff Kirsher 1244f73d12bdSBill Pemberton static struct vnet *vnet_find_or_create(const u64 *local_mac) 1245e689cf4aSJeff Kirsher { 1246e689cf4aSJeff Kirsher struct vnet *iter, *vp; 1247e689cf4aSJeff Kirsher 1248e689cf4aSJeff Kirsher mutex_lock(&vnet_list_mutex); 1249e689cf4aSJeff Kirsher vp = NULL; 1250e689cf4aSJeff Kirsher list_for_each_entry(iter, &vnet_list, list) { 1251e689cf4aSJeff Kirsher if (iter->local_mac == *local_mac) { 1252e689cf4aSJeff Kirsher vp = iter; 1253e689cf4aSJeff Kirsher break; 1254e689cf4aSJeff Kirsher } 1255e689cf4aSJeff Kirsher } 1256e689cf4aSJeff Kirsher if (!vp) 1257e689cf4aSJeff Kirsher vp = vnet_new(local_mac); 1258e689cf4aSJeff Kirsher mutex_unlock(&vnet_list_mutex); 1259e689cf4aSJeff Kirsher 1260e689cf4aSJeff Kirsher return vp; 1261e689cf4aSJeff Kirsher } 1262e689cf4aSJeff Kirsher 1263a4b70a07SSowmini Varadhan static void vnet_cleanup(void) 1264a4b70a07SSowmini Varadhan { 1265a4b70a07SSowmini Varadhan struct vnet *vp; 1266a4b70a07SSowmini Varadhan struct net_device *dev; 1267a4b70a07SSowmini Varadhan 1268a4b70a07SSowmini Varadhan mutex_lock(&vnet_list_mutex); 1269a4b70a07SSowmini Varadhan while (!list_empty(&vnet_list)) { 1270a4b70a07SSowmini Varadhan vp = list_first_entry(&vnet_list, struct vnet, list); 1271a4b70a07SSowmini Varadhan list_del(&vp->list); 1272a4b70a07SSowmini Varadhan dev = vp->dev; 12731d311ad2SSowmini Varadhan tasklet_kill(&vp->vnet_tx_wakeup); 1274a4b70a07SSowmini Varadhan /* vio_unregister_driver() should have cleaned up port_list */ 1275a4b70a07SSowmini Varadhan BUG_ON(!list_empty(&vp->port_list)); 1276a4b70a07SSowmini Varadhan unregister_netdev(dev); 1277a4b70a07SSowmini Varadhan free_netdev(dev); 1278a4b70a07SSowmini Varadhan } 1279a4b70a07SSowmini Varadhan mutex_unlock(&vnet_list_mutex); 1280a4b70a07SSowmini Varadhan } 1281a4b70a07SSowmini Varadhan 1282e689cf4aSJeff Kirsher static const char *local_mac_prop = "local-mac-address"; 1283e689cf4aSJeff Kirsher 1284f73d12bdSBill Pemberton static struct vnet *vnet_find_parent(struct mdesc_handle *hp, 1285e689cf4aSJeff Kirsher u64 port_node) 1286e689cf4aSJeff Kirsher { 1287e689cf4aSJeff Kirsher const u64 *local_mac = NULL; 1288e689cf4aSJeff Kirsher u64 a; 1289e689cf4aSJeff Kirsher 1290e689cf4aSJeff Kirsher mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) { 1291e689cf4aSJeff Kirsher u64 target = mdesc_arc_target(hp, a); 1292e689cf4aSJeff Kirsher const char *name; 1293e689cf4aSJeff Kirsher 1294e689cf4aSJeff Kirsher name = mdesc_get_property(hp, target, "name", NULL); 1295e689cf4aSJeff Kirsher if (!name || strcmp(name, "network")) 1296e689cf4aSJeff Kirsher continue; 1297e689cf4aSJeff Kirsher 1298e689cf4aSJeff Kirsher local_mac = mdesc_get_property(hp, target, 1299e689cf4aSJeff Kirsher local_mac_prop, NULL); 1300e689cf4aSJeff Kirsher if (local_mac) 1301e689cf4aSJeff Kirsher break; 1302e689cf4aSJeff Kirsher } 1303e689cf4aSJeff Kirsher if (!local_mac) 1304e689cf4aSJeff Kirsher return ERR_PTR(-ENODEV); 1305e689cf4aSJeff Kirsher 1306e689cf4aSJeff Kirsher return vnet_find_or_create(local_mac); 1307e689cf4aSJeff Kirsher } 1308e689cf4aSJeff Kirsher 1309e689cf4aSJeff Kirsher static struct ldc_channel_config vnet_ldc_cfg = { 1310e689cf4aSJeff Kirsher .event = vnet_event, 1311e689cf4aSJeff Kirsher .mtu = 64, 1312e689cf4aSJeff Kirsher .mode = LDC_MODE_UNRELIABLE, 1313e689cf4aSJeff Kirsher }; 1314e689cf4aSJeff Kirsher 1315e689cf4aSJeff Kirsher static struct vio_driver_ops vnet_vio_ops = { 1316e689cf4aSJeff Kirsher .send_attr = vnet_send_attr, 1317e689cf4aSJeff Kirsher .handle_attr = vnet_handle_attr, 1318e689cf4aSJeff Kirsher .handshake_complete = vnet_handshake_complete, 1319e689cf4aSJeff Kirsher }; 1320e689cf4aSJeff Kirsher 1321f73d12bdSBill Pemberton static void print_version(void) 1322e689cf4aSJeff Kirsher { 1323e689cf4aSJeff Kirsher printk_once(KERN_INFO "%s", version); 1324e689cf4aSJeff Kirsher } 1325e689cf4aSJeff Kirsher 1326e689cf4aSJeff Kirsher const char *remote_macaddr_prop = "remote-mac-address"; 1327e689cf4aSJeff Kirsher 13281dd06ae8SGreg Kroah-Hartman static int vnet_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) 1329e689cf4aSJeff Kirsher { 1330e689cf4aSJeff Kirsher struct mdesc_handle *hp; 1331e689cf4aSJeff Kirsher struct vnet_port *port; 1332e689cf4aSJeff Kirsher unsigned long flags; 1333e689cf4aSJeff Kirsher struct vnet *vp; 1334e689cf4aSJeff Kirsher const u64 *rmac; 1335e689cf4aSJeff Kirsher int len, i, err, switch_port; 1336e689cf4aSJeff Kirsher 1337e689cf4aSJeff Kirsher print_version(); 1338e689cf4aSJeff Kirsher 1339e689cf4aSJeff Kirsher hp = mdesc_grab(); 1340e689cf4aSJeff Kirsher 1341e689cf4aSJeff Kirsher vp = vnet_find_parent(hp, vdev->mp); 1342e689cf4aSJeff Kirsher if (IS_ERR(vp)) { 1343e689cf4aSJeff Kirsher pr_err("Cannot find port parent vnet\n"); 1344e689cf4aSJeff Kirsher err = PTR_ERR(vp); 1345e689cf4aSJeff Kirsher goto err_out_put_mdesc; 1346e689cf4aSJeff Kirsher } 1347e689cf4aSJeff Kirsher 1348e689cf4aSJeff Kirsher rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); 1349e689cf4aSJeff Kirsher err = -ENODEV; 1350e689cf4aSJeff Kirsher if (!rmac) { 1351e689cf4aSJeff Kirsher pr_err("Port lacks %s property\n", remote_macaddr_prop); 1352e689cf4aSJeff Kirsher goto err_out_put_mdesc; 1353e689cf4aSJeff Kirsher } 1354e689cf4aSJeff Kirsher 1355e689cf4aSJeff Kirsher port = kzalloc(sizeof(*port), GFP_KERNEL); 1356e689cf4aSJeff Kirsher err = -ENOMEM; 1357e404decbSJoe Perches if (!port) 1358e689cf4aSJeff Kirsher goto err_out_put_mdesc; 1359e689cf4aSJeff Kirsher 1360e689cf4aSJeff Kirsher for (i = 0; i < ETH_ALEN; i++) 1361e689cf4aSJeff Kirsher port->raddr[i] = (*rmac >> (5 - i) * 8) & 0xff; 1362e689cf4aSJeff Kirsher 1363e689cf4aSJeff Kirsher port->vp = vp; 1364e689cf4aSJeff Kirsher 1365e689cf4aSJeff Kirsher err = vio_driver_init(&port->vio, vdev, VDEV_NETWORK, 1366e689cf4aSJeff Kirsher vnet_versions, ARRAY_SIZE(vnet_versions), 1367e689cf4aSJeff Kirsher &vnet_vio_ops, vp->dev->name); 1368e689cf4aSJeff Kirsher if (err) 1369e689cf4aSJeff Kirsher goto err_out_free_port; 1370e689cf4aSJeff Kirsher 1371e689cf4aSJeff Kirsher err = vio_ldc_alloc(&port->vio, &vnet_ldc_cfg, port); 1372e689cf4aSJeff Kirsher if (err) 1373e689cf4aSJeff Kirsher goto err_out_free_port; 1374e689cf4aSJeff Kirsher 1375e689cf4aSJeff Kirsher err = vnet_port_alloc_tx_bufs(port); 1376e689cf4aSJeff Kirsher if (err) 1377e689cf4aSJeff Kirsher goto err_out_free_ldc; 1378e689cf4aSJeff Kirsher 1379e689cf4aSJeff Kirsher INIT_HLIST_NODE(&port->hash); 1380e689cf4aSJeff Kirsher INIT_LIST_HEAD(&port->list); 1381e689cf4aSJeff Kirsher 1382e689cf4aSJeff Kirsher switch_port = 0; 1383e689cf4aSJeff Kirsher if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) 1384e689cf4aSJeff Kirsher switch_port = 1; 1385e689cf4aSJeff Kirsher port->switch_port = switch_port; 1386e689cf4aSJeff Kirsher 1387e689cf4aSJeff Kirsher spin_lock_irqsave(&vp->lock, flags); 1388e689cf4aSJeff Kirsher if (switch_port) 1389e689cf4aSJeff Kirsher list_add(&port->list, &vp->port_list); 1390e689cf4aSJeff Kirsher else 1391e689cf4aSJeff Kirsher list_add_tail(&port->list, &vp->port_list); 1392e689cf4aSJeff Kirsher hlist_add_head(&port->hash, &vp->port_hash[vnet_hashfn(port->raddr)]); 1393e689cf4aSJeff Kirsher spin_unlock_irqrestore(&vp->lock, flags); 1394e689cf4aSJeff Kirsher 1395e689cf4aSJeff Kirsher dev_set_drvdata(&vdev->dev, port); 1396e689cf4aSJeff Kirsher 1397e689cf4aSJeff Kirsher pr_info("%s: PORT ( remote-mac %pM%s )\n", 1398e689cf4aSJeff Kirsher vp->dev->name, port->raddr, switch_port ? " switch-port" : ""); 1399e689cf4aSJeff Kirsher 1400e689cf4aSJeff Kirsher vio_port_up(&port->vio); 1401e689cf4aSJeff Kirsher 1402e689cf4aSJeff Kirsher mdesc_release(hp); 1403e689cf4aSJeff Kirsher 1404e689cf4aSJeff Kirsher return 0; 1405e689cf4aSJeff Kirsher 1406e689cf4aSJeff Kirsher err_out_free_ldc: 1407e689cf4aSJeff Kirsher vio_ldc_free(&port->vio); 1408e689cf4aSJeff Kirsher 1409e689cf4aSJeff Kirsher err_out_free_port: 1410e689cf4aSJeff Kirsher kfree(port); 1411e689cf4aSJeff Kirsher 1412e689cf4aSJeff Kirsher err_out_put_mdesc: 1413e689cf4aSJeff Kirsher mdesc_release(hp); 1414e689cf4aSJeff Kirsher return err; 1415e689cf4aSJeff Kirsher } 1416e689cf4aSJeff Kirsher 1417e689cf4aSJeff Kirsher static int vnet_port_remove(struct vio_dev *vdev) 1418e689cf4aSJeff Kirsher { 1419e689cf4aSJeff Kirsher struct vnet_port *port = dev_get_drvdata(&vdev->dev); 1420e689cf4aSJeff Kirsher 1421e689cf4aSJeff Kirsher if (port) { 1422e689cf4aSJeff Kirsher struct vnet *vp = port->vp; 1423e689cf4aSJeff Kirsher unsigned long flags; 1424e689cf4aSJeff Kirsher 1425e689cf4aSJeff Kirsher del_timer_sync(&port->vio.timer); 1426e689cf4aSJeff Kirsher 1427e689cf4aSJeff Kirsher spin_lock_irqsave(&vp->lock, flags); 1428e689cf4aSJeff Kirsher list_del(&port->list); 1429e689cf4aSJeff Kirsher hlist_del(&port->hash); 1430e689cf4aSJeff Kirsher spin_unlock_irqrestore(&vp->lock, flags); 1431e689cf4aSJeff Kirsher 1432e689cf4aSJeff Kirsher vnet_port_free_tx_bufs(port); 1433e689cf4aSJeff Kirsher vio_ldc_free(&port->vio); 1434e689cf4aSJeff Kirsher 1435e689cf4aSJeff Kirsher dev_set_drvdata(&vdev->dev, NULL); 1436e689cf4aSJeff Kirsher 1437e689cf4aSJeff Kirsher kfree(port); 1438aabb9875SDave Kleikamp 1439e689cf4aSJeff Kirsher } 1440e689cf4aSJeff Kirsher return 0; 1441e689cf4aSJeff Kirsher } 1442e689cf4aSJeff Kirsher 1443e689cf4aSJeff Kirsher static const struct vio_device_id vnet_port_match[] = { 1444e689cf4aSJeff Kirsher { 1445e689cf4aSJeff Kirsher .type = "vnet-port", 1446e689cf4aSJeff Kirsher }, 1447e689cf4aSJeff Kirsher {}, 1448e689cf4aSJeff Kirsher }; 1449e689cf4aSJeff Kirsher MODULE_DEVICE_TABLE(vio, vnet_port_match); 1450e689cf4aSJeff Kirsher 1451e689cf4aSJeff Kirsher static struct vio_driver vnet_port_driver = { 1452e689cf4aSJeff Kirsher .id_table = vnet_port_match, 1453e689cf4aSJeff Kirsher .probe = vnet_port_probe, 1454e689cf4aSJeff Kirsher .remove = vnet_port_remove, 1455e689cf4aSJeff Kirsher .name = "vnet_port", 1456e689cf4aSJeff Kirsher }; 1457e689cf4aSJeff Kirsher 1458e689cf4aSJeff Kirsher static int __init vnet_init(void) 1459e689cf4aSJeff Kirsher { 1460e689cf4aSJeff Kirsher return vio_register_driver(&vnet_port_driver); 1461e689cf4aSJeff Kirsher } 1462e689cf4aSJeff Kirsher 1463e689cf4aSJeff Kirsher static void __exit vnet_exit(void) 1464e689cf4aSJeff Kirsher { 1465e689cf4aSJeff Kirsher vio_unregister_driver(&vnet_port_driver); 1466a4b70a07SSowmini Varadhan vnet_cleanup(); 1467e689cf4aSJeff Kirsher } 1468e689cf4aSJeff Kirsher 1469e689cf4aSJeff Kirsher module_init(vnet_init); 1470e689cf4aSJeff Kirsher module_exit(vnet_exit); 1471