X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fnet%2Fiseries_veth.c;h=855f8b2cf13b6f83d1b41011253d514b22e5cc2c;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=634cb049c600c8c3aa1440da3d80ccd22a27524e;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 634cb049c..855f8b2cf 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -81,8 +81,6 @@ #include "iseries_veth.h" -extern struct vio_dev *iSeries_veth_dev; - MODULE_AUTHOR("Kyle Lucke "); MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); MODULE_LICENSE("GPL"); @@ -119,6 +117,7 @@ struct veth_msg { int token; unsigned long in_use; struct sk_buff *skb; + struct device *dev; }; struct veth_lpar_connection { @@ -147,6 +146,7 @@ struct veth_lpar_connection { }; struct veth_port { + struct device *dev; struct net_device_stats stats; u64 mac_addr; HvLpIndexMap lpar_map; @@ -248,7 +248,7 @@ static int veth_allocate_events(HvLpIndex rlp, int number) { struct veth_allocation vc = { COMPLETION_INITIALIZER(vc.c), 0 }; - mf_allocateLpEvents(rlp, HvLpEvent_Type_VirtualLan, + mf_allocate_lp_events(rlp, HvLpEvent_Type_VirtualLan, sizeof(struct VethLpEvent), number, &veth_complete_allocation, &vc); wait_for_completion(&vc.c); @@ -642,7 +642,7 @@ static int veth_init_connection(u8 rlp) return 0; } -static void veth_destroy_connection(u8 rlp) +static void veth_stop_connection(u8 rlp) { struct veth_lpar_connection *cnx = veth_cnx[rlp]; @@ -662,18 +662,27 @@ static void veth_destroy_connection(u8 rlp) del_timer_sync(&cnx->ack_timer); if (cnx->num_events > 0) - mf_deallocateLpEvents(cnx->remote_lp, + mf_deallocate_lp_events(cnx->remote_lp, HvLpEvent_Type_VirtualLan, cnx->num_events, NULL, NULL); if (cnx->num_ack_events > 0) - mf_deallocateLpEvents(cnx->remote_lp, + mf_deallocate_lp_events(cnx->remote_lp, HvLpEvent_Type_VirtualLan, cnx->num_ack_events, NULL, NULL); +} - if (cnx->msgs) - kfree(cnx->msgs); +static void veth_destroy_connection(u8 rlp) +{ + struct veth_lpar_connection *cnx = veth_cnx[rlp]; + + if (! cnx) + return; + + kfree(cnx->msgs); + kfree(cnx); + veth_cnx[rlp] = NULL; } /* @@ -747,60 +756,41 @@ static void veth_set_multicast_list(struct net_device *dev) write_unlock_irqrestore(&port->mcast_gate, flags); } -static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { -#ifdef SIOCETHTOOL - struct ethtool_cmd ecmd; - - if (cmd != SIOCETHTOOL) - return -EOPNOTSUPP; - if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) - return -EFAULT; - switch (ecmd.cmd) { - case ETHTOOL_GSET: - ecmd.supported = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - ecmd.advertising = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - - ecmd.port = PORT_FIBRE; - ecmd.transceiver = XCVR_INTERNAL; - ecmd.phy_address = 0; - ecmd.speed = SPEED_1000; - ecmd.duplex = DUPLEX_FULL; - ecmd.autoneg = AUTONEG_ENABLE; - ecmd.maxtxpkt = 120; - ecmd.maxrxpkt = 120; - if (copy_to_user(ifr->ifr_data, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - - case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strncpy(info.driver, "veth", sizeof(info.driver) - 1); - info.driver[sizeof(info.driver) - 1] = '\0'; - strncpy(info.version, "1.0", sizeof(info.version) - 1); - if (copy_to_user(ifr->ifr_data, &info, sizeof(info))) - return -EFAULT; - return 0; - } - /* get link status */ - case ETHTOOL_GLINK:{ - struct ethtool_value edata = { ETHTOOL_GLINK }; - edata.data = 1; - if (copy_to_user(ifr->ifr_data, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } + strncpy(info->driver, "veth", sizeof(info->driver) - 1); + info->driver[sizeof(info->driver) - 1] = '\0'; + strncpy(info->version, "1.0", sizeof(info->version) - 1); +} - default: - break; - } +static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + ecmd->supported = (SUPPORTED_1000baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_FIBRE); + ecmd->advertising = (SUPPORTED_1000baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_FIBRE); + ecmd->port = PORT_FIBRE; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = 0; + ecmd->speed = SPEED_1000; + ecmd->duplex = DUPLEX_FULL; + ecmd->autoneg = AUTONEG_ENABLE; + ecmd->maxtxpkt = 120; + ecmd->maxrxpkt = 120; + return 0; +} -#endif - return -EOPNOTSUPP; +static u32 veth_get_link(struct net_device *dev) +{ + return 1; } +static struct ethtool_ops ops = { + .get_drvinfo = veth_get_drvinfo, + .get_settings = veth_get_settings, + .get_link = veth_get_link, +}; + static void veth_tx_timeout(struct net_device *dev) { struct veth_port *port = (struct veth_port *)dev->priv; @@ -843,7 +833,7 @@ static void veth_tx_timeout(struct net_device *dev) spin_unlock_irqrestore(&port->pending_gate, flags); } -struct net_device * __init veth_probe_one(int vlan) +static struct net_device * __init veth_probe_one(int vlan, struct device *vdev) { struct net_device *dev; struct veth_port *port; @@ -869,6 +859,7 @@ struct net_device * __init veth_probe_one(int vlan) if (map & (0x8000 >> vlan)) port->lpar_map |= (1 << i); } + port->dev = vdev; dev->dev_addr[0] = 0x02; dev->dev_addr[1] = 0x01; @@ -888,11 +879,13 @@ struct net_device * __init veth_probe_one(int vlan) dev->change_mtu = veth_change_mtu; dev->set_mac_address = NULL; dev->set_multicast_list = veth_set_multicast_list; - dev->do_ioctl = veth_ioctl; + SET_ETHTOOL_OPS(dev, &ops); dev->watchdog_timeo = 2 * (VETH_ACKTIMEOUT * HZ / 1000000); dev->tx_timeout = veth_tx_timeout; + SET_NETDEV_DEV(dev, vdev); + rc = register_netdev(dev); if (rc != 0) { veth_printk(KERN_ERR, @@ -945,7 +938,7 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, } dma_length = skb->len; - dma_address = vio_map_single(iSeries_veth_dev, skb->data, + dma_address = dma_map_single(port->dev, skb->data, dma_length, DMA_TO_DEVICE); if (dma_mapping_error(dma_address)) @@ -954,6 +947,7 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, /* Is it really necessary to check the length and address * fields of the first entry here? */ msg->skb = skb; + msg->dev = port->dev; msg->data.addr[0] = dma_address; msg->data.len[0] = dma_length; msg->data.eofmask = 1 << VETH_EOF_SHIFT; @@ -1059,7 +1053,7 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx, dma_address = msg->data.addr[0]; dma_length = msg->data.len[0]; - vio_unmap_single(iSeries_veth_dev, dma_address, dma_length, + dma_unmap_single(msg->dev, dma_address, dma_length, DMA_TO_DEVICE); if (msg->skb) { @@ -1327,6 +1321,58 @@ static void veth_timed_ack(unsigned long ptr) spin_unlock_irqrestore(&cnx->lock, flags); } +static int veth_remove(struct vio_dev *vdev) +{ + int i = vdev->unit_address; + struct net_device *dev; + + dev = veth_dev[i]; + if (dev != NULL) { + veth_dev[i] = NULL; + unregister_netdev(dev); + free_netdev(dev); + } + return 0; +} + +static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) +{ + int i = vdev->unit_address; + struct net_device *dev; + + dev = veth_probe_one(i, &vdev->dev); + if (dev == NULL) { + veth_remove(vdev); + return 1; + } + veth_dev[i] = dev; + + /* Start the state machine on each connection, to commence + * link negotiation */ + for (i = 0; i < HVMAXARCHITECTEDLPS; i++) + if (veth_cnx[i]) + veth_kick_statemachine(veth_cnx[i]); + + return 0; +} + +/** + * veth_device_table: Used by vio.c to match devices that we + * support. + */ +static struct vio_device_id veth_device_table[] __devinitdata = { + { "vlan", "" }, + { NULL, NULL } +}; +MODULE_DEVICE_TABLE(vio, veth_device_table); + +static struct vio_driver veth_driver = { + .name = "iseries_veth", + .id_table = veth_device_table, + .probe = veth_probe, + .remove = veth_remove +}; + /* * Module initialization/cleanup */ @@ -1335,27 +1381,26 @@ void __exit veth_module_cleanup(void) { int i; + vio_unregister_driver(&veth_driver); + for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) - veth_destroy_connection(i); + veth_stop_connection(i); HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; ++i) { - struct net_device *dev = veth_dev[i]; + /* Hypervisor callbacks may have scheduled more work while we + * were destroying connections. Now that we've disconnected from + * the hypervisor make sure everything's finished. */ + flush_scheduled_work(); - if (! dev) - continue; + for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) + veth_destroy_connection(i); - veth_dev[i] = NULL; - unregister_netdev(dev); - free_netdev(dev); - } } module_exit(veth_module_cleanup); int __init veth_module_init(void) { - HvLpIndexMap vlan_map = HvLpConfig_getVirtualLanIndexMap(); int i; int rc; @@ -1369,31 +1414,9 @@ int __init veth_module_init(void) } } - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; ++i) { - struct net_device *dev; - - if (! (vlan_map & (0x8000 >> i))) - continue; - - dev = veth_probe_one(i); - - if (! dev) { - veth_module_cleanup(); - return rc; - } - - veth_dev[i] = dev; - } - HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, &veth_handle_event); - /* Start the state machine on each connection, to commence - * link negotiation */ - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) - if (veth_cnx[i]) - veth_kick_statemachine(veth_cnx[i]); - - return 0; + return vio_register_driver(&veth_driver); } module_init(veth_module_init);