Skip to content
Snippets Groups Projects
dev.c 154 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /**
     * __napi_schedule - schedule for receive
    
     * @n: entry to schedule
    
     *
     * The entry's receive function will be scheduled to run
     */
    
    void __napi_schedule(struct napi_struct *n)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    	____napi_schedule(&__get_cpu_var(softnet_data), n);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    void __napi_complete(struct napi_struct *n)
    {
    	BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
    	BUG_ON(n->gro_list);
    
    	list_del(&n->poll_list);
    	smp_mb__before_clear_bit();
    	clear_bit(NAPI_STATE_SCHED, &n->state);
    }
    EXPORT_SYMBOL(__napi_complete);
    
    void napi_complete(struct napi_struct *n)
    {
    	unsigned long flags;
    
    	/*
    	 * don't let napi dequeue from the cpu poll list
    	 * just in case its running on a different cpu
    	 */
    	if (unlikely(test_bit(NAPI_STATE_NPSVC, &n->state)))
    		return;
    
    
    	napi_gro_flush(n, false);
    
    	local_irq_save(flags);
    	__napi_complete(n);
    	local_irq_restore(flags);
    }
    EXPORT_SYMBOL(napi_complete);
    
    void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
    		    int (*poll)(struct napi_struct *, int), int weight)
    {
    	INIT_LIST_HEAD(&napi->poll_list);
    
    	napi->gro_list = NULL;
    
    Herbert Xu's avatar
    Herbert Xu committed
    	napi->skb = NULL;
    
    	napi->poll = poll;
    	napi->weight = weight;
    	list_add(&napi->dev_list, &dev->napi_list);
    	napi->dev = dev;
    
    Herbert Xu's avatar
    Herbert Xu committed
    #ifdef CONFIG_NETPOLL
    
    	spin_lock_init(&napi->poll_lock);
    	napi->poll_owner = -1;
    #endif
    	set_bit(NAPI_STATE_SCHED, &napi->state);
    }
    EXPORT_SYMBOL(netif_napi_add);
    
    void netif_napi_del(struct napi_struct *napi)
    {
    	struct sk_buff *skb, *next;
    
    
    	list_del_init(&napi->dev_list);
    
    	napi_free_frags(napi);
    
    
    	for (skb = napi->gro_list; skb; skb = next) {
    		next = skb->next;
    		skb->next = NULL;
    		kfree_skb(skb);
    	}
    
    	napi->gro_list = NULL;
    
    }
    EXPORT_SYMBOL(netif_napi_del);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static void net_rx_action(struct softirq_action *h)
    {
    
    	struct softnet_data *sd = &__get_cpu_var(softnet_data);
    
    	unsigned long time_limit = jiffies + 2;
    
    	int budget = netdev_budget;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	local_irq_disable();
    
    
    	while (!list_empty(&sd->poll_list)) {
    
    		struct napi_struct *n;
    		int work, weight;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    		/* If softirq window is exhuasted then punt.
    
    		 * Allow this to run for 2 jiffies since which will allow
    		 * an average latency of 1.5/HZ.
    
    		if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			goto softnet_break;
    
    		local_irq_enable();
    
    
    		/* Even though interrupts have been re-enabled, this
    		 * access is safe because interrupts can only add new
    		 * entries to the tail of this list, and only ->poll()
    		 * calls can remove this head entry from the list.
    		 */
    
    		n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    		/* This NAPI_STATE_SCHED test is for avoiding a race
    		 * with netpoll's poll_napi().  Only the entity which
    		 * obtains the lock and sees NAPI_STATE_SCHED set will
    		 * actually make the ->poll() call.  Therefore we avoid
    
    Lucas De Marchi's avatar
    Lucas De Marchi committed
    		 * accidentally calling ->poll() when NAPI is not scheduled.
    
    		if (test_bit(NAPI_STATE_SCHED, &n->state)) {
    
    			work = n->poll(n, weight);
    
    
    		WARN_ON_ONCE(work > weight);
    
    		budget -= work;
    
    		local_irq_disable();
    
    		/* Drivers must not modify the NAPI state if they
    		 * consume the entire weight.  In such cases this code
    		 * still "owns" the NAPI instance and therefore can
    		 * move the instance around on the list at-will.
    		 */
    
    		if (unlikely(work == weight)) {
    
    			if (unlikely(napi_disable_pending(n))) {
    				local_irq_enable();
    				napi_complete(n);
    				local_irq_disable();
    
    			} else {
    				if (n->gro_list) {
    					/* flush too old packets
    					 * If HZ < 1000, flush all packets.
    					 */
    					local_irq_enable();
    					napi_gro_flush(n, HZ >= 1000);
    					local_irq_disable();
    				}
    
    				list_move_tail(&n->poll_list, &sd->poll_list);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    out:
    
    	net_rps_action_and_irq_enable(sd);
    
    #ifdef CONFIG_NET_DMA
    	/*
    	 * There may not be any more sk_buffs coming right now, so push
    	 * any pending DMA copies to hardware
    	 */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return;
    
    softnet_break:
    
    Changli Gao's avatar
    Changli Gao committed
    	sd->time_squeeze++;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
    	goto out;
    }
    
    
    struct netdev_upper {
    	struct net_device *dev;
    	bool master;
    	struct list_head list;
    	struct rcu_head rcu;
    	struct list_head search_list;
    };
    
    static void __append_search_uppers(struct list_head *search_list,
    				   struct net_device *dev)
    {
    	struct netdev_upper *upper;
    
    	list_for_each_entry(upper, &dev->upper_dev_list, list) {
    		/* check if this upper is not already in search list */
    		if (list_empty(&upper->search_list))
    			list_add_tail(&upper->search_list, search_list);
    	}
    }
    
    static bool __netdev_search_upper_dev(struct net_device *dev,
    				      struct net_device *upper_dev)
    {
    	LIST_HEAD(search_list);
    	struct netdev_upper *upper;
    	struct netdev_upper *tmp;
    	bool ret = false;
    
    	__append_search_uppers(&search_list, dev);
    	list_for_each_entry(upper, &search_list, search_list) {
    		if (upper->dev == upper_dev) {
    			ret = true;
    			break;
    		}
    		__append_search_uppers(&search_list, upper->dev);
    	}
    	list_for_each_entry_safe(upper, tmp, &search_list, search_list)
    		INIT_LIST_HEAD(&upper->search_list);
    	return ret;
    }
    
    static struct netdev_upper *__netdev_find_upper(struct net_device *dev,
    						struct net_device *upper_dev)
    {
    	struct netdev_upper *upper;
    
    	list_for_each_entry(upper, &dev->upper_dev_list, list) {
    		if (upper->dev == upper_dev)
    			return upper;
    	}
    	return NULL;
    }
    
    /**
     * netdev_has_upper_dev - Check if device is linked to an upper device
     * @dev: device
     * @upper_dev: upper device to check
     *
     * Find out if a device is linked to specified upper device and return true
     * in case it is. Note that this checks only immediate upper device,
     * not through a complete stack of devices. The caller must hold the RTNL lock.
     */
    bool netdev_has_upper_dev(struct net_device *dev,
    			  struct net_device *upper_dev)
    {
    	ASSERT_RTNL();
    
    	return __netdev_find_upper(dev, upper_dev);
    }
    EXPORT_SYMBOL(netdev_has_upper_dev);
    
    /**
     * netdev_has_any_upper_dev - Check if device is linked to some device
     * @dev: device
     *
     * Find out if a device is linked to an upper device and return true in case
     * it is. The caller must hold the RTNL lock.
     */
    bool netdev_has_any_upper_dev(struct net_device *dev)
    {
    	ASSERT_RTNL();
    
    	return !list_empty(&dev->upper_dev_list);
    }
    EXPORT_SYMBOL(netdev_has_any_upper_dev);
    
    /**
     * netdev_master_upper_dev_get - Get master upper device
     * @dev: device
     *
     * Find a master upper device and return pointer to it or NULL in case
     * it's not there. The caller must hold the RTNL lock.
     */
    struct net_device *netdev_master_upper_dev_get(struct net_device *dev)
    {
    	struct netdev_upper *upper;
    
    	ASSERT_RTNL();
    
    	if (list_empty(&dev->upper_dev_list))
    		return NULL;
    
    	upper = list_first_entry(&dev->upper_dev_list,
    				 struct netdev_upper, list);
    	if (likely(upper->master))
    		return upper->dev;
    	return NULL;
    }
    EXPORT_SYMBOL(netdev_master_upper_dev_get);
    
    /**
     * netdev_master_upper_dev_get_rcu - Get master upper device
     * @dev: device
     *
     * Find a master upper device and return pointer to it or NULL in case
     * it's not there. The caller must hold the RCU read lock.
     */
    struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev)
    {
    	struct netdev_upper *upper;
    
    	upper = list_first_or_null_rcu(&dev->upper_dev_list,
    				       struct netdev_upper, list);
    	if (upper && likely(upper->master))
    		return upper->dev;
    	return NULL;
    }
    EXPORT_SYMBOL(netdev_master_upper_dev_get_rcu);
    
    static int __netdev_upper_dev_link(struct net_device *dev,
    				   struct net_device *upper_dev, bool master)
    {
    	struct netdev_upper *upper;
    
    	ASSERT_RTNL();
    
    	if (dev == upper_dev)
    		return -EBUSY;
    
    	/* To prevent loops, check if dev is not upper device to upper_dev. */
    	if (__netdev_search_upper_dev(upper_dev, dev))
    		return -EBUSY;
    
    	if (__netdev_find_upper(dev, upper_dev))
    		return -EEXIST;
    
    	if (master && netdev_master_upper_dev_get(dev))
    		return -EBUSY;
    
    	upper = kmalloc(sizeof(*upper), GFP_KERNEL);
    	if (!upper)
    		return -ENOMEM;
    
    	upper->dev = upper_dev;
    	upper->master = master;
    	INIT_LIST_HEAD(&upper->search_list);
    
    	/* Ensure that master upper link is always the first item in list. */
    	if (master)
    		list_add_rcu(&upper->list, &dev->upper_dev_list);
    	else
    		list_add_tail_rcu(&upper->list, &dev->upper_dev_list);
    	dev_hold(upper_dev);
    
    	return 0;
    }
    
    /**
     * netdev_upper_dev_link - Add a link to the upper device
     * @dev: device
     * @upper_dev: new upper device
     *
     * Adds a link to device which is upper to this one. The caller must hold
     * the RTNL lock. On a failure a negative errno code is returned.
     * On success the reference counts are adjusted and the function
     * returns zero.
     */
    int netdev_upper_dev_link(struct net_device *dev,
    			  struct net_device *upper_dev)
    {
    	return __netdev_upper_dev_link(dev, upper_dev, false);
    }
    EXPORT_SYMBOL(netdev_upper_dev_link);
    
    /**
     * netdev_master_upper_dev_link - Add a master link to the upper device
     * @dev: device
     * @upper_dev: new upper device
     *
     * Adds a link to device which is upper to this one. In this case, only
     * one master upper device can be linked, although other non-master devices
     * might be linked as well. The caller must hold the RTNL lock.
     * On a failure a negative errno code is returned. On success the reference
     * counts are adjusted and the function returns zero.
     */
    int netdev_master_upper_dev_link(struct net_device *dev,
    				 struct net_device *upper_dev)
    {
    	return __netdev_upper_dev_link(dev, upper_dev, true);
    }
    EXPORT_SYMBOL(netdev_master_upper_dev_link);
    
    /**
     * netdev_upper_dev_unlink - Removes a link to upper device
     * @dev: device
     * @upper_dev: new upper device
     *
     * Removes a link to device which is upper to this one. The caller must hold
     * the RTNL lock.
     */
    void netdev_upper_dev_unlink(struct net_device *dev,
    			     struct net_device *upper_dev)
    {
    	struct netdev_upper *upper;
    
    	ASSERT_RTNL();
    
    	upper = __netdev_find_upper(dev, upper_dev);
    	if (!upper)
    		return;
    	list_del_rcu(&upper->list);
    	dev_put(upper_dev);
    	kfree_rcu(upper, rcu);
    }
    EXPORT_SYMBOL(netdev_upper_dev_unlink);
    
    
    static void dev_change_rx_flags(struct net_device *dev, int flags)
    {
    
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    	if ((dev->flags & IFF_UP) && ops->ndo_change_rx_flags)
    		ops->ndo_change_rx_flags(dev, flags);
    
    static int __dev_set_promiscuity(struct net_device *dev, int inc)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	unsigned int old_flags = dev->flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	dev->flags |= IFF_PROMISC;
    	dev->promiscuity += inc;
    	if (dev->promiscuity == 0) {
    		/*
    		 * Avoid overflow.
    		 * If inc causes overflow, untouch promisc and return error.
    		 */
    		if (inc < 0)
    			dev->flags &= ~IFF_PROMISC;
    		else {
    			dev->promiscuity -= inc;
    
    			pr_warn("%s: promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken.\n",
    				dev->name);
    
    		pr_info("device %s %s promiscuous mode\n",
    			dev->name,
    			dev->flags & IFF_PROMISC ? "entered" : "left");
    
    		if (audit_enabled) {
    			current_uid_gid(&uid, &gid);
    
    			audit_log(current->audit_context, GFP_ATOMIC,
    				AUDIT_ANOM_PROMISCUOUS,
    				"dev=%s prom=%d old_prom=%d auid=%u uid=%u gid=%u ses=%u",
    				dev->name, (dev->flags & IFF_PROMISC),
    				(old_flags & IFF_PROMISC),
    
    				from_kuid(&init_user_ns, audit_get_loginuid(current)),
    
    				from_kuid(&init_user_ns, uid),
    				from_kgid(&init_user_ns, gid),
    
    				audit_get_sessionid(current));
    
    		dev_change_rx_flags(dev, IFF_PROMISC);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    /**
     *	dev_set_promiscuity	- update promiscuity count on a device
     *	@dev: device
     *	@inc: modifier
     *
     *	Add or remove promiscuity from a device. While the count in the device
     *	remains above zero the interface remains promiscuous. Once it hits zero
     *	the device reverts back to normal filtering operation. A negative inc
     *	value is used to drop promiscuity on the device.
    
     *	Return 0 if successful or a negative errno code on error.
    
    int dev_set_promiscuity(struct net_device *dev, int inc)
    
    	unsigned int old_flags = dev->flags;
    
    	err = __dev_set_promiscuity(dev, inc);
    
    	if (dev->flags != old_flags)
    		dev_set_rx_mode(dev);
    
    EXPORT_SYMBOL(dev_set_promiscuity);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /**
     *	dev_set_allmulti	- update allmulti count on a device
     *	@dev: device
     *	@inc: modifier
     *
     *	Add or remove reception of all multicast frames to a device. While the
     *	count in the device remains above zero the interface remains listening
     *	to all interfaces. Once it hits zero the device reverts back to normal
     *	filtering operation. A negative @inc value is used to drop the counter
     *	when releasing a resource needing all multicasts.
    
     *	Return 0 if successful or a negative errno code on error.
    
    int dev_set_allmulti(struct net_device *dev, int inc)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	unsigned int old_flags = dev->flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	dev->flags |= IFF_ALLMULTI;
    
    	dev->allmulti += inc;
    	if (dev->allmulti == 0) {
    		/*
    		 * Avoid overflow.
    		 * If inc causes overflow, untouch allmulti and return error.
    		 */
    		if (inc < 0)
    			dev->flags &= ~IFF_ALLMULTI;
    		else {
    			dev->allmulti -= inc;
    
    			pr_warn("%s: allmulti touches roof, set allmulti failed. allmulti feature of device might be broken.\n",
    				dev->name);
    
    	if (dev->flags ^ old_flags) {
    
    		dev_change_rx_flags(dev, IFF_ALLMULTI);
    
    		dev_set_rx_mode(dev);
    
    EXPORT_SYMBOL(dev_set_allmulti);
    
    
    /*
     *	Upload unicast and multicast address lists to device and
     *	configure RX filtering. When the device doesn't support unicast
    
     *	filtering it is put in promiscuous mode while unicast addresses
    
     *	are present.
     */
    void __dev_set_rx_mode(struct net_device *dev)
    {
    
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    
    	/* dev_open will call this function so the list will stay sane. */
    	if (!(dev->flags&IFF_UP))
    		return;
    
    	if (!netif_device_present(dev))
    
    	if (!(dev->priv_flags & IFF_UNICAST_FLT)) {
    
    		/* Unicast addresses changes may only happen under the rtnl,
    		 * therefore calling __dev_set_promiscuity here is safe.
    		 */
    
    		if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
    
    			__dev_set_promiscuity(dev, 1);
    
    			dev->uc_promisc = true;
    
    		} else if (netdev_uc_empty(dev) && dev->uc_promisc) {
    
    			__dev_set_promiscuity(dev, -1);
    
    			dev->uc_promisc = false;
    
    
    	if (ops->ndo_set_rx_mode)
    		ops->ndo_set_rx_mode(dev);
    
    }
    
    void dev_set_rx_mode(struct net_device *dev)
    {
    
    	netif_addr_lock_bh(dev);
    
    	__dev_set_rx_mode(dev);
    
    	netif_addr_unlock_bh(dev);
    
    /**
     *	dev_get_flags - get flags reported to userspace
     *	@dev: device
     *
     *	Get the combination of flag bits exported through APIs to userspace.
     */
    
    unsigned int dev_get_flags(const struct net_device *dev)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	unsigned int flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	flags = (dev->flags & ~(IFF_PROMISC |
    				IFF_ALLMULTI |
    
    				IFF_RUNNING |
    				IFF_LOWER_UP |
    				IFF_DORMANT)) |
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		(dev->gflags & (IFF_PROMISC |
    				IFF_ALLMULTI));
    
    
    	if (netif_running(dev)) {
    		if (netif_oper_up(dev))
    			flags |= IFF_RUNNING;
    		if (netif_carrier_ok(dev))
    			flags |= IFF_LOWER_UP;
    		if (netif_dormant(dev))
    			flags |= IFF_DORMANT;
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	return flags;
    }
    
    EXPORT_SYMBOL(dev_get_flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    int __dev_change_flags(struct net_device *dev, unsigned int flags)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	unsigned int old_flags = dev->flags;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/*
    	 *	Set the flags on our device.
    	 */
    
    	dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP |
    			       IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL |
    			       IFF_AUTOMEDIA)) |
    		     (dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC |
    				    IFF_ALLMULTI));
    
    	/*
    	 *	Load in the correct multicast list now the flags have changed.
    	 */
    
    
    	if ((old_flags ^ flags) & IFF_MULTICAST)
    		dev_change_rx_flags(dev, IFF_MULTICAST);
    
    	dev_set_rx_mode(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 *	Have we downed the interface. We handle IFF_UP ourselves
    	 *	according to user attempts to set it, rather than blindly
    	 *	setting it.
    	 */
    
    	ret = 0;
    	if ((old_flags ^ flags) & IFF_UP) {	/* Bit is different  ? */
    
    		ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    		if (!ret)
    
    			dev_set_rx_mode(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	if ((flags ^ dev->gflags) & IFF_PROMISC) {
    
    		int inc = (flags & IFF_PROMISC) ? 1 : -1;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		dev->gflags ^= IFF_PROMISC;
    		dev_set_promiscuity(dev, inc);
    	}
    
    	/* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
    	   is important. Some (broken) drivers set IFF_PROMISC, when
    	   IFF_ALLMULTI is requested not asking us and not reporting.
    	 */
    	if ((flags ^ dev->gflags) & IFF_ALLMULTI) {
    
    		int inc = (flags & IFF_ALLMULTI) ? 1 : -1;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		dev->gflags ^= IFF_ALLMULTI;
    		dev_set_allmulti(dev, inc);
    	}
    
    
    	return ret;
    }
    
    void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
    {
    	unsigned int changes = dev->flags ^ old_flags;
    
    	if (changes & IFF_UP) {
    		if (dev->flags & IFF_UP)
    			call_netdevice_notifiers(NETDEV_UP, dev);
    		else
    			call_netdevice_notifiers(NETDEV_DOWN, dev);
    	}
    
    	if (dev->flags & IFF_UP &&
    	    (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE)))
    		call_netdevice_notifiers(NETDEV_CHANGE, dev);
    }
    
    /**
     *	dev_change_flags - change device settings
     *	@dev: device
     *	@flags: device state flags
     *
     *	Change settings on device based state flags. The flags are
     *	in the userspace exported format.
     */
    
    int dev_change_flags(struct net_device *dev, unsigned int flags)
    
    	int ret;
    	unsigned int changes, old_flags = dev->flags;
    
    
    	ret = __dev_change_flags(dev, flags);
    	if (ret < 0)
    		return ret;
    
    	changes = old_flags ^ dev->flags;
    
    	if (changes)
    		rtmsg_ifinfo(RTM_NEWLINK, dev, changes);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	__dev_notify_flags(dev, old_flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return ret;
    }
    
    EXPORT_SYMBOL(dev_change_flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /**
     *	dev_set_mtu - Change maximum transfer unit
     *	@dev: device
     *	@new_mtu: new transfer unit
     *
     *	Change the maximum transfer size of the network device.
     */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    int dev_set_mtu(struct net_device *dev, int new_mtu)
    {
    
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int err;
    
    	if (new_mtu == dev->mtu)
    		return 0;
    
    	/*	MTU must be positive.	 */
    	if (new_mtu < 0)
    		return -EINVAL;
    
    	if (!netif_device_present(dev))
    		return -ENODEV;
    
    	err = 0;
    
    	if (ops->ndo_change_mtu)
    		err = ops->ndo_change_mtu(dev, new_mtu);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	else
    		dev->mtu = new_mtu;
    
    		call_netdevice_notifiers(NETDEV_CHANGEMTU, dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return err;
    }
    
    EXPORT_SYMBOL(dev_set_mtu);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /**
     *	dev_set_group - Change group this device belongs to
     *	@dev: device
     *	@new_group: group this device should belong to
     */
    void dev_set_group(struct net_device *dev, int new_group)
    {
    	dev->group = new_group;
    }
    EXPORT_SYMBOL(dev_set_group);
    
    
    /**
     *	dev_set_mac_address - Change Media Access Control Address
     *	@dev: device
     *	@sa: new address
     *
     *	Change the hardware (MAC) address of the device
     */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
    {
    
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int err;
    
    
    	if (!ops->ndo_set_mac_address)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EOPNOTSUPP;
    	if (sa->sa_family != dev->type)
    		return -EINVAL;
    	if (!netif_device_present(dev))
    		return -ENODEV;
    
    	err = ops->ndo_set_mac_address(dev, sa);
    
    	dev->addr_assign_type = NET_ADDR_SET;
    
    	call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
    
    	add_device_randomness(dev->dev_addr, dev->addr_len);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(dev_set_mac_address);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /**
     *	dev_change_carrier - Change device carrier
     *	@dev: device
    
     *	@new_carrier: new value
    
     *
     *	Change device carrier
     */
    int dev_change_carrier(struct net_device *dev, bool new_carrier)
    {
    	const struct net_device_ops *ops = dev->netdev_ops;
    
    	if (!ops->ndo_change_carrier)
    		return -EOPNOTSUPP;
    	if (!netif_device_present(dev))
    		return -ENODEV;
    	return ops->ndo_change_carrier(dev, new_carrier);
    }
    EXPORT_SYMBOL(dev_change_carrier);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /**
     *	dev_new_index	-	allocate an ifindex
    
     *	@net: the applicable net namespace
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *
     *	Returns a suitable unique value for a new device interface
     *	number.  The caller must hold the rtnl semaphore or the
     *	dev_base_lock to be sure it remains unique.
     */
    
    static int dev_new_index(struct net *net)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	int ifindex = net->ifindex;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	for (;;) {
    		if (++ifindex <= 0)
    			ifindex = 1;
    
    		if (!__dev_get_by_index(net, ifindex))
    
    			return net->ifindex = ifindex;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    }
    
    /* Delayed registration/unregisteration */
    
    static LIST_HEAD(net_todo_list);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static void net_set_todo(struct net_device *dev)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	list_add_tail(&dev->todo_list, &net_todo_list);
    }
    
    
    static void rollback_registered_many(struct list_head *head)
    
    	struct net_device *dev, *tmp;
    
    	BUG_ON(dev_boot_phase);
    	ASSERT_RTNL();
    
    
    	list_for_each_entry_safe(dev, tmp, head, unreg_list) {
    
    		/* Some devices call without registering
    
    		 * for initialization unwind. Remove those
    		 * devices and proceed with the remaining.
    
    		 */
    		if (dev->reg_state == NETREG_UNINITIALIZED) {
    
    			pr_debug("unregister_netdevice: device %s/%p never was registered\n",
    				 dev->name, dev);
    
    			list_del(&dev->unreg_list);
    			continue;
    
    		dev->dismantle = true;
    
    		BUG_ON(dev->reg_state != NETREG_REGISTERED);
    
    	/* If device is running, close it first. */
    	dev_close_many(head);
    
    	list_for_each_entry(dev, head, unreg_list) {
    
    		/* And unlink it from device chain. */
    		unlist_netdevice(dev);
    
    		dev->reg_state = NETREG_UNREGISTERING;
    	}
    
    	list_for_each_entry(dev, head, unreg_list) {
    		/* Shutdown queueing discipline. */
    		dev_shutdown(dev);
    
    		/* Notify protocols, that we are about to destroy
    		   this device. They should clean all the things.
    		*/
    		call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
    
    		if (!dev->rtnl_link_ops ||
    		    dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
    			rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
    
    
    		/*
    		 *	Flush the unicast and multicast chains
    		 */
    
    		dev_mc_flush(dev);
    
    		if (dev->netdev_ops->ndo_uninit)
    			dev->netdev_ops->ndo_uninit(dev);
    
    		/* Notifier chain MUST detach us all upper devices. */
    		WARN_ON(netdev_has_any_upper_dev(dev));
    
    		/* Remove entries from kobject tree */
    		netdev_unregister_kobject(dev);
    
    #ifdef CONFIG_XPS
    		/* Remove XPS queueing entries */
    		netif_reset_xps_queues_gt(dev, 0);
    #endif
    
    	list_for_each_entry(dev, head, unreg_list)
    
    		dev_put(dev);
    }
    
    static void rollback_registered(struct net_device *dev)
    {
    	LIST_HEAD(single);
    
    	list_add(&dev->unreg_list, &single);
    	rollback_registered_many(&single);
    
    	list_del(&single);
    
    static netdev_features_t netdev_fix_features(struct net_device *dev,
    	netdev_features_t features)
    
    	/* Fix illegal checksum combinations */
    	if ((features & NETIF_F_HW_CSUM) &&
    	    (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
    
    		netdev_warn(dev, "mixed HW and IP checksum settings.\n");
    
    		features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
    	}
    
    
    	/* Fix illegal SG+CSUM combinations. */
    	if ((features & NETIF_F_SG) &&
    	    !(features & NETIF_F_ALL_CSUM)) {
    
    		netdev_dbg(dev,
    			"Dropping NETIF_F_SG since no checksum feature.\n");
    
    		features &= ~NETIF_F_SG;
    	}
    
    	/* TSO requires that SG is present as well. */
    
    	if ((features & NETIF_F_ALL_TSO) && !(features & NETIF_F_SG)) {
    
    		netdev_dbg(dev, "Dropping TSO features since no SG feature.\n");
    
    		features &= ~NETIF_F_ALL_TSO;
    
    	/* TSO ECN requires that TSO is present as well. */
    	if ((features & NETIF_F_ALL_TSO) == NETIF_F_TSO_ECN)
    		features &= ~NETIF_F_TSO_ECN;
    
    
    	/* Software GSO depends on SG. */
    	if ((features & NETIF_F_GSO) && !(features & NETIF_F_SG)) {
    
    		netdev_dbg(dev, "Dropping NETIF_F_GSO since no SG feature.\n");
    
    		features &= ~NETIF_F_GSO;
    	}
    
    
    	/* UFO needs SG and checksumming */
    
    	if (features & NETIF_F_UFO) {
    
    		/* maybe split UFO into V4 and V6? */
    		if (!((features & NETIF_F_GEN_CSUM) ||
    		    (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
    			    == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
    
    				"Dropping NETIF_F_UFO since no checksum offload features.\n");
    
    			features &= ~NETIF_F_UFO;
    		}
    
    		if (!(features & NETIF_F_SG)) {
    
    				"Dropping NETIF_F_UFO since no NETIF_F_SG feature.\n");
    
    			features &= ~NETIF_F_UFO;
    		}
    	}
    
    	return features;
    }
    
    
    int __netdev_update_features(struct net_device *dev)
    
    	features = netdev_get_wanted_features(dev);
    
    	if (dev->netdev_ops->ndo_fix_features)
    		features = dev->netdev_ops->ndo_fix_features(dev, features);
    
    	/* driver might be less strict about feature dependencies */
    	features = netdev_fix_features(dev, features);
    
    	if (dev->features == features)
    
    	netdev_dbg(dev, "Features changed: %pNF -> %pNF\n",
    		&dev->features, &features);
    
    
    	if (dev->netdev_ops->ndo_set_features)
    		err = dev->netdev_ops->ndo_set_features(dev, features);
    
    
    		netdev_err(dev,
    
    			"set_features() failed (%d); wanted %pNF, left %pNF\n",
    			err, &features, &dev->features);