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 + -
显示快捷键?