Skip to content
Snippets Groups Projects
dev.c 128 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	WARN_ON(err);
    
    
    	/* Add the device back in the hashes */
    	list_netdevice(dev);
    
    	/* Notify protocols, that a new device appeared. */
    	call_netdevice_notifiers(NETDEV_REGISTER, dev);
    
    	synchronize_net();
    	err = 0;
    out:
    	return err;
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int dev_cpu_callback(struct notifier_block *nfb,
    			    unsigned long action,
    			    void *ocpu)
    {
    	struct sk_buff **list_skb;
    
    	struct Qdisc **list_net;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct sk_buff *skb;
    	unsigned int cpu, oldcpu = (unsigned long)ocpu;
    	struct softnet_data *sd, *oldsd;
    
    
    	if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return NOTIFY_OK;
    
    	local_irq_disable();
    	cpu = smp_processor_id();
    	sd = &per_cpu(softnet_data, cpu);
    	oldsd = &per_cpu(softnet_data, oldcpu);
    
    	/* Find end of our completion_queue. */
    	list_skb = &sd->completion_queue;
    	while (*list_skb)
    		list_skb = &(*list_skb)->next;
    	/* Append completion queue from offline CPU. */
    	*list_skb = oldsd->completion_queue;
    	oldsd->completion_queue = NULL;
    
    	/* Find end of our output_queue. */
    	list_net = &sd->output_queue;
    	while (*list_net)
    		list_net = &(*list_net)->next_sched;
    	/* Append output queue from offline CPU. */
    	*list_net = oldsd->output_queue;
    	oldsd->output_queue = NULL;
    
    	raise_softirq_irqoff(NET_TX_SOFTIRQ);
    	local_irq_enable();
    
    	/* Process offline CPU's input_pkt_queue */
    	while ((skb = __skb_dequeue(&oldsd->input_pkt_queue)))
    		netif_rx(skb);
    
    	return NOTIFY_OK;
    }
    
    
    
     *	netdev_increment_features - increment feature set by one
     *	@all: current feature set
     *	@one: new feature set
     *	@mask: mask feature set
    
     *
     *	Computes a new feature set after adding a device with feature set
    
     *	@one to the master device with current feature set @all.  Will not
     *	enable anything that is off in @mask. Returns the new feature set.
    
    unsigned long netdev_increment_features(unsigned long all, unsigned long one,
    					unsigned long mask)
    {
    	/* If device needs checksumming, downgrade to it. */
            if (all & NETIF_F_NO_CSUM && !(one & NETIF_F_NO_CSUM))
    		all ^= NETIF_F_NO_CSUM | (one & NETIF_F_ALL_CSUM);
    	else if (mask & NETIF_F_ALL_CSUM) {
    		/* If one device supports v4/v6 checksumming, set for all. */
    		if (one & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM) &&
    		    !(all & NETIF_F_GEN_CSUM)) {
    			all &= ~NETIF_F_ALL_CSUM;
    			all |= one & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
    		}
    
    		/* If one device supports hw checksumming, set for all. */
    		if (one & NETIF_F_GEN_CSUM && !(all & NETIF_F_GEN_CSUM)) {
    			all &= ~NETIF_F_ALL_CSUM;
    			all |= NETIF_F_HW_CSUM;
    		}
    	}
    
    	one |= NETIF_F_ALL_CSUM;
    
    	one |= all & NETIF_F_ONE_FOR_ALL;
    	all &= one | NETIF_F_LLTX | NETIF_F_GSO;
    	all |= one & mask & NETIF_F_ONE_FOR_ALL;
    
    EXPORT_SYMBOL(netdev_increment_features);
    
    static struct hlist_head *netdev_create_hash(void)
    {
    	int i;
    	struct hlist_head *hash;
    
    	hash = kmalloc(sizeof(*hash) * NETDEV_HASHENTRIES, GFP_KERNEL);
    	if (hash != NULL)
    		for (i = 0; i < NETDEV_HASHENTRIES; i++)
    			INIT_HLIST_HEAD(&hash[i]);
    
    	return hash;
    }
    
    
    /* Initialize per network namespace state */
    
    static int __net_init netdev_init(struct net *net)
    
    	net->dev_name_head = netdev_create_hash();
    	if (net->dev_name_head == NULL)
    		goto err_name;
    
    	net->dev_index_head = netdev_create_hash();
    	if (net->dev_index_head == NULL)
    		goto err_idx;
    
    
    err_idx:
    	kfree(net->dev_name_head);
    err_name:
    	return -ENOMEM;
    
    /**
     *	netdev_drivername - network driver for the device
     *	@dev: network device
     *	@buffer: buffer for resulting name
     *	@len: size of buffer
     *
     *	Determine network driver for device.
     */
    
    char *netdev_drivername(const struct net_device *dev, char *buffer, int len)
    
    	const struct device_driver *driver;
    	const struct device *parent;
    
    
    	if (len <= 0 || !buffer)
    		return buffer;
    	buffer[0] = 0;
    
    	parent = dev->dev.parent;
    
    	if (!parent)
    		return buffer;
    
    	driver = parent->driver;
    	if (driver && driver->name)
    		strlcpy(buffer, driver->name, len);
    	return buffer;
    }
    
    
    static void __net_exit netdev_exit(struct net *net)
    
    {
    	kfree(net->dev_name_head);
    	kfree(net->dev_index_head);
    }
    
    
    static struct pernet_operations __net_initdata netdev_net_ops = {
    
    static void __net_exit default_device_exit(struct net *net)
    
    	/*
    	 * Push all migratable of the network devices back to the
    	 * initial network namespace
    	 */
    	rtnl_lock();
    
    
    		/* Ignore unmoveable devices (i.e. loopback) */
    		if (dev->features & NETIF_F_NETNS_LOCAL)
    			continue;
    
    
    		/* Delete virtual devices */
    		if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink) {
    			dev->rtnl_link_ops->dellink(dev);
    
    		/* Push remaing network devices to init_net */
    
    		snprintf(fb_name, IFNAMSIZ, "dev%d", dev->ifindex);
    		err = dev_change_net_namespace(dev, &init_net, fb_name);
    
    			printk(KERN_EMERG "%s: failed to move %s to init_net: %d\n",
    
    static struct pernet_operations __net_initdata default_device_ops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     *	Initialize the DEV module. At boot time this walks the device list and
     *	unhooks any devices that fail to initialise (normally hardware not
     *	present) and leaves us with a valid list of present and active devices.
     *
     */
    
    /*
     *       This is called single threaded during boot, so no need
     *       to take the rtnl semaphore.
     */
    static int __init net_dev_init(void)
    {
    	int i, rc = -ENOMEM;
    
    	BUG_ON(!dev_boot_phase);
    
    	if (dev_proc_init())
    		goto out;
    
    
    	if (netdev_kobject_init())
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out;
    
    	INIT_LIST_HEAD(&ptype_all);
    
    	for (i = 0; i < PTYPE_HASH_SIZE; i++)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		INIT_LIST_HEAD(&ptype_base[i]);
    
    
    	if (register_pernet_subsys(&netdev_net_ops))
    		goto out;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 *	Initialise the packet receive queues.
    	 */
    
    
    	for_each_possible_cpu(i) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		struct softnet_data *queue;
    
    		queue = &per_cpu(softnet_data, i);
    		skb_queue_head_init(&queue->input_pkt_queue);
    		queue->completion_queue = NULL;
    		INIT_LIST_HEAD(&queue->poll_list);
    
    
    		queue->backlog.poll = process_backlog;
    		queue->backlog.weight = weight_p;
    
    		queue->backlog.gro_list = NULL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	dev_boot_phase = 0;
    
    
    	/* The loopback device is special if any other network devices
    	 * is present in a network namespace the loopback device must
    	 * be present. Since we now dynamically allocate and free the
    	 * loopback device ensure this invariant is maintained by
    	 * keeping the loopback device as the first device on the
    	 * list of network devices.  Ensuring the loopback devices
    	 * is the first device that appears and the last network device
    	 * that disappears.
    	 */
    	if (register_pernet_device(&loopback_net_ops))
    		goto out;
    
    	if (register_pernet_device(&default_device_ops))
    		goto out;
    
    
    	open_softirq(NET_TX_SOFTIRQ, net_tx_action);
    	open_softirq(NET_RX_SOFTIRQ, net_rx_action);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	hotcpu_notifier(dev_cpu_callback, 0);
    	dst_init();
    	dev_mcast_init();
    	rc = 0;
    out:
    	return rc;
    }
    
    subsys_initcall(net_dev_init);
    
    EXPORT_SYMBOL(__dev_get_by_index);
    EXPORT_SYMBOL(__dev_get_by_name);
    EXPORT_SYMBOL(__dev_remove_pack);
    
    EXPORT_SYMBOL(dev_valid_name);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    EXPORT_SYMBOL(dev_add_pack);
    EXPORT_SYMBOL(dev_alloc_name);
    EXPORT_SYMBOL(dev_close);
    EXPORT_SYMBOL(dev_get_by_flags);
    EXPORT_SYMBOL(dev_get_by_index);
    EXPORT_SYMBOL(dev_get_by_name);
    EXPORT_SYMBOL(dev_open);
    EXPORT_SYMBOL(dev_queue_xmit);
    EXPORT_SYMBOL(dev_remove_pack);
    EXPORT_SYMBOL(dev_set_allmulti);
    EXPORT_SYMBOL(dev_set_promiscuity);
    EXPORT_SYMBOL(dev_change_flags);
    EXPORT_SYMBOL(dev_set_mtu);
    EXPORT_SYMBOL(dev_set_mac_address);
    EXPORT_SYMBOL(free_netdev);
    EXPORT_SYMBOL(netdev_boot_setup_check);
    EXPORT_SYMBOL(netdev_set_master);
    EXPORT_SYMBOL(netdev_state_change);
    EXPORT_SYMBOL(netif_receive_skb);
    EXPORT_SYMBOL(netif_rx);
    EXPORT_SYMBOL(register_gifconf);
    EXPORT_SYMBOL(register_netdevice);
    EXPORT_SYMBOL(register_netdevice_notifier);
    EXPORT_SYMBOL(skb_checksum_help);
    EXPORT_SYMBOL(synchronize_net);
    EXPORT_SYMBOL(unregister_netdevice);
    EXPORT_SYMBOL(unregister_netdevice_notifier);
    EXPORT_SYMBOL(net_enable_timestamp);
    EXPORT_SYMBOL(net_disable_timestamp);
    EXPORT_SYMBOL(dev_get_flags);
    
    #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
    EXPORT_SYMBOL(br_handle_frame_hook);
    EXPORT_SYMBOL(br_fdb_get_hook);
    EXPORT_SYMBOL(br_fdb_put_hook);
    #endif
    
    EXPORT_SYMBOL(dev_load);
    
    EXPORT_PER_CPU_SYMBOL(softnet_data);