Skip to content
Snippets Groups Projects
dev.c 160 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    /**
     *	free_netdev - free network device
     *	@dev: device
     *
    
     *	This function does the last stage of destroying an allocated device
     * 	interface. The reference to the device object is released.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *	If this is the last reference then it will be freed.
     */
    void free_netdev(struct net_device *dev)
    {
    
    	struct napi_struct *p, *n;
    
    
    	kfree(dev->_tx);
    
    #ifdef CONFIG_RPS
    	kfree(dev->_rx);
    #endif
    
    	kfree(rcu_dereference_protected(dev->ingress_queue, 1));
    
    	/* Flush device addresses */
    	dev_addr_flush(dev);
    
    
    	list_for_each_entry_safe(p, n, &dev->napi_list, dev_list)
    		netif_napi_del(p);
    
    
    	free_percpu(dev->pcpu_refcnt);
    	dev->pcpu_refcnt = NULL;
    
    
    	/*  Compatibility with error handling in drivers */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (dev->reg_state == NETREG_UNINITIALIZED) {
    		kfree((char *)dev - dev->padded);
    		return;
    	}
    
    	BUG_ON(dev->reg_state != NETREG_UNREGISTERED);
    	dev->reg_state = NETREG_RELEASED;
    
    
    	/* will free via device release */
    	put_device(&dev->dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(free_netdev);
    
    /**
     *	synchronize_net -  Synchronize with packet receive processing
     *
     *	Wait for packets currently being received to be done.
     *	Does not block later packets from starting.
     */
    
    void synchronize_net(void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	might_sleep();
    
    	if (rtnl_is_locked())
    		synchronize_rcu_expedited();
    	else
    		synchronize_rcu();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(synchronize_net);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /**
    
     *	unregister_netdevice_queue - remove device from the kernel
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *	@dev: device
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *	This function shuts down a device interface and removes it
    
     *	from the kernel tables.
    
     *	If head not NULL, device is queued to be unregistered later.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *
     *	Callers must hold the rtnl semaphore.  You may want
     *	unregister_netdev() instead of this.
     */
    
    
    void unregister_netdevice_queue(struct net_device *dev, struct list_head *head)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    		list_move_tail(&dev->unreg_list, head);
    
    	} else {
    		rollback_registered(dev);
    		/* Finish processing unregister after unlock */
    		net_set_todo(dev);
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(unregister_netdevice_queue);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /**
     *	unregister_netdevice_many - unregister many devices
     *	@head: list of devices
     */
    void unregister_netdevice_many(struct list_head *head)
    {
    	struct net_device *dev;
    
    	if (!list_empty(head)) {
    		rollback_registered_many(head);
    		list_for_each_entry(dev, head, unreg_list)
    			net_set_todo(dev);
    	}
    }
    
    EXPORT_SYMBOL(unregister_netdevice_many);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /**
     *	unregister_netdev - remove device from the kernel
     *	@dev: device
     *
     *	This function shuts down a device interface and removes it
    
     *	from the kernel tables.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *
     *	This is just a wrapper for unregister_netdevice that takes
     *	the rtnl semaphore.  In general you want to use this and not
     *	unregister_netdevice.
     */
    void unregister_netdev(struct net_device *dev)
    {
    	rtnl_lock();
    	unregister_netdevice(dev);
    	rtnl_unlock();
    }
    EXPORT_SYMBOL(unregister_netdev);
    
    
    /**
     *	dev_change_net_namespace - move device to different nethost namespace
     *	@dev: device
     *	@net: network namespace
     *	@pat: If not NULL name pattern to try if the current device name
     *	      is already taken in the destination network namespace.
     *
     *	This function shuts down a device interface and moves it
     *	to a new network namespace. On success 0 is returned, on
     *	a failure a netagive errno code is returned.
     *
     *	Callers must hold the rtnl semaphore.
     */
    
    int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
    {
    	int err;
    
    	ASSERT_RTNL();
    
    	/* Don't allow namespace local devices to be moved. */
    	err = -EINVAL;
    	if (dev->features & NETIF_F_NETNS_LOCAL)
    		goto out;
    
    	/* Ensure the device has been registrered */
    	err = -EINVAL;
    	if (dev->reg_state != NETREG_REGISTERED)
    		goto out;
    
    	/* Get out if there is nothing todo */
    	err = 0;
    
    		goto out;
    
    	/* Pick the destination device name, and ensure
    	 * we can use it in the destination network namespace.
    	 */
    	err = -EEXIST;
    
    	if (__dev_get_by_name(net, dev->name)) {
    
    		/* We get here if we can't use the current device name */
    		if (!pat)
    			goto out;
    
    		if (dev_get_valid_name(dev, pat) < 0)
    
    			goto out;
    	}
    
    	/*
    	 * And now a mini version of register_netdevice unregister_netdevice.
    	 */
    
    	/* If device is running close it first. */
    
    
    	/* And unlink it from device chain */
    	err = -ENODEV;
    	unlist_netdevice(dev);
    
    	synchronize_net();
    
    	/* Shutdown queueing discipline. */
    	dev_shutdown(dev);
    
    	/* Notify protocols, that we are about to destroy
    	   this device. They should clean all the things.
    
    
    	   Note that dev->reg_state stays at NETREG_REGISTERED.
    	   This is wanted because this way 8021q and macvlan know
    	   the device is just moving and can keep their slaves up.
    
    	*/
    	call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
    
    	call_netdevice_notifiers(NETDEV_UNREGISTER_BATCH, dev);
    
    
    	/*
    	 *	Flush the unicast and multicast chains
    	 */
    
    	dev_mc_flush(dev);
    
    
    	/* Actually switch the network namespace */
    
    
    	/* If there is an ifindex conflict assign a new one */
    	if (__dev_get_by_index(net, dev->ifindex)) {
    		int iflink = (dev->iflink == dev->ifindex);
    		dev->ifindex = dev_new_index(net);
    		if (iflink)
    			dev->iflink = dev->ifindex;
    	}
    
    
    	/* Fixup kobjects */
    
    	err = device_rename(&dev->dev, dev->name);
    
    	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);
    
    
    	/*
    	 *	Prevent userspace races by waiting until the network
    	 *	device is fully setup before sending notifications.
    	 */
    	rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
    
    
    EXPORT_SYMBOL_GPL(dev_change_net_namespace);
    
    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 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;
    
    	/* Append output queue from offline CPU. */
    
    	if (oldsd->output_queue) {
    		*sd->output_queue_tailp = oldsd->output_queue;
    		sd->output_queue_tailp = oldsd->output_queue_tailp;
    		oldsd->output_queue = NULL;
    		oldsd->output_queue_tailp = &oldsd->output_queue;
    	}
    
    	/* Append NAPI poll list from offline CPU. */
    	if (!list_empty(&oldsd->poll_list)) {
    		list_splice_init(&oldsd->poll_list, &sd->poll_list);
    		raise_softirq_irqoff(NET_RX_SOFTIRQ);
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	raise_softirq_irqoff(NET_TX_SOFTIRQ);
    	local_irq_enable();
    
    	/* Process offline CPU's input_pkt_queue */
    
    	while ((skb = __skb_dequeue(&oldsd->process_queue))) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		netif_rx(skb);
    
    		input_queue_head_incr(oldsd);
    
    	while ((skb = __skb_dequeue(&oldsd->input_pkt_queue))) {
    
    		input_queue_head_incr(oldsd);
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	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.
    
    netdev_features_t netdev_increment_features(netdev_features_t all,
    	netdev_features_t one, netdev_features_t mask)
    
    	if (mask & NETIF_F_GEN_CSUM)
    		mask |= NETIF_F_ALL_CSUM;
    	mask |= NETIF_F_VLAN_CHALLENGED;
    
    	all |= one & (NETIF_F_ONE_FOR_ALL|NETIF_F_ALL_CSUM) & mask;
    	all &= one | ~NETIF_F_ALL_FOR_ALL;
    
    	/* If one device supports hw checksumming, set for all. */
    	if (all & NETIF_F_GEN_CSUM)
    		all &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
    
    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
     *
     *	Determine network driver for device.
     */
    
    const char *netdev_drivername(const struct net_device *dev)
    
    	const struct device_driver *driver;
    	const struct device *parent;
    
    	const char *empty = "";
    
    
    	parent = dev->dev.parent;
    	if (!parent)
    
    
    	driver = parent->driver;
    	if (driver && driver->name)
    
    		return driver->name;
    	return empty;
    
    int __netdev_printk(const char *level, const struct net_device *dev,
    
    			   struct va_format *vaf)
    {
    	int r;
    
    	if (dev && dev->dev.parent)
    		r = dev_printk(level, dev->dev.parent, "%s: %pV",
    			       netdev_name(dev), vaf);
    	else if (dev)
    		r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
    	else
    		r = printk("%s(NULL net_device): %pV", level, vaf);
    
    	return r;
    }
    
    EXPORT_SYMBOL(__netdev_printk);
    
    
    int netdev_printk(const char *level, const struct net_device *dev,
    		  const char *format, ...)
    {
    	struct va_format vaf;
    	va_list args;
    	int r;
    
    	va_start(args, format);
    
    	vaf.fmt = format;
    	vaf.va = &args;
    
    	r = __netdev_printk(level, dev, &vaf);
    	va_end(args);
    
    	return r;
    }
    EXPORT_SYMBOL(netdev_printk);
    
    #define define_netdev_printk_level(func, level)			\
    int func(const struct net_device *dev, const char *fmt, ...)	\
    {								\
    	int r;							\
    	struct va_format vaf;					\
    	va_list args;						\
    								\
    	va_start(args, fmt);					\
    								\
    	vaf.fmt = fmt;						\
    	vaf.va = &args;						\
    								\
    	r = __netdev_printk(level, dev, &vaf);			\
    	va_end(args);						\
    								\
    	return r;						\
    }								\
    EXPORT_SYMBOL(func);
    
    define_netdev_printk_level(netdev_emerg, KERN_EMERG);
    define_netdev_printk_level(netdev_alert, KERN_ALERT);
    define_netdev_printk_level(netdev_crit, KERN_CRIT);
    define_netdev_printk_level(netdev_err, KERN_ERR);
    define_netdev_printk_level(netdev_warn, KERN_WARNING);
    define_netdev_printk_level(netdev_notice, KERN_NOTICE);
    define_netdev_printk_level(netdev_info, KERN_INFO);
    
    
    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)
    
    	struct net_device *dev, *aux;
    
    	 * Push all migratable network devices back to the
    
    	 * initial network namespace
    	 */
    	rtnl_lock();
    
    	for_each_netdev_safe(net, dev, aux) {
    
    
    		/* Ignore unmoveable devices (i.e. loopback) */
    		if (dev->features & NETIF_F_NETNS_LOCAL)
    			continue;
    
    
    		/* Leave virtual devices for the generic cleanup */
    		if (dev->rtnl_link_ops)
    			continue;
    
    Lucas De Marchi's avatar
    Lucas De Marchi committed
    		/* Push remaining 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 void __net_exit default_device_exit_batch(struct list_head *net_list)
    {
    	/* At exit all network devices most be removed from a network
    
    	 * namespace.  Do this in the reverse order of registration.
    
    	 * Do this across as many network namespaces as possible to
    	 * improve batching efficiency.
    	 */
    	struct net_device *dev;
    	struct net *net;
    	LIST_HEAD(dev_kill_list);
    
    	rtnl_lock();
    	list_for_each_entry(net, net_list, exit_list) {
    		for_each_netdev_reverse(net, dev) {
    			if (dev->rtnl_link_ops)
    				dev->rtnl_link_ops->dellink(dev, &dev_kill_list);
    			else
    				unregister_netdevice_queue(dev, &dev_kill_list);
    		}
    	}
    	unregister_netdevice_many(&dev_kill_list);
    
    	list_del(&dev_kill_list);
    
    static struct pernet_operations __net_initdata default_device_ops = {
    
    	.exit_batch = default_device_exit_batch,
    
    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) {
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    		struct softnet_data *sd = &per_cpu(softnet_data, i);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Changli Gao's avatar
    Changli Gao committed
    		memset(sd, 0, sizeof(*sd));
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    		skb_queue_head_init(&sd->input_pkt_queue);
    
    		skb_queue_head_init(&sd->process_queue);
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    		sd->completion_queue = NULL;
    		INIT_LIST_HEAD(&sd->poll_list);
    
    		sd->output_queue = NULL;
    		sd->output_queue_tailp = &sd->output_queue;
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    #ifdef CONFIG_RPS
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    		sd->csd.func = rps_trigger_softirq;
    		sd->csd.info = sd;
    		sd->csd.flags = 0;
    		sd->cpu = i;
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    		sd->backlog.poll = process_backlog;
    		sd->backlog.weight = weight_p;
    		sd->backlog.gro_list = NULL;
    		sd->backlog.gro_count = 0;
    
    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);
    
    
    static int __init initialize_hashrnd(void)
    {
    
    	get_random_bytes(&hashrnd, sizeof(hashrnd));
    
    	return 0;
    }
    
    late_initcall_sync(initialize_hashrnd);