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