sunvnet.c
来自「linux 内核源代码」· C语言 代码 · 共 1,295 行 · 第 1/2 页
C
1,295 行
unsigned long flags; unsigned int len; void *tx_buf; int i, err; if (unlikely(!port)) goto out_dropped; spin_lock_irqsave(&port->vio.lock, flags); dr = &port->vio.drings[VIO_DRIVER_TX_RING]; if (unlikely(vnet_tx_dring_avail(dr) < 2)) { if (!netif_queue_stopped(dev)) { netif_stop_queue(dev); /* This is a hard error, log it. */ printk(KERN_ERR PFX "%s: BUG! Tx Ring full when " "queue awake!\n", dev->name); dev->stats.tx_errors++; } spin_unlock_irqrestore(&port->vio.lock, flags); return NETDEV_TX_BUSY; } d = vio_dring_cur(dr); tx_buf = port->tx_bufs[dr->prod].buf; skb_copy_from_linear_data(skb, tx_buf + VNET_PACKET_SKIP, skb->len); len = skb->len; if (len < ETH_ZLEN) { len = ETH_ZLEN; memset(tx_buf+VNET_PACKET_SKIP+skb->len, 0, len - skb->len); } d->hdr.ack = VIO_ACK_ENABLE; d->size = len; d->ncookies = port->tx_bufs[dr->prod].ncookies; for (i = 0; i < d->ncookies; i++) d->cookies[i] = port->tx_bufs[dr->prod].cookies[i]; /* This has to be a non-SMP write barrier because we are writing * to memory which is shared with the peer LDOM. */ wmb(); d->hdr.state = VIO_DESC_READY; err = __vnet_tx_trigger(port); if (unlikely(err < 0)) { printk(KERN_INFO PFX "%s: TX trigger error %d\n", dev->name, err); d->hdr.state = VIO_DESC_FREE; dev->stats.tx_carrier_errors++; goto out_dropped_unlock; } dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1); if (unlikely(vnet_tx_dring_avail(dr) < 2)) { netif_stop_queue(dev); if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr)) netif_wake_queue(dev); } spin_unlock_irqrestore(&port->vio.lock, flags); dev_kfree_skb(skb); dev->trans_start = jiffies; return NETDEV_TX_OK;out_dropped_unlock: spin_unlock_irqrestore(&port->vio.lock, flags);out_dropped: dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK;}static void vnet_tx_timeout(struct net_device *dev){ /* XXX Implement me XXX */}static int vnet_open(struct net_device *dev){ netif_carrier_on(dev); netif_start_queue(dev); return 0;}static int vnet_close(struct net_device *dev){ netif_stop_queue(dev); netif_carrier_off(dev); return 0;}static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr){ struct vnet_mcast_entry *m; for (m = vp->mcast_list; m; m = m->next) { if (!memcmp(m->addr, addr, ETH_ALEN)) return m; } return NULL;}static void __update_mc_list(struct vnet *vp, struct net_device *dev){ struct dev_addr_list *p; for (p = dev->mc_list; p; p = p->next) { struct vnet_mcast_entry *m; m = __vnet_mc_find(vp, p->dmi_addr); if (m) { m->hit = 1; continue; } if (!m) { m = kzalloc(sizeof(*m), GFP_ATOMIC); if (!m) continue; memcpy(m->addr, p->dmi_addr, ETH_ALEN); m->hit = 1; m->next = vp->mcast_list; vp->mcast_list = m; } }}static void __send_mc_list(struct vnet *vp, struct vnet_port *port){ struct vio_net_mcast_info info; struct vnet_mcast_entry *m, **pp; int n_addrs; memset(&info, 0, sizeof(info)); info.tag.type = VIO_TYPE_CTRL; info.tag.stype = VIO_SUBTYPE_INFO; info.tag.stype_env = VNET_MCAST_INFO; info.tag.sid = vio_send_sid(&port->vio); info.set = 1; n_addrs = 0; for (m = vp->mcast_list; m; m = m->next) { if (m->sent) continue; m->sent = 1; memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], m->addr, ETH_ALEN); if (++n_addrs == VNET_NUM_MCAST) { info.count = n_addrs; (void) vio_ldc_send(&port->vio, &info, sizeof(info)); n_addrs = 0; } } if (n_addrs) { info.count = n_addrs; (void) vio_ldc_send(&port->vio, &info, sizeof(info)); } info.set = 0; n_addrs = 0; pp = &vp->mcast_list; while ((m = *pp) != NULL) { if (m->hit) { m->hit = 0; pp = &m->next; continue; } memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], m->addr, ETH_ALEN); if (++n_addrs == VNET_NUM_MCAST) { info.count = n_addrs; (void) vio_ldc_send(&port->vio, &info, sizeof(info)); n_addrs = 0; } *pp = m->next; kfree(m); } if (n_addrs) { info.count = n_addrs; (void) vio_ldc_send(&port->vio, &info, sizeof(info)); }}static void vnet_set_rx_mode(struct net_device *dev){ struct vnet *vp = netdev_priv(dev); struct vnet_port *port; unsigned long flags; spin_lock_irqsave(&vp->lock, flags); if (!list_empty(&vp->port_list)) { port = list_entry(vp->port_list.next, struct vnet_port, list); if (port->switch_port) { __update_mc_list(vp, dev); __send_mc_list(vp, port); } } spin_unlock_irqrestore(&vp->lock, flags);}static int vnet_change_mtu(struct net_device *dev, int new_mtu){ if (new_mtu != ETH_DATA_LEN) return -EINVAL; dev->mtu = new_mtu; return 0;}static int vnet_set_mac_addr(struct net_device *dev, void *p){ return -EINVAL;}static void vnet_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strcpy(info->driver, DRV_MODULE_NAME); strcpy(info->version, DRV_MODULE_VERSION);}static u32 vnet_get_msglevel(struct net_device *dev){ struct vnet *vp = netdev_priv(dev); return vp->msg_enable;}static void vnet_set_msglevel(struct net_device *dev, u32 value){ struct vnet *vp = netdev_priv(dev); vp->msg_enable = value;}static const struct ethtool_ops vnet_ethtool_ops = { .get_drvinfo = vnet_get_drvinfo, .get_msglevel = vnet_get_msglevel, .set_msglevel = vnet_set_msglevel, .get_link = ethtool_op_get_link,};static void vnet_port_free_tx_bufs(struct vnet_port *port){ struct vio_dring_state *dr; int i; dr = &port->vio.drings[VIO_DRIVER_TX_RING]; if (dr->base) { ldc_free_exp_dring(port->vio.lp, dr->base, (dr->entry_size * dr->num_entries), dr->cookies, dr->ncookies); dr->base = NULL; dr->entry_size = 0; dr->num_entries = 0; dr->pending = 0; dr->ncookies = 0; } for (i = 0; i < VNET_TX_RING_SIZE; i++) { void *buf = port->tx_bufs[i].buf; if (!buf) continue; ldc_unmap(port->vio.lp, port->tx_bufs[i].cookies, port->tx_bufs[i].ncookies); kfree(buf); port->tx_bufs[i].buf = NULL; }}static int __devinit vnet_port_alloc_tx_bufs(struct vnet_port *port){ struct vio_dring_state *dr; unsigned long len; int i, err, ncookies; void *dring; for (i = 0; i < VNET_TX_RING_SIZE; i++) { void *buf = kzalloc(ETH_FRAME_LEN + 8, GFP_KERNEL); int map_len = (ETH_FRAME_LEN + 7) & ~7; err = -ENOMEM; if (!buf) { printk(KERN_ERR "TX buffer allocation failure\n"); goto err_out; } err = -EFAULT; if ((unsigned long)buf & (8UL - 1)) { printk(KERN_ERR "TX buffer misaligned\n"); kfree(buf); goto err_out; } err = ldc_map_single(port->vio.lp, buf, map_len, port->tx_bufs[i].cookies, 2, (LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_RW)); if (err < 0) { kfree(buf); goto err_out; } port->tx_bufs[i].buf = buf; port->tx_bufs[i].ncookies = err; } dr = &port->vio.drings[VIO_DRIVER_TX_RING]; len = (VNET_TX_RING_SIZE * (sizeof(struct vio_net_desc) + (sizeof(struct ldc_trans_cookie) * 2))); ncookies = VIO_MAX_RING_COOKIES; dring = ldc_alloc_exp_dring(port->vio.lp, len, dr->cookies, &ncookies, (LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_RW)); if (IS_ERR(dring)) { err = PTR_ERR(dring); goto err_out; } dr->base = dring; dr->entry_size = (sizeof(struct vio_net_desc) + (sizeof(struct ldc_trans_cookie) * 2)); dr->num_entries = VNET_TX_RING_SIZE; dr->prod = dr->cons = 0; dr->pending = VNET_TX_RING_SIZE; dr->ncookies = ncookies; return 0;err_out: vnet_port_free_tx_bufs(port); return err;}static LIST_HEAD(vnet_list);static DEFINE_MUTEX(vnet_list_mutex);static struct vnet * __devinit vnet_new(const u64 *local_mac){ struct net_device *dev; struct vnet *vp; int err, i; dev = alloc_etherdev(sizeof(*vp)); if (!dev) { printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); return ERR_PTR(-ENOMEM); } for (i = 0; i < ETH_ALEN; i++) dev->dev_addr[i] = (*local_mac >> (5 - i) * 8) & 0xff; memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); vp = netdev_priv(dev); spin_lock_init(&vp->lock); vp->dev = dev; INIT_LIST_HEAD(&vp->port_list); for (i = 0; i < VNET_PORT_HASH_SIZE; i++) INIT_HLIST_HEAD(&vp->port_hash[i]); INIT_LIST_HEAD(&vp->list); vp->local_mac = *local_mac; dev->open = vnet_open; dev->stop = vnet_close; dev->set_multicast_list = vnet_set_rx_mode; dev->set_mac_address = vnet_set_mac_addr; dev->tx_timeout = vnet_tx_timeout; dev->ethtool_ops = &vnet_ethtool_ops; dev->watchdog_timeo = VNET_TX_TIMEOUT; dev->change_mtu = vnet_change_mtu; dev->hard_start_xmit = vnet_start_xmit; err = register_netdev(dev); if (err) { printk(KERN_ERR PFX "Cannot register net device, " "aborting.\n"); goto err_out_free_dev; } printk(KERN_INFO "%s: Sun LDOM vnet ", dev->name); for (i = 0; i < 6; i++) printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); list_add(&vp->list, &vnet_list); return vp;err_out_free_dev: free_netdev(dev); return ERR_PTR(err);}static struct vnet * __devinit vnet_find_or_create(const u64 *local_mac){ struct vnet *iter, *vp; mutex_lock(&vnet_list_mutex); vp = NULL; list_for_each_entry(iter, &vnet_list, list) { if (iter->local_mac == *local_mac) { vp = iter; break; } } if (!vp) vp = vnet_new(local_mac); mutex_unlock(&vnet_list_mutex); return vp;}static const char *local_mac_prop = "local-mac-address";static struct vnet * __devinit vnet_find_parent(struct mdesc_handle *hp, u64 port_node){ const u64 *local_mac = NULL; u64 a; mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) { u64 target = mdesc_arc_target(hp, a); const char *name; name = mdesc_get_property(hp, target, "name", NULL); if (!name || strcmp(name, "network")) continue; local_mac = mdesc_get_property(hp, target, local_mac_prop, NULL); if (local_mac) break; } if (!local_mac) return ERR_PTR(-ENODEV); return vnet_find_or_create(local_mac);}static struct ldc_channel_config vnet_ldc_cfg = { .event = vnet_event, .mtu = 64, .mode = LDC_MODE_UNRELIABLE,};static struct vio_driver_ops vnet_vio_ops = { .send_attr = vnet_send_attr, .handle_attr = vnet_handle_attr, .handshake_complete = vnet_handshake_complete,};static void print_version(void){ static int version_printed; if (version_printed++ == 0) printk(KERN_INFO "%s", version);}const char *remote_macaddr_prop = "remote-mac-address";static int __devinit vnet_port_probe(struct vio_dev *vdev, const struct vio_device_id *id){ struct mdesc_handle *hp; struct vnet_port *port; unsigned long flags; struct vnet *vp; const u64 *rmac; int len, i, err, switch_port; print_version(); hp = mdesc_grab(); vp = vnet_find_parent(hp, vdev->mp); if (IS_ERR(vp)) { printk(KERN_ERR PFX "Cannot find port parent vnet.\n"); err = PTR_ERR(vp); goto err_out_put_mdesc; } rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); err = -ENODEV; if (!rmac) { printk(KERN_ERR PFX "Port lacks %s property.\n", remote_macaddr_prop); goto err_out_put_mdesc; } port = kzalloc(sizeof(*port), GFP_KERNEL); err = -ENOMEM; if (!port) { printk(KERN_ERR PFX "Cannot allocate vnet_port.\n"); goto err_out_put_mdesc; } for (i = 0; i < ETH_ALEN; i++) port->raddr[i] = (*rmac >> (5 - i) * 8) & 0xff; port->vp = vp; err = vio_driver_init(&port->vio, vdev, VDEV_NETWORK, vnet_versions, ARRAY_SIZE(vnet_versions), &vnet_vio_ops, vp->dev->name); if (err) goto err_out_free_port; err = vio_ldc_alloc(&port->vio, &vnet_ldc_cfg, port); if (err) goto err_out_free_port; err = vnet_port_alloc_tx_bufs(port); if (err) goto err_out_free_ldc; INIT_HLIST_NODE(&port->hash); INIT_LIST_HEAD(&port->list); switch_port = 0; if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) switch_port = 1; port->switch_port = switch_port; spin_lock_irqsave(&vp->lock, flags); if (switch_port) list_add(&port->list, &vp->port_list); else list_add_tail(&port->list, &vp->port_list); hlist_add_head(&port->hash, &vp->port_hash[vnet_hashfn(port->raddr)]); spin_unlock_irqrestore(&vp->lock, flags); dev_set_drvdata(&vdev->dev, port); printk(KERN_INFO "%s: PORT ( remote-mac ", vp->dev->name); for (i = 0; i < 6; i++) printk("%2.2x%c", port->raddr[i], i == 5 ? ' ' : ':'); if (switch_port) printk("switch-port "); printk(")\n"); vio_port_up(&port->vio); mdesc_release(hp); return 0;err_out_free_ldc: vio_ldc_free(&port->vio);err_out_free_port: kfree(port);err_out_put_mdesc: mdesc_release(hp); return err;}static int vnet_port_remove(struct vio_dev *vdev){ struct vnet_port *port = dev_get_drvdata(&vdev->dev); if (port) { struct vnet *vp = port->vp; unsigned long flags; del_timer_sync(&port->vio.timer); spin_lock_irqsave(&vp->lock, flags); list_del(&port->list); hlist_del(&port->hash); spin_unlock_irqrestore(&vp->lock, flags); vnet_port_free_tx_bufs(port); vio_ldc_free(&port->vio); dev_set_drvdata(&vdev->dev, NULL); kfree(port); } return 0;}static struct vio_device_id vnet_port_match[] = { { .type = "vnet-port", }, {},};MODULE_DEVICE_TABLE(vio, vnet_port_match);static struct vio_driver vnet_port_driver = { .id_table = vnet_port_match, .probe = vnet_port_probe, .remove = vnet_port_remove, .driver = { .name = "vnet_port", .owner = THIS_MODULE, }};static int __init vnet_init(void){ return vio_register_driver(&vnet_port_driver);}static void __exit vnet_exit(void){ vio_unregister_driver(&vnet_port_driver);}module_init(vnet_init);module_exit(vnet_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?