net_kern.c

来自「linux 内核源代码」· C语言 代码 · 共 925 行 · 第 1/2 页

C
925
字号
/* * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and * James Leu (jleu@mindspring.net). * Copyright (C) 2001 by various other people who didn't put their name here. * Licensed under the GPL. */#include <linux/bootmem.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/inetdevice.h>#include <linux/init.h>#include <linux/list.h>#include <linux/netdevice.h>#include <linux/platform_device.h>#include <linux/rtnetlink.h>#include <linux/skbuff.h>#include <linux/spinlock.h>#include "init.h"#include "irq_kern.h"#include "irq_user.h"#include "mconsole_kern.h"#include "net_kern.h"#include "net_user.h"static inline void set_ether_mac(struct net_device *dev, unsigned char *addr){	memcpy(dev->dev_addr, addr, ETH_ALEN);}#define DRIVER_NAME "uml-netdev"static DEFINE_SPINLOCK(opened_lock);static LIST_HEAD(opened);/* * The drop_skb is used when we can't allocate an skb.  The * packet is read into drop_skb in order to get the data off the * connection to the host. * It is reallocated whenever a maximum packet size is seen which is * larger than any seen before.  update_drop_skb is called from * eth_configure when a new interface is added. */static DEFINE_SPINLOCK(drop_lock);static struct sk_buff *drop_skb;static int drop_max;static int update_drop_skb(int max){	struct sk_buff *new;	unsigned long flags;	int err = 0;	spin_lock_irqsave(&drop_lock, flags);	if (max <= drop_max)		goto out;	err = -ENOMEM;	new = dev_alloc_skb(max);	if (new == NULL)		goto out;	skb_put(new, max);	kfree_skb(drop_skb);	drop_skb = new;	drop_max = max;	err = 0;out:	spin_unlock_irqrestore(&drop_lock, flags);	return err;}static int uml_net_rx(struct net_device *dev){	struct uml_net_private *lp = dev->priv;	int pkt_len;	struct sk_buff *skb;	/* If we can't allocate memory, try again next round. */	skb = dev_alloc_skb(lp->max_packet);	if (skb == NULL) {		drop_skb->dev = dev;		/* Read a packet into drop_skb and don't do anything with it. */		(*lp->read)(lp->fd, drop_skb, lp);		lp->stats.rx_dropped++;		return 0;	}	skb->dev = dev;	skb_put(skb, lp->max_packet);	skb_reset_mac_header(skb);	pkt_len = (*lp->read)(lp->fd, skb, lp);	if (pkt_len > 0) {		skb_trim(skb, pkt_len);		skb->protocol = (*lp->protocol)(skb);		lp->stats.rx_bytes += skb->len;		lp->stats.rx_packets++;		netif_rx(skb);		return pkt_len;	}	kfree_skb(skb);	return pkt_len;}static void uml_dev_close(struct work_struct *work){	struct uml_net_private *lp =		container_of(work, struct uml_net_private, work);	dev_close(lp->dev);}irqreturn_t uml_net_interrupt(int irq, void *dev_id){	struct net_device *dev = dev_id;	struct uml_net_private *lp = dev->priv;	int err;	if (!netif_running(dev))		return IRQ_NONE;	spin_lock(&lp->lock);	while ((err = uml_net_rx(dev)) > 0) ;	if (err < 0) {		printk(KERN_ERR		       "Device '%s' read returned %d, shutting it down\n",		       dev->name, err);		/* dev_close can't be called in interrupt context, and takes		 * again lp->lock.		 * And dev_close() can be safely called multiple times on the		 * same device, since it tests for (dev->flags & IFF_UP). So		 * there's no harm in delaying the device shutdown.		 * Furthermore, the workqueue will not re-enqueue an already		 * enqueued work item. */		schedule_work(&lp->work);		goto out;	}	reactivate_fd(lp->fd, UM_ETH_IRQ);out:	spin_unlock(&lp->lock);	return IRQ_HANDLED;}static int uml_net_open(struct net_device *dev){	struct uml_net_private *lp = dev->priv;	int err;	if (lp->fd >= 0) {		err = -ENXIO;		goto out;	}	lp->fd = (*lp->open)(&lp->user);	if (lp->fd < 0) {		err = lp->fd;		goto out;	}	err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,			     IRQF_DISABLED | IRQF_SHARED, dev->name, dev);	if (err != 0) {		printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);		err = -ENETUNREACH;		goto out_close;	}	lp->tl.data = (unsigned long) &lp->user;	netif_start_queue(dev);	/* clear buffer - it can happen that the host side of the interface	 * is full when we get here.  In this case, new data is never queued,	 * SIGIOs never arrive, and the net never works.	 */	while ((err = uml_net_rx(dev)) > 0) ;	spin_lock(&opened_lock);	list_add(&lp->list, &opened);	spin_unlock(&opened_lock);	return 0;out_close:	if (lp->close != NULL) (*lp->close)(lp->fd, &lp->user);	lp->fd = -1;out:	return err;}static int uml_net_close(struct net_device *dev){	struct uml_net_private *lp = dev->priv;	netif_stop_queue(dev);	free_irq(dev->irq, dev);	if (lp->close != NULL)		(*lp->close)(lp->fd, &lp->user);	lp->fd = -1;	spin_lock(&opened_lock);	list_del(&lp->list);	spin_unlock(&opened_lock);	return 0;}static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct uml_net_private *lp = dev->priv;	unsigned long flags;	int len;	netif_stop_queue(dev);	spin_lock_irqsave(&lp->lock, flags);	len = (*lp->write)(lp->fd, skb, lp);	if (len == skb->len) {		lp->stats.tx_packets++;		lp->stats.tx_bytes += skb->len;		dev->trans_start = jiffies;		netif_start_queue(dev);		/* this is normally done in the interrupt when tx finishes */		netif_wake_queue(dev);	}	else if (len == 0) {		netif_start_queue(dev);		lp->stats.tx_dropped++;	}	else {		netif_start_queue(dev);		printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len);	}	spin_unlock_irqrestore(&lp->lock, flags);	dev_kfree_skb(skb);	return 0;}static struct net_device_stats *uml_net_get_stats(struct net_device *dev){	struct uml_net_private *lp = dev->priv;	return &lp->stats;}static void uml_net_set_multicast_list(struct net_device *dev){	if (dev->flags & IFF_PROMISC)		return;	else if (dev->mc_count)		dev->flags |= IFF_ALLMULTI;	else dev->flags &= ~IFF_ALLMULTI;}static void uml_net_tx_timeout(struct net_device *dev){	dev->trans_start = jiffies;	netif_wake_queue(dev);}static int uml_net_set_mac(struct net_device *dev, void *addr){	struct uml_net_private *lp = dev->priv;	struct sockaddr *hwaddr = addr;	spin_lock_irq(&lp->lock);	set_ether_mac(dev, hwaddr->sa_data);	spin_unlock_irq(&lp->lock);	return 0;}static int uml_net_change_mtu(struct net_device *dev, int new_mtu){	dev->mtu = new_mtu;	return 0;}static void uml_net_get_drvinfo(struct net_device *dev,				struct ethtool_drvinfo *info){	strcpy(info->driver, DRIVER_NAME);	strcpy(info->version, "42");}static struct ethtool_ops uml_net_ethtool_ops = {	.get_drvinfo	= uml_net_get_drvinfo,	.get_link	= ethtool_op_get_link,};void uml_net_user_timer_expire(unsigned long _conn){#ifdef undef	struct connection *conn = (struct connection *)_conn;	dprintk(KERN_INFO "uml_net_user_timer_expire [%p]\n", conn);	do_connect(conn);#endif}static void setup_etheraddr(char *str, unsigned char *addr, char *name){	char *end;	int i;	if (str == NULL)		goto random;	for (i = 0;i < 6; i++) {		addr[i] = simple_strtoul(str, &end, 16);		if ((end == str) ||		   ((*end != ':') && (*end != ',') && (*end != '\0'))) {			printk(KERN_ERR			       "setup_etheraddr: failed to parse '%s' "			       "as an ethernet address\n", str);			goto random;		}		str = end + 1;	}	if (is_multicast_ether_addr(addr)) {		printk(KERN_ERR		       "Attempt to assign a multicast ethernet address to a "		       "device disallowed\n");		goto random;	}	if (!is_valid_ether_addr(addr)) {		printk(KERN_ERR		       "Attempt to assign an invalid ethernet address to a "		       "device disallowed\n");		goto random;	}	if (!is_local_ether_addr(addr)) {		printk(KERN_WARNING		       "Warning: attempt to assign a globally valid ethernet "		       "address to a device\n");		printk(KERN_WARNING "You should better enable the 2nd "		       "rightmost bit in the first byte of the MAC,\n");		printk(KERN_WARNING "i.e. %02x:%02x:%02x:%02x:%02x:%02x\n",		       addr[0] | 0x02, addr[1], addr[2], addr[3], addr[4],		       addr[5]);		goto random;	}	return;random:	printk(KERN_INFO	       "Choosing a random ethernet address for device %s\n", name);	random_ether_addr(addr);}static DEFINE_SPINLOCK(devices_lock);static LIST_HEAD(devices);static struct platform_driver uml_net_driver = {	.driver = {		.name  = DRIVER_NAME,	},};static int driver_registered;static void net_device_release(struct device *dev){	struct uml_net *device = dev->driver_data;	struct net_device *netdev = device->dev;	struct uml_net_private *lp = netdev->priv;	if (lp->remove != NULL)		(*lp->remove)(&lp->user);	list_del(&device->list);	kfree(device);	free_netdev(netdev);}static void eth_configure(int n, void *init, char *mac,			  struct transport *transport){	struct uml_net *device;	struct net_device *dev;	struct uml_net_private *lp;	int err, size;	size = transport->private_size + sizeof(struct uml_net_private);	device = kzalloc(sizeof(*device), GFP_KERNEL);	if (device == NULL) {		printk(KERN_ERR "eth_configure failed to allocate struct "		       "uml_net\n");		return;	}	dev = alloc_etherdev(size);	if (dev == NULL) {		printk(KERN_ERR "eth_configure: failed to allocate struct "		       "net_device for eth%d\n", n);		goto out_free_device;	}	INIT_LIST_HEAD(&device->list);	device->index = n;	/* If this name ends up conflicting with an existing registered	 * netdevice, that is OK, register_netdev{,ice}() will notice this	 * and fail.	 */	snprintf(dev->name, sizeof(dev->name), "eth%d", n);	setup_etheraddr(mac, device->mac, dev->name);	printk(KERN_INFO "Netdevice %d ", n);	printk("(%02x:%02x:%02x:%02x:%02x:%02x) ",	       device->mac[0], device->mac[1],	       device->mac[2], device->mac[3],	       device->mac[4], device->mac[5]);	printk(": ");	lp = dev->priv;	/* This points to the transport private data. It's still clear, but we	 * must memset it to 0 *now*. Let's help the drivers. */	memset(lp, 0, size);	INIT_WORK(&lp->work, uml_dev_close);	/* sysfs register */	if (!driver_registered) {		platform_driver_register(&uml_net_driver);		driver_registered = 1;	}	device->pdev.id = n;	device->pdev.name = DRIVER_NAME;	device->pdev.dev.release = net_device_release;	device->pdev.dev.driver_data = device;	if (platform_device_register(&device->pdev))		goto out_free_netdev;	SET_NETDEV_DEV(dev,&device->pdev.dev);	device->dev = dev;	/*	 * These just fill in a data structure, so there's no failure	 * to be worried about.	 */	(*transport->kern->init)(dev, init);	*lp = ((struct uml_net_private)		{ .list  		= LIST_HEAD_INIT(lp->list),		  .dev 			= dev,		  .fd 			= -1,		  .mac 			= { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0},		  .max_packet		= transport->user->max_packet,		  .protocol 		= transport->kern->protocol,		  .open 		= transport->user->open,		  .close 		= transport->user->close,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?