diff options
Diffstat (limited to 'net/core')
| -rw-r--r-- | net/core/dev.c | 233 | ||||
| -rw-r--r-- | net/core/dev_addr_lists.c | 12 | ||||
| -rw-r--r-- | net/core/drop_monitor.c | 12 | ||||
| -rw-r--r-- | net/core/dst.c | 55 | ||||
| -rw-r--r-- | net/core/ethtool.c | 196 | ||||
| -rw-r--r-- | net/core/fib_rules.c | 3 | ||||
| -rw-r--r-- | net/core/filter.c | 65 | ||||
| -rw-r--r-- | net/core/gen_estimator.c | 9 | ||||
| -rw-r--r-- | net/core/net-sysfs.c | 60 | ||||
| -rw-r--r-- | net/core/net_namespace.c | 22 | ||||
| -rw-r--r-- | net/core/netpoll.c | 28 | ||||
| -rw-r--r-- | net/core/pktgen.c | 222 | ||||
| -rw-r--r-- | net/core/rtnetlink.c | 27 | ||||
| -rw-r--r-- | net/core/skbuff.c | 4 | ||||
| -rw-r--r-- | net/core/sysctl_net_core.c | 9 | ||||
| -rw-r--r-- | net/core/utils.c | 24 | 
16 files changed, 475 insertions, 506 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 956d3b006e8..bcb05cb799c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -948,7 +948,7 @@ int dev_alloc_name(struct net_device *dev, const char *name)  }  EXPORT_SYMBOL(dev_alloc_name); -static int dev_get_valid_name(struct net_device *dev, const char *name, bool fmt) +static int dev_get_valid_name(struct net_device *dev, const char *name)  {  	struct net *net; @@ -958,7 +958,7 @@ static int dev_get_valid_name(struct net_device *dev, const char *name, bool fmt  	if (!dev_valid_name(name))  		return -EINVAL; -	if (fmt && strchr(name, '%')) +	if (strchr(name, '%'))  		return dev_alloc_name(dev, name);  	else if (__dev_get_by_name(net, name))  		return -EEXIST; @@ -995,7 +995,7 @@ int dev_change_name(struct net_device *dev, const char *newname)  	memcpy(oldname, dev->name, IFNAMSIZ); -	err = dev_get_valid_name(dev, newname, 1); +	err = dev_get_valid_name(dev, newname);  	if (err < 0)  		return err; @@ -1007,7 +1007,7 @@ rollback:  	}  	write_lock_bh(&dev_base_lock); -	hlist_del(&dev->name_hlist); +	hlist_del_rcu(&dev->name_hlist);  	write_unlock_bh(&dev_base_lock);  	synchronize_rcu(); @@ -1284,11 +1284,13 @@ static int dev_close_many(struct list_head *head)   */  int dev_close(struct net_device *dev)  { -	LIST_HEAD(single); +	if (dev->flags & IFF_UP) { +		LIST_HEAD(single); -	list_add(&dev->unreg_list, &single); -	dev_close_many(&single); -	list_del(&single); +		list_add(&dev->unreg_list, &single); +		dev_close_many(&single); +		list_del(&single); +	}  	return 0;  }  EXPORT_SYMBOL(dev_close); @@ -1315,7 +1317,8 @@ void dev_disable_lro(struct net_device *dev)  		return;  	__ethtool_set_flags(dev, flags & ~ETH_FLAG_LRO); -	WARN_ON(dev->features & NETIF_F_LRO); +	if (unlikely(dev->features & NETIF_F_LRO)) +		netdev_WARN(dev, "failed to disable LRO!\n");  }  EXPORT_SYMBOL(dev_disable_lro); @@ -2502,8 +2505,8 @@ static inline void ____napi_schedule(struct softnet_data *sd,  __u32 __skb_get_rxhash(struct sk_buff *skb)  {  	int nhoff, hash = 0, poff; -	struct ipv6hdr *ip6; -	struct iphdr *ip; +	const struct ipv6hdr *ip6; +	const struct iphdr *ip;  	u8 ip_proto;  	u32 addr1, addr2, ihl;  	union { @@ -2518,7 +2521,7 @@ __u32 __skb_get_rxhash(struct sk_buff *skb)  		if (!pskb_may_pull(skb, sizeof(*ip) + nhoff))  			goto done; -		ip = (struct iphdr *) (skb->data + nhoff); +		ip = (const struct iphdr *) (skb->data + nhoff);  		if (ip->frag_off & htons(IP_MF | IP_OFFSET))  			ip_proto = 0;  		else @@ -2531,7 +2534,7 @@ __u32 __skb_get_rxhash(struct sk_buff *skb)  		if (!pskb_may_pull(skb, sizeof(*ip6) + nhoff))  			goto done; -		ip6 = (struct ipv6hdr *) (skb->data + nhoff); +		ip6 = (const struct ipv6hdr *) (skb->data + nhoff);  		ip_proto = ip6->nexthdr;  		addr1 = (__force u32) ip6->saddr.s6_addr32[3];  		addr2 = (__force u32) ip6->daddr.s6_addr32[3]; @@ -3076,25 +3079,6 @@ void netdev_rx_handler_unregister(struct net_device *dev)  }  EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister); -static void vlan_on_bond_hook(struct sk_buff *skb) -{ -	/* -	 * Make sure ARP frames received on VLAN interfaces stacked on -	 * bonding interfaces still make their way to any base bonding -	 * device that may have registered for a specific ptype. -	 */ -	if (skb->dev->priv_flags & IFF_802_1Q_VLAN && -	    vlan_dev_real_dev(skb->dev)->priv_flags & IFF_BONDING && -	    skb->protocol == htons(ETH_P_ARP)) { -		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); - -		if (!skb2) -			return; -		skb2->dev = vlan_dev_real_dev(skb->dev); -		netif_rx(skb2); -	} -} -  static int __netif_receive_skb(struct sk_buff *skb)  {  	struct packet_type *ptype, *pt_prev; @@ -3130,6 +3114,12 @@ another_round:  	__this_cpu_inc(softnet_data.processed); +	if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) { +		skb = vlan_untag(skb); +		if (unlikely(!skb)) +			goto out; +	} +  #ifdef CONFIG_NET_CLS_ACT  	if (skb->tc_verd & TC_NCLS) {  		skb->tc_verd = CLR_TC_NCLS(skb->tc_verd); @@ -3177,15 +3167,13 @@ ncls:  			ret = deliver_skb(skb, pt_prev, orig_dev);  			pt_prev = NULL;  		} -		if (vlan_hwaccel_do_receive(&skb)) { +		if (vlan_do_receive(&skb)) {  			ret = __netif_receive_skb(skb);  			goto out;  		} else if (unlikely(!skb))  			goto out;  	} -	vlan_on_bond_hook(skb); -  	/* deliver only exact match when indicated */  	null_or_dev = deliver_exact ? skb->dev : NULL; @@ -4306,10 +4294,8 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)  	slave->master = master; -	if (old) { -		synchronize_net(); +	if (old)  		dev_put(old); -	}  	return 0;  }  EXPORT_SYMBOL(netdev_set_master); @@ -4510,6 +4496,30 @@ void dev_set_rx_mode(struct net_device *dev)  }  /** + *	dev_ethtool_get_settings - call device's ethtool_ops::get_settings() + *	@dev: device + *	@cmd: memory area for ethtool_ops::get_settings() result + * + *      The cmd arg is initialized properly (cleared and + *      ethtool_cmd::cmd field set to ETHTOOL_GSET). + * + *	Return device's ethtool_ops::get_settings() result value or + *	-EOPNOTSUPP when device doesn't expose + *	ethtool_ops::get_settings() operation. + */ +int dev_ethtool_get_settings(struct net_device *dev, +			     struct ethtool_cmd *cmd) +{ +	if (!dev->ethtool_ops || !dev->ethtool_ops->get_settings) +		return -EOPNOTSUPP; + +	memset(cmd, 0, sizeof(struct ethtool_cmd)); +	cmd->cmd = ETHTOOL_GSET; +	return dev->ethtool_ops->get_settings(dev, cmd); +} +EXPORT_SYMBOL(dev_ethtool_get_settings); + +/**   *	dev_get_flags - get flags reported to userspace   *	@dev: device   * @@ -4773,7 +4783,7 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm  		 * is never reached  		 */  		WARN_ON(1); -		err = -EINVAL; +		err = -ENOTTY;  		break;  	} @@ -5041,7 +5051,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)  		/* Set the per device memory buffer space.  		 * Not applicable in our case */  	case SIOCSIFLINK: -		return -EINVAL; +		return -ENOTTY;  	/*  	 *	Unknown or private ioctl. @@ -5062,7 +5072,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)  		/* Take care of Wireless Extensions */  		if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)  			return wext_handle_ioctl(net, &ifr, cmd, arg); -		return -EINVAL; +		return -ENOTTY;  	}  } @@ -5114,7 +5124,7 @@ static void rollback_registered_many(struct list_head *head)  			list_del(&dev->unreg_list);  			continue;  		} - +		dev->dismantle = true;  		BUG_ON(dev->reg_state != NETREG_REGISTERED);  	} @@ -5184,33 +5194,37 @@ u32 netdev_fix_features(struct net_device *dev, u32 features)  	/* Fix illegal checksum combinations */  	if ((features & NETIF_F_HW_CSUM) &&  	    (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { -		netdev_info(dev, "mixed HW and IP checksum settings.\n"); +		netdev_warn(dev, "mixed HW and IP checksum settings.\n");  		features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);  	}  	if ((features & NETIF_F_NO_CSUM) &&  	    (features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { -		netdev_info(dev, "mixed no checksumming and other settings.\n"); +		netdev_warn(dev, "mixed no checksumming and other settings.\n");  		features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM);  	}  	/* Fix illegal SG+CSUM combinations. */  	if ((features & NETIF_F_SG) &&  	    !(features & NETIF_F_ALL_CSUM)) { -		netdev_info(dev, -			    "Dropping NETIF_F_SG since no checksum feature.\n"); +		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_TSO) && !(features & NETIF_F_SG)) { -		netdev_info(dev, "Dropping NETIF_F_TSO since no SG feature.\n"); -		features &= ~NETIF_F_TSO; +	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_info(dev, "Dropping NETIF_F_GSO since no SG feature.\n"); +		netdev_dbg(dev, "Dropping NETIF_F_GSO since no SG feature.\n");  		features &= ~NETIF_F_GSO;  	} @@ -5220,13 +5234,13 @@ u32 netdev_fix_features(struct net_device *dev, u32 features)  		if (!((features & NETIF_F_GEN_CSUM) ||  		    (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))  			    == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { -			netdev_info(dev, +			netdev_dbg(dev,  				"Dropping NETIF_F_UFO since no checksum offload features.\n");  			features &= ~NETIF_F_UFO;  		}  		if (!(features & NETIF_F_SG)) { -			netdev_info(dev, +			netdev_dbg(dev,  				"Dropping NETIF_F_UFO since no NETIF_F_SG feature.\n");  			features &= ~NETIF_F_UFO;  		} @@ -5236,11 +5250,13 @@ u32 netdev_fix_features(struct net_device *dev, u32 features)  }  EXPORT_SYMBOL(netdev_fix_features); -void netdev_update_features(struct net_device *dev) +int __netdev_update_features(struct net_device *dev)  {  	u32 features;  	int err = 0; +	ASSERT_RTNL(); +  	features = netdev_get_wanted_features(dev);  	if (dev->netdev_ops->ndo_fix_features) @@ -5250,24 +5266,60 @@ void netdev_update_features(struct net_device *dev)  	features = netdev_fix_features(dev, features);  	if (dev->features == features) -		return; +		return 0; -	netdev_info(dev, "Features changed: 0x%08x -> 0x%08x\n", +	netdev_dbg(dev, "Features changed: 0x%08x -> 0x%08x\n",  		dev->features, features);  	if (dev->netdev_ops->ndo_set_features)  		err = dev->netdev_ops->ndo_set_features(dev, features); -	if (!err) -		dev->features = features; -	else if (err < 0) +	if (unlikely(err < 0)) {  		netdev_err(dev,  			"set_features() failed (%d); wanted 0x%08x, left 0x%08x\n",  			err, features, dev->features); +		return -1; +	} + +	if (!err) +		dev->features = features; + +	return 1; +} + +/** + *	netdev_update_features - recalculate device features + *	@dev: the device to check + * + *	Recalculate dev->features set and send notifications if it + *	has changed. Should be called after driver or hardware dependent + *	conditions might have changed that influence the features. + */ +void netdev_update_features(struct net_device *dev) +{ +	if (__netdev_update_features(dev)) +		netdev_features_change(dev);  }  EXPORT_SYMBOL(netdev_update_features);  /** + *	netdev_change_features - recalculate device features + *	@dev: the device to check + * + *	Recalculate dev->features set and send notifications even + *	if they have not changed. Should be called instead of + *	netdev_update_features() if also dev->vlan_features might + *	have changed to allow the changes to be propagated to stacked + *	VLAN devices. + */ +void netdev_change_features(struct net_device *dev) +{ +	__netdev_update_features(dev); +	netdev_features_change(dev); +} +EXPORT_SYMBOL(netdev_change_features); + +/**   *	netif_stacked_transfer_operstate -	transfer operstate   *	@rootdev: the root or lower level device to transfer state from   *	@dev: the device to transfer operstate to @@ -5383,6 +5435,10 @@ int register_netdevice(struct net_device *dev)  	dev->iflink = -1; +	ret = dev_get_valid_name(dev, dev->name); +	if (ret < 0) +		goto out; +  	/* Init, if this function is available */  	if (dev->netdev_ops->ndo_init) {  		ret = dev->netdev_ops->ndo_init(dev); @@ -5393,10 +5449,6 @@ int register_netdevice(struct net_device *dev)  		}  	} -	ret = dev_get_valid_name(dev, dev->name, 0); -	if (ret) -		goto err_uninit; -  	dev->ifindex = dev_new_index(net);  	if (dev->iflink == -1)  		dev->iflink = dev->ifindex; @@ -5408,10 +5460,12 @@ int register_netdevice(struct net_device *dev)  	dev->features |= NETIF_F_SOFT_FEATURES;  	dev->wanted_features = dev->features & dev->hw_features; -	/* Avoid warning from netdev_fix_features() for GSO without SG */ -	if (!(dev->wanted_features & NETIF_F_SG)) { -		dev->wanted_features &= ~NETIF_F_GSO; -		dev->features &= ~NETIF_F_GSO; +	/* Turn on no cache copy if HW is doing checksum */ +	dev->hw_features |= NETIF_F_NOCACHE_COPY; +	if ((dev->features & NETIF_F_ALL_CSUM) && +	    !(dev->features & NETIF_F_NO_CSUM)) { +		dev->wanted_features |= NETIF_F_NOCACHE_COPY; +		dev->features |= NETIF_F_NOCACHE_COPY;  	}  	/* Enable GRO and NETIF_F_HIGHDMA for vlans by default, @@ -5430,7 +5484,7 @@ int register_netdevice(struct net_device *dev)  		goto err_uninit;  	dev->reg_state = NETREG_REGISTERED; -	netdev_update_features(dev); +	__netdev_update_features(dev);  	/*  	 *	Default initial state at registry is that the @@ -5527,19 +5581,7 @@ int register_netdev(struct net_device *dev)  	int err;  	rtnl_lock(); - -	/* -	 * If the name is a format string the caller wants us to do a -	 * name allocation. -	 */ -	if (strchr(dev->name, '%')) { -		err = dev_alloc_name(dev, dev->name); -		if (err < 0) -			goto out; -	} -  	err = register_netdevice(dev); -out:  	rtnl_unlock();  	return err;  } @@ -6021,7 +6063,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char  		/* We get here if we can't use the current device name */  		if (!pat)  			goto out; -		if (dev_get_valid_name(dev, pat, 1)) +		if (dev_get_valid_name(dev, pat) < 0)  			goto out;  	} @@ -6153,29 +6195,20 @@ static int dev_cpu_callback(struct notifier_block *nfb,   */  u32 netdev_increment_features(u32 all, u32 one, u32 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 (mask & NETIF_F_GEN_CSUM) +		mask |= NETIF_F_ALL_CSUM; +	mask |= NETIF_F_VLAN_CHALLENGED; -		/* 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; -		} -	} +	all |= one & (NETIF_F_ONE_FOR_ALL|NETIF_F_ALL_CSUM) & mask; +	all &= one | ~NETIF_F_ALL_FOR_ALL; -	one |= NETIF_F_ALL_CSUM; +	/* If device needs checksumming, downgrade to it. */ +	if (all & (NETIF_F_ALL_CSUM & ~NETIF_F_NO_CSUM)) +		all &= ~NETIF_F_NO_CSUM; -	one |= all & NETIF_F_ONE_FOR_ALL; -	all &= one | NETIF_F_LLTX | NETIF_F_GSO | NETIF_F_UFO; -	all |= one & mask & NETIF_F_ONE_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);  	return all;  } diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 7b39f3ed2fd..e2e66939ed0 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -68,14 +68,6 @@ static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr,  	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false);  } -static void ha_rcu_free(struct rcu_head *head) -{ -	struct netdev_hw_addr *ha; - -	ha = container_of(head, struct netdev_hw_addr, rcu_head); -	kfree(ha); -} -  static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,  			    unsigned char *addr, int addr_len,  			    unsigned char addr_type, bool global) @@ -94,7 +86,7 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,  			if (--ha->refcount)  				return 0;  			list_del_rcu(&ha->list); -			call_rcu(&ha->rcu_head, ha_rcu_free); +			kfree_rcu(ha, rcu_head);  			list->count--;  			return 0;  		} @@ -197,7 +189,7 @@ void __hw_addr_flush(struct netdev_hw_addr_list *list)  	list_for_each_entry_safe(ha, tmp, &list->list, list) {  		list_del_rcu(&ha->list); -		call_rcu(&ha->rcu_head, ha_rcu_free); +		kfree_rcu(ha, rcu_head);  	}  	list->count = 0;  } diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 706502ff64a..7f36b38e060 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -207,14 +207,6 @@ static void trace_napi_poll_hit(void *ignore, struct napi_struct *napi)  	rcu_read_unlock();  } - -static void free_dm_hw_stat(struct rcu_head *head) -{ -	struct dm_hw_stat_delta *n; -	n = container_of(head, struct dm_hw_stat_delta, rcu); -	kfree(n); -} -  static int set_all_monitor_traces(int state)  {  	int rc = 0; @@ -245,7 +237,7 @@ static int set_all_monitor_traces(int state)  		list_for_each_entry_safe(new_stat, temp, &hw_stats_list, list) {  			if (new_stat->dev == NULL) {  				list_del_rcu(&new_stat->list); -				call_rcu(&new_stat->rcu, free_dm_hw_stat); +				kfree_rcu(new_stat, rcu);  			}  		}  		break; @@ -314,7 +306,7 @@ static int dropmon_net_event(struct notifier_block *ev_block,  				new_stat->dev = NULL;  				if (trace_state == TRACE_OFF) {  					list_del_rcu(&new_stat->list); -					call_rcu(&new_stat->rcu, free_dm_hw_stat); +					kfree_rcu(new_stat, rcu);  					break;  				}  			} diff --git a/net/core/dst.c b/net/core/dst.c index 91104d35de7..81a4fa1c95e 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -19,6 +19,7 @@  #include <linux/types.h>  #include <net/net_namespace.h>  #include <linux/sched.h> +#include <linux/prefetch.h>  #include <net/dst.h> @@ -33,9 +34,6 @@   * 3) This list is guarded by a mutex,   *    so that the gc_task and dst_dev_event() can be synchronized.   */ -#if RT_CACHE_DEBUG >= 2 -static atomic_t			 dst_total = ATOMIC_INIT(0); -#endif  /*   * We want to keep lock & list close together @@ -69,10 +67,6 @@ static void dst_gc_task(struct work_struct *work)  	unsigned long expires = ~0L;  	struct dst_entry *dst, *next, head;  	struct dst_entry *last = &head; -#if RT_CACHE_DEBUG >= 2 -	ktime_t time_start = ktime_get(); -	struct timespec elapsed; -#endif  	mutex_lock(&dst_gc_mutex);  	next = dst_busy_list; @@ -146,15 +140,6 @@ loop:  	spin_unlock_bh(&dst_garbage.lock);  	mutex_unlock(&dst_gc_mutex); -#if RT_CACHE_DEBUG >= 2 -	elapsed = ktime_to_timespec(ktime_sub(ktime_get(), time_start)); -	printk(KERN_DEBUG "dst_total: %d delayed: %d work_perf: %d" -		" expires: %lu elapsed: %lu us\n", -		atomic_read(&dst_total), delayed, work_performed, -		expires, -		elapsed.tv_sec * USEC_PER_SEC + -		  elapsed.tv_nsec / NSEC_PER_USEC); -#endif  }  int dst_discard(struct sk_buff *skb) @@ -166,7 +151,8 @@ EXPORT_SYMBOL(dst_discard);  const u32 dst_default_metrics[RTAX_MAX]; -void *dst_alloc(struct dst_ops *ops, int initial_ref) +void *dst_alloc(struct dst_ops *ops, struct net_device *dev, +		int initial_ref, int initial_obsolete, int flags)  {  	struct dst_entry *dst; @@ -174,18 +160,36 @@ void *dst_alloc(struct dst_ops *ops, int initial_ref)  		if (ops->gc(ops))  			return NULL;  	} -	dst = kmem_cache_zalloc(ops->kmem_cachep, GFP_ATOMIC); +	dst = kmem_cache_alloc(ops->kmem_cachep, GFP_ATOMIC);  	if (!dst)  		return NULL; -	atomic_set(&dst->__refcnt, initial_ref); +	dst->child = NULL; +	dst->dev = dev; +	if (dev) +		dev_hold(dev);  	dst->ops = ops; -	dst->lastuse = jiffies; -	dst->path = dst; -	dst->input = dst->output = dst_discard;  	dst_init_metrics(dst, dst_default_metrics, true); -#if RT_CACHE_DEBUG >= 2 -	atomic_inc(&dst_total); +	dst->expires = 0UL; +	dst->path = dst; +	dst->neighbour = NULL; +	dst->hh = NULL; +#ifdef CONFIG_XFRM +	dst->xfrm = NULL; +#endif +	dst->input = dst_discard; +	dst->output = dst_discard; +	dst->error = 0; +	dst->obsolete = initial_obsolete; +	dst->header_len = 0; +	dst->trailer_len = 0; +#ifdef CONFIG_IP_ROUTE_CLASSID +	dst->tclassid = 0;  #endif +	atomic_set(&dst->__refcnt, initial_ref); +	dst->__use = 0; +	dst->lastuse = jiffies; +	dst->flags = flags; +	dst->next = NULL;  	dst_entries_add(ops, 1);  	return dst;  } @@ -245,9 +249,6 @@ again:  		dst->ops->destroy(dst);  	if (dst->dev)  		dev_put(dst->dev); -#if RT_CACHE_DEBUG >= 2 -	atomic_dec(&dst_total); -#endif  	kmem_cache_free(dst->ops->kmem_cachep, dst);  	dst = child; diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 74ead9eca12..84e7304532e 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -21,6 +21,8 @@  #include <linux/uaccess.h>  #include <linux/vmalloc.h>  #include <linux/slab.h> +#include <linux/rtnetlink.h> +#include <linux/sched.h>  /*   * Some useful ethtool_ops methods that're device independent. @@ -317,7 +319,7 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)  	dev->wanted_features &= ~features[0].valid;  	dev->wanted_features |= features[0].valid & features[0].requested; -	netdev_update_features(dev); +	__netdev_update_features(dev);  	if ((dev->wanted_features ^ dev->features) & features[0].valid)  		ret |= ETHTOOL_F_WISH; @@ -330,7 +332,7 @@ static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GS  	/* NETIF_F_IP_CSUM */         "tx-checksum-ipv4",  	/* NETIF_F_NO_CSUM */         "tx-checksum-unneeded",  	/* NETIF_F_HW_CSUM */         "tx-checksum-ip-generic", -	/* NETIF_F_IPV6_CSUM */       "tx_checksum-ipv6", +	/* NETIF_F_IPV6_CSUM */       "tx-checksum-ipv6",  	/* NETIF_F_HIGHDMA */         "highdma",  	/* NETIF_F_FRAGLIST */        "tx-scatter-gather-fraglist",  	/* NETIF_F_HW_VLAN_TX */      "tx-vlan-hw-insert", @@ -359,8 +361,8 @@ static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GS  	/* NETIF_F_NTUPLE */          "rx-ntuple-filter",  	/* NETIF_F_RXHASH */          "rx-hashing",  	/* NETIF_F_RXCSUM */          "rx-checksum", -	"", -	"", +	/* NETIF_F_NOCACHE_COPY */    "tx-nocache-copy", +	/* NETIF_F_LOOPBACK */        "loopback",  };  static int __ethtool_get_sset_count(struct net_device *dev, int sset) @@ -499,7 +501,7 @@ static int ethtool_set_one_feature(struct net_device *dev,  		else  			dev->wanted_features &= ~mask; -		netdev_update_features(dev); +		__netdev_update_features(dev);  		return 0;  	} @@ -544,14 +546,14 @@ int __ethtool_set_flags(struct net_device *dev, u32 data)  	}  	/* allow changing only bits set in hw_features */ -	changed = (data ^ dev->wanted_features) & flags_dup_features; +	changed = (data ^ dev->features) & flags_dup_features;  	if (changed & ~dev->hw_features)  		return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP;  	dev->wanted_features = -		(dev->wanted_features & ~changed) | data; +		(dev->wanted_features & ~changed) | (data & dev->hw_features); -	netdev_update_features(dev); +	__netdev_update_features(dev);  	return 0;  } @@ -908,6 +910,9 @@ static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev,  	struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL;  	int ret; +	if (!ops->set_rx_ntuple) +		return -EOPNOTSUPP; +  	if (!(dev->features & NETIF_F_NTUPLE))  		return -EINVAL; @@ -1441,6 +1446,35 @@ static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)  	return dev->ethtool_ops->set_ringparam(dev, &ringparam);  } +static noinline_for_stack int ethtool_get_channels(struct net_device *dev, +						   void __user *useraddr) +{ +	struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS }; + +	if (!dev->ethtool_ops->get_channels) +		return -EOPNOTSUPP; + +	dev->ethtool_ops->get_channels(dev, &channels); + +	if (copy_to_user(useraddr, &channels, sizeof(channels))) +		return -EFAULT; +	return 0; +} + +static noinline_for_stack int ethtool_set_channels(struct net_device *dev, +						   void __user *useraddr) +{ +	struct ethtool_channels channels; + +	if (!dev->ethtool_ops->set_channels) +		return -EOPNOTSUPP; + +	if (copy_from_user(&channels, useraddr, sizeof(channels))) +		return -EFAULT; + +	return dev->ethtool_ops->set_channels(dev, &channels); +} +  static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)  {  	struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM }; @@ -1618,14 +1652,60 @@ out:  static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)  {  	struct ethtool_value id; +	static bool busy; +	int rc; -	if (!dev->ethtool_ops->phys_id) +	if (!dev->ethtool_ops->set_phys_id)  		return -EOPNOTSUPP; +	if (busy) +		return -EBUSY; +  	if (copy_from_user(&id, useraddr, sizeof(id)))  		return -EFAULT; -	return dev->ethtool_ops->phys_id(dev, id.data); +	rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE); +	if (rc < 0) +		return rc; + +	/* Drop the RTNL lock while waiting, but prevent reentry or +	 * removal of the device. +	 */ +	busy = true; +	dev_hold(dev); +	rtnl_unlock(); + +	if (rc == 0) { +		/* Driver will handle this itself */ +		schedule_timeout_interruptible( +			id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT); +	} else { +		/* Driver expects to be called at twice the frequency in rc */ +		int n = rc * 2, i, interval = HZ / n; + +		/* Count down seconds */ +		do { +			/* Count down iterations per second */ +			i = n; +			do { +				rtnl_lock(); +				rc = dev->ethtool_ops->set_phys_id(dev, +				    (i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON); +				rtnl_unlock(); +				if (rc) +					break; +				schedule_timeout_interruptible(interval); +			} while (!signal_pending(current) && --i != 0); +		} while (!signal_pending(current) && +			 (id.data == 0 || --id.data != 0)); +	} + +	rtnl_lock(); +	dev_put(dev); +	busy = false; + +	(void)dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE); +	return rc;  }  static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) @@ -1743,6 +1823,87 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev,  	return dev->ethtool_ops->flash_device(dev, &efl);  } +static int ethtool_set_dump(struct net_device *dev, +			void __user *useraddr) +{ +	struct ethtool_dump dump; + +	if (!dev->ethtool_ops->set_dump) +		return -EOPNOTSUPP; + +	if (copy_from_user(&dump, useraddr, sizeof(dump))) +		return -EFAULT; + +	return dev->ethtool_ops->set_dump(dev, &dump); +} + +static int ethtool_get_dump_flag(struct net_device *dev, +				void __user *useraddr) +{ +	int ret; +	struct ethtool_dump dump; +	const struct ethtool_ops *ops = dev->ethtool_ops; + +	if (!dev->ethtool_ops->get_dump_flag) +		return -EOPNOTSUPP; + +	if (copy_from_user(&dump, useraddr, sizeof(dump))) +		return -EFAULT; + +	ret = ops->get_dump_flag(dev, &dump); +	if (ret) +		return ret; + +	if (copy_to_user(useraddr, &dump, sizeof(dump))) +		return -EFAULT; +	return 0; +} + +static int ethtool_get_dump_data(struct net_device *dev, +				void __user *useraddr) +{ +	int ret; +	__u32 len; +	struct ethtool_dump dump, tmp; +	const struct ethtool_ops *ops = dev->ethtool_ops; +	void *data = NULL; + +	if (!dev->ethtool_ops->get_dump_data || +		!dev->ethtool_ops->get_dump_flag) +		return -EOPNOTSUPP; + +	if (copy_from_user(&dump, useraddr, sizeof(dump))) +		return -EFAULT; + +	memset(&tmp, 0, sizeof(tmp)); +	tmp.cmd = ETHTOOL_GET_DUMP_FLAG; +	ret = ops->get_dump_flag(dev, &tmp); +	if (ret) +		return ret; + +	len = (tmp.len > dump.len) ? dump.len : tmp.len; +	if (!len) +		return -EFAULT; + +	data = vzalloc(tmp.len); +	if (!data) +		return -ENOMEM; +	ret = ops->get_dump_data(dev, &dump, data); +	if (ret) +		goto out; + +	if (copy_to_user(useraddr, &dump, sizeof(dump))) { +		ret = -EFAULT; +		goto out; +	} +	useraddr += offsetof(struct ethtool_dump, data); +	if (copy_to_user(useraddr, data, len)) +		ret = -EFAULT; +out: +	vfree(data); +	return ret; +} +  /* The main entry point in this file.  Called from net/core/dev.c */  int dev_ethtool(struct net *net, struct ifreq *ifr) @@ -1953,6 +2114,21 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)  	case ETHTOOL_SGRO:  		rc = ethtool_set_one_feature(dev, useraddr, ethcmd);  		break; +	case ETHTOOL_GCHANNELS: +		rc = ethtool_get_channels(dev, useraddr); +		break; +	case ETHTOOL_SCHANNELS: +		rc = ethtool_set_channels(dev, useraddr); +		break; +	case ETHTOOL_SET_DUMP: +		rc = ethtool_set_dump(dev, useraddr); +		break; +	case ETHTOOL_GET_DUMP_FLAG: +		rc = ethtool_get_dump_flag(dev, useraddr); +		break; +	case ETHTOOL_GET_DUMP_DATA: +		rc = ethtool_get_dump_data(dev, useraddr); +		break;  	default:  		rc = -EOPNOTSUPP;  	} diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 8248ebb5891..3911586e12e 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -590,7 +590,8 @@ static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb,  	int idx = 0;  	struct fib_rule *rule; -	list_for_each_entry(rule, &ops->rules_list, list) { +	rcu_read_lock(); +	list_for_each_entry_rcu(rule, &ops->rules_list, list) {  		if (idx < cb->args[1])  			goto skip; diff --git a/net/core/filter.c b/net/core/filter.c index afb8afb066b..0eb8c4466ea 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -39,65 +39,6 @@  #include <linux/filter.h>  #include <linux/reciprocal_div.h> -enum { -	BPF_S_RET_K = 1, -	BPF_S_RET_A, -	BPF_S_ALU_ADD_K, -	BPF_S_ALU_ADD_X, -	BPF_S_ALU_SUB_K, -	BPF_S_ALU_SUB_X, -	BPF_S_ALU_MUL_K, -	BPF_S_ALU_MUL_X, -	BPF_S_ALU_DIV_X, -	BPF_S_ALU_AND_K, -	BPF_S_ALU_AND_X, -	BPF_S_ALU_OR_K, -	BPF_S_ALU_OR_X, -	BPF_S_ALU_LSH_K, -	BPF_S_ALU_LSH_X, -	BPF_S_ALU_RSH_K, -	BPF_S_ALU_RSH_X, -	BPF_S_ALU_NEG, -	BPF_S_LD_W_ABS, -	BPF_S_LD_H_ABS, -	BPF_S_LD_B_ABS, -	BPF_S_LD_W_LEN, -	BPF_S_LD_W_IND, -	BPF_S_LD_H_IND, -	BPF_S_LD_B_IND, -	BPF_S_LD_IMM, -	BPF_S_LDX_W_LEN, -	BPF_S_LDX_B_MSH, -	BPF_S_LDX_IMM, -	BPF_S_MISC_TAX, -	BPF_S_MISC_TXA, -	BPF_S_ALU_DIV_K, -	BPF_S_LD_MEM, -	BPF_S_LDX_MEM, -	BPF_S_ST, -	BPF_S_STX, -	BPF_S_JMP_JA, -	BPF_S_JMP_JEQ_K, -	BPF_S_JMP_JEQ_X, -	BPF_S_JMP_JGE_K, -	BPF_S_JMP_JGE_X, -	BPF_S_JMP_JGT_K, -	BPF_S_JMP_JGT_X, -	BPF_S_JMP_JSET_K, -	BPF_S_JMP_JSET_X, -	/* Ancillary data */ -	BPF_S_ANC_PROTOCOL, -	BPF_S_ANC_PKTTYPE, -	BPF_S_ANC_IFINDEX, -	BPF_S_ANC_NLATTR, -	BPF_S_ANC_NLATTR_NEST, -	BPF_S_ANC_MARK, -	BPF_S_ANC_QUEUE, -	BPF_S_ANC_HATYPE, -	BPF_S_ANC_RXHASH, -	BPF_S_ANC_CPU, -}; -  /* No hurry in this branch */  static void *__load_pointer(const struct sk_buff *skb, int k, unsigned int size)  { @@ -145,7 +86,7 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)  	rcu_read_lock();  	filter = rcu_dereference(sk->sk_filter);  	if (filter) { -		unsigned int pkt_len = sk_run_filter(skb, filter->insns); +		unsigned int pkt_len = SK_RUN_FILTER(filter, skb);  		err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;  	} @@ -638,6 +579,7 @@ void sk_filter_release_rcu(struct rcu_head *rcu)  {  	struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); +	bpf_jit_free(fp);  	kfree(fp);  }  EXPORT_SYMBOL(sk_filter_release_rcu); @@ -672,6 +614,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)  	atomic_set(&fp->refcnt, 1);  	fp->len = fprog->len; +	fp->bpf_func = sk_run_filter;  	err = sk_chk_filter(fp->insns, fp->len);  	if (err) { @@ -679,6 +622,8 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)  		return err;  	} +	bpf_jit_compile(fp); +  	old_fp = rcu_dereference_protected(sk->sk_filter,  					   sock_owned_by_user(sk));  	rcu_assign_pointer(sk->sk_filter, fp); diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index 7c2373321b7..43b03dd71e8 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -249,13 +249,6 @@ int gen_new_estimator(struct gnet_stats_basic_packed *bstats,  }  EXPORT_SYMBOL(gen_new_estimator); -static void __gen_kill_estimator(struct rcu_head *head) -{ -	struct gen_estimator *e = container_of(head, -					struct gen_estimator, e_rcu); -	kfree(e); -} -  /**   * gen_kill_estimator - remove a rate estimator   * @bstats: basic statistics @@ -279,7 +272,7 @@ void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,  		write_unlock(&est_lock);  		list_del_rcu(&e->list); -		call_rcu(&e->e_rcu, __gen_kill_estimator); +		kfree_rcu(e, e_rcu);  	}  	spin_unlock_bh(&est_tree_lock);  } diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 5ceb257e860..11b98bc2aa8 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -28,6 +28,7 @@  static const char fmt_hex[] = "%#x\n";  static const char fmt_long_hex[] = "%#lx\n";  static const char fmt_dec[] = "%d\n"; +static const char fmt_udec[] = "%u\n";  static const char fmt_ulong[] = "%lu\n";  static const char fmt_u64[] = "%llu\n"; @@ -145,13 +146,10 @@ static ssize_t show_speed(struct device *dev,  	if (!rtnl_trylock())  		return restart_syscall(); -	if (netif_running(netdev) && -	    netdev->ethtool_ops && -	    netdev->ethtool_ops->get_settings) { -		struct ethtool_cmd cmd = { ETHTOOL_GSET }; - -		if (!netdev->ethtool_ops->get_settings(netdev, &cmd)) -			ret = sprintf(buf, fmt_dec, ethtool_cmd_speed(&cmd)); +	if (netif_running(netdev)) { +		struct ethtool_cmd cmd; +		if (!dev_ethtool_get_settings(netdev, &cmd)) +			ret = sprintf(buf, fmt_udec, ethtool_cmd_speed(&cmd));  	}  	rtnl_unlock();  	return ret; @@ -166,13 +164,11 @@ static ssize_t show_duplex(struct device *dev,  	if (!rtnl_trylock())  		return restart_syscall(); -	if (netif_running(netdev) && -	    netdev->ethtool_ops && -	    netdev->ethtool_ops->get_settings) { -		struct ethtool_cmd cmd = { ETHTOOL_GSET }; - -		if (!netdev->ethtool_ops->get_settings(netdev, &cmd)) -			ret = sprintf(buf, "%s\n", cmd.duplex ? "full" : "half"); +	if (netif_running(netdev)) { +		struct ethtool_cmd cmd; +		if (!dev_ethtool_get_settings(netdev, &cmd)) +			ret = sprintf(buf, "%s\n", +				      cmd.duplex ? "full" : "half");  	}  	rtnl_unlock();  	return ret; @@ -565,13 +561,6 @@ static ssize_t show_rps_map(struct netdev_rx_queue *queue,  	return len;  } -static void rps_map_release(struct rcu_head *rcu) -{ -	struct rps_map *map = container_of(rcu, struct rps_map, rcu); - -	kfree(map); -} -  static ssize_t store_rps_map(struct netdev_rx_queue *queue,  		      struct rx_queue_attribute *attribute,  		      const char *buf, size_t len) @@ -619,7 +608,7 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue,  	spin_unlock(&rps_map_lock);  	if (old_map) -		call_rcu(&old_map->rcu, rps_map_release); +		kfree_rcu(old_map, rcu);  	free_cpumask_var(mask);  	return len; @@ -728,7 +717,7 @@ static void rx_queue_release(struct kobject *kobj)  	map = rcu_dereference_raw(queue->rps_map);  	if (map) {  		RCU_INIT_POINTER(queue->rps_map, NULL); -		call_rcu(&map->rcu, rps_map_release); +		kfree_rcu(map, rcu);  	}  	flow_table = rcu_dereference_raw(queue->rps_flow_table); @@ -898,21 +887,6 @@ static ssize_t show_xps_map(struct netdev_queue *queue,  	return len;  } -static void xps_map_release(struct rcu_head *rcu) -{ -	struct xps_map *map = container_of(rcu, struct xps_map, rcu); - -	kfree(map); -} - -static void xps_dev_maps_release(struct rcu_head *rcu) -{ -	struct xps_dev_maps *dev_maps = -	    container_of(rcu, struct xps_dev_maps, rcu); - -	kfree(dev_maps); -} -  static DEFINE_MUTEX(xps_map_mutex);  #define xmap_dereference(P)		\  	rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex)) @@ -968,7 +942,7 @@ static ssize_t store_xps_map(struct netdev_queue *queue,  		} else  			pos = map_len = alloc_len = 0; -		need_set = cpu_isset(cpu, *mask) && cpu_online(cpu); +		need_set = cpumask_test_cpu(cpu, mask) && cpu_online(cpu);  #ifdef CONFIG_NUMA  		if (need_set) {  			if (numa_node == -2) @@ -1009,7 +983,7 @@ static ssize_t store_xps_map(struct netdev_queue *queue,  		map = dev_maps ?  			xmap_dereference(dev_maps->cpu_map[cpu]) : NULL;  		if (map && xmap_dereference(new_dev_maps->cpu_map[cpu]) != map) -			call_rcu(&map->rcu, xps_map_release); +			kfree_rcu(map, rcu);  		if (new_dev_maps->cpu_map[cpu])  			nonempty = 1;  	} @@ -1022,7 +996,7 @@ static ssize_t store_xps_map(struct netdev_queue *queue,  	}  	if (dev_maps) -		call_rcu(&dev_maps->rcu, xps_dev_maps_release); +		kfree_rcu(dev_maps, rcu);  	netdev_queue_numa_node_write(queue, (numa_node >= 0) ? numa_node :  					    NUMA_NO_NODE); @@ -1084,7 +1058,7 @@ static void netdev_queue_release(struct kobject *kobj)  				else {  					RCU_INIT_POINTER(dev_maps->cpu_map[i],  					    NULL); -					call_rcu(&map->rcu, xps_map_release); +					kfree_rcu(map, rcu);  					map = NULL;  				}  			} @@ -1094,7 +1068,7 @@ static void netdev_queue_release(struct kobject *kobj)  		if (!nonempty) {  			RCU_INIT_POINTER(dev->xps_maps, NULL); -			call_rcu(&dev_maps->rcu, xps_dev_maps_release); +			kfree_rcu(dev_maps, rcu);  		}  	} diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 3f860261c5e..2e2dce6583e 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -27,14 +27,6 @@ EXPORT_SYMBOL(init_net);  #define INITIAL_NET_GEN_PTRS	13 /* +1 for len +2 for rcu_head */ -static void net_generic_release(struct rcu_head *rcu) -{ -	struct net_generic *ng; - -	ng = container_of(rcu, struct net_generic, rcu); -	kfree(ng); -} -  static int net_assign_generic(struct net *net, int id, void *data)  {  	struct net_generic *ng, *old_ng; @@ -68,7 +60,7 @@ static int net_assign_generic(struct net *net, int id, void *data)  	memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*));  	rcu_assign_pointer(net->gen, ng); -	call_rcu(&old_ng->rcu, net_generic_release); +	kfree_rcu(old_ng, rcu);  assign:  	ng->ptr[id - 1] = data;  	return 0; @@ -216,11 +208,14 @@ static void net_free(struct net *net)  	kmem_cache_free(net_cachep, net);  } -static struct net *net_create(void) +struct net *copy_net_ns(unsigned long flags, struct net *old_net)  {  	struct net *net;  	int rv; +	if (!(flags & CLONE_NEWNET)) +		return get_net(old_net); +  	net = net_alloc();  	if (!net)  		return ERR_PTR(-ENOMEM); @@ -239,13 +234,6 @@ static struct net *net_create(void)  	return net;  } -struct net *copy_net_ns(unsigned long flags, struct net *old_net) -{ -	if (!(flags & CLONE_NEWNET)) -		return get_net(old_net); -	return net_create(); -} -  static DEFINE_SPINLOCK(cleanup_list_lock);  static LIST_HEAD(cleanup_list);  /* Must hold cleanup_list_lock to touch */ diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 06be2431753..2d7d6d47378 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -539,7 +539,7 @@ int __netpoll_rx(struct sk_buff *skb)  {  	int proto, len, ulen;  	int hits = 0; -	struct iphdr *iph; +	const struct iphdr *iph;  	struct udphdr *uh;  	struct netpoll_info *npinfo = skb->dev->npinfo;  	struct netpoll *np, *tmp; @@ -698,32 +698,8 @@ int netpoll_parse_options(struct netpoll *np, char *opt)  	if (*cur != 0) {  		/* MAC address */ -		if ((delim = strchr(cur, ':')) == NULL) -			goto parse_failed; -		*delim = 0; -		np->remote_mac[0] = simple_strtol(cur, NULL, 16); -		cur = delim + 1; -		if ((delim = strchr(cur, ':')) == NULL) -			goto parse_failed; -		*delim = 0; -		np->remote_mac[1] = simple_strtol(cur, NULL, 16); -		cur = delim + 1; -		if ((delim = strchr(cur, ':')) == NULL) +		if (!mac_pton(cur, np->remote_mac))  			goto parse_failed; -		*delim = 0; -		np->remote_mac[2] = simple_strtol(cur, NULL, 16); -		cur = delim + 1; -		if ((delim = strchr(cur, ':')) == NULL) -			goto parse_failed; -		*delim = 0; -		np->remote_mac[3] = simple_strtol(cur, NULL, 16); -		cur = delim + 1; -		if ((delim = strchr(cur, ':')) == NULL) -			goto parse_failed; -		*delim = 0; -		np->remote_mac[4] = simple_strtol(cur, NULL, 16); -		cur = delim + 1; -		np->remote_mac[5] = simple_strtol(cur, NULL, 16);  	}  	netpoll_print_options(np); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index aeeece72b72..f76079cd750 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -156,6 +156,7 @@  #include <linux/wait.h>  #include <linux/etherdevice.h>  #include <linux/kthread.h> +#include <linux/prefetch.h>  #include <net/net_namespace.h>  #include <net/checksum.h>  #include <net/ipv6.h> @@ -449,7 +450,6 @@ static void pktgen_stop(struct pktgen_thread *t);  static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);  static unsigned int scan_ip6(const char *s, char ip[16]); -static unsigned int fmt_ip6(char *s, const char ip[16]);  /* Module parameters, defaults. */  static int pg_count_d __read_mostly = 1000; @@ -556,21 +556,13 @@ static int pktgen_if_show(struct seq_file *seq, void *v)  			   pkt_dev->skb_priority);  	if (pkt_dev->flags & F_IPV6) { -		char b1[128], b2[128], b3[128]; -		fmt_ip6(b1, pkt_dev->in6_saddr.s6_addr); -		fmt_ip6(b2, pkt_dev->min_in6_saddr.s6_addr); -		fmt_ip6(b3, pkt_dev->max_in6_saddr.s6_addr);  		seq_printf(seq, -			   "     saddr: %s  min_saddr: %s  max_saddr: %s\n", b1, -			   b2, b3); - -		fmt_ip6(b1, pkt_dev->in6_daddr.s6_addr); -		fmt_ip6(b2, pkt_dev->min_in6_daddr.s6_addr); -		fmt_ip6(b3, pkt_dev->max_in6_daddr.s6_addr); -		seq_printf(seq, -			   "     daddr: %s  min_daddr: %s  max_daddr: %s\n", b1, -			   b2, b3); - +			   "     saddr: %pI6c  min_saddr: %pI6c  max_saddr: %pI6c\n" +			   "     daddr: %pI6c  min_daddr: %pI6c  max_daddr: %pI6c\n", +			   &pkt_dev->in6_saddr, +			   &pkt_dev->min_in6_saddr, &pkt_dev->max_in6_saddr, +			   &pkt_dev->in6_daddr, +			   &pkt_dev->min_in6_daddr, &pkt_dev->max_in6_daddr);  	} else {  		seq_printf(seq,  			   "     dst_min: %s  dst_max: %s\n", @@ -706,10 +698,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)  		   pkt_dev->cur_src_mac_offset);  	if (pkt_dev->flags & F_IPV6) { -		char b1[128], b2[128]; -		fmt_ip6(b1, pkt_dev->cur_in6_daddr.s6_addr); -		fmt_ip6(b2, pkt_dev->cur_in6_saddr.s6_addr); -		seq_printf(seq, "     cur_saddr: %s  cur_daddr: %s\n", b2, b1); +		seq_printf(seq, "     cur_saddr: %pI6c  cur_daddr: %pI6c\n", +				&pkt_dev->cur_in6_saddr, +				&pkt_dev->cur_in6_daddr);  	} else  		seq_printf(seq, "     cur_saddr: 0x%x  cur_daddr: 0x%x\n",  			   pkt_dev->cur_saddr, pkt_dev->cur_daddr); @@ -1309,7 +1300,7 @@ static ssize_t pktgen_if_write(struct file *file,  		buf[len] = 0;  		scan_ip6(buf, pkt_dev->in6_daddr.s6_addr); -		fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr); +		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_daddr);  		ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->in6_daddr); @@ -1332,7 +1323,7 @@ static ssize_t pktgen_if_write(struct file *file,  		buf[len] = 0;  		scan_ip6(buf, pkt_dev->min_in6_daddr.s6_addr); -		fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr); +		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->min_in6_daddr);  		ipv6_addr_copy(&pkt_dev->cur_in6_daddr,  			       &pkt_dev->min_in6_daddr); @@ -1355,7 +1346,7 @@ static ssize_t pktgen_if_write(struct file *file,  		buf[len] = 0;  		scan_ip6(buf, pkt_dev->max_in6_daddr.s6_addr); -		fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr); +		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->max_in6_daddr);  		if (debug)  			printk(KERN_DEBUG "pktgen: dst6_max set to: %s\n", buf); @@ -1376,7 +1367,7 @@ static ssize_t pktgen_if_write(struct file *file,  		buf[len] = 0;  		scan_ip6(buf, pkt_dev->in6_saddr.s6_addr); -		fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr); +		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_saddr);  		ipv6_addr_copy(&pkt_dev->cur_in6_saddr, &pkt_dev->in6_saddr); @@ -1430,11 +1421,6 @@ static ssize_t pktgen_if_write(struct file *file,  		return count;  	}  	if (!strcmp(name, "dst_mac")) { -		char *v = valstr; -		unsigned char old_dmac[ETH_ALEN]; -		unsigned char *m = pkt_dev->dst_mac; -		memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN); -  		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);  		if (len < 0)  			return len; @@ -1442,35 +1428,16 @@ static ssize_t pktgen_if_write(struct file *file,  		memset(valstr, 0, sizeof(valstr));  		if (copy_from_user(valstr, &user_buffer[i], len))  			return -EFAULT; -		i += len; - -		for (*m = 0; *v && m < pkt_dev->dst_mac + 6; v++) { -			int value; - -			value = hex_to_bin(*v); -			if (value >= 0) -				*m = *m * 16 + value; - -			if (*v == ':') { -				m++; -				*m = 0; -			} -		} +		if (!mac_pton(valstr, pkt_dev->dst_mac)) +			return -EINVAL;  		/* Set up Dest MAC */ -		if (compare_ether_addr(old_dmac, pkt_dev->dst_mac)) -			memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN); +		memcpy(&pkt_dev->hh[0], pkt_dev->dst_mac, ETH_ALEN); -		sprintf(pg_result, "OK: dstmac"); +		sprintf(pg_result, "OK: dstmac %pM", pkt_dev->dst_mac);  		return count;  	}  	if (!strcmp(name, "src_mac")) { -		char *v = valstr; -		unsigned char old_smac[ETH_ALEN]; -		unsigned char *m = pkt_dev->src_mac; - -		memcpy(old_smac, pkt_dev->src_mac, ETH_ALEN); -  		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);  		if (len < 0)  			return len; @@ -1478,26 +1445,13 @@ static ssize_t pktgen_if_write(struct file *file,  		memset(valstr, 0, sizeof(valstr));  		if (copy_from_user(valstr, &user_buffer[i], len))  			return -EFAULT; -		i += len; - -		for (*m = 0; *v && m < pkt_dev->src_mac + 6; v++) { -			int value; - -			value = hex_to_bin(*v); -			if (value >= 0) -				*m = *m * 16 + value; - -			if (*v == ':') { -				m++; -				*m = 0; -			} -		} +		if (!mac_pton(valstr, pkt_dev->src_mac)) +			return -EINVAL;  		/* Set up Src MAC */ -		if (compare_ether_addr(old_smac, pkt_dev->src_mac)) -			memcpy(&(pkt_dev->hh[6]), pkt_dev->src_mac, ETH_ALEN); +		memcpy(&pkt_dev->hh[6], pkt_dev->src_mac, ETH_ALEN); -		sprintf(pg_result, "OK: srcmac"); +		sprintf(pg_result, "OK: srcmac %pM", pkt_dev->src_mac);  		return count;  	} @@ -2514,7 +2468,6 @@ static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)  {  	struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;  	int err = 0; -	struct iphdr *iph;  	if (!x)  		return 0; @@ -2524,7 +2477,6 @@ static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)  		return 0;  	spin_lock(&x->lock); -	iph = ip_hdr(skb);  	err = x->outer_mode->output(x, skb);  	if (err) @@ -2624,6 +2576,7 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,  	} else {  		int frags = pkt_dev->nfrags;  		int i, len; +		int frag_len;  		if (frags > MAX_SKB_FRAGS) @@ -2635,6 +2588,8 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,  		}  		i = 0; +		frag_len = (datalen/frags) < PAGE_SIZE ? +			   (datalen/frags) : PAGE_SIZE;  		while (datalen > 0) {  			if (unlikely(!pkt_dev->page)) {  				int node = numa_node_id(); @@ -2648,38 +2603,18 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,  			skb_shinfo(skb)->frags[i].page = pkt_dev->page;  			get_page(pkt_dev->page);  			skb_shinfo(skb)->frags[i].page_offset = 0; -			skb_shinfo(skb)->frags[i].size = -			    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE); +			/*last fragment, fill rest of data*/ +			if (i == (frags - 1)) +				skb_shinfo(skb)->frags[i].size = +				    (datalen < PAGE_SIZE ? datalen : PAGE_SIZE); +			else +				skb_shinfo(skb)->frags[i].size = frag_len;  			datalen -= skb_shinfo(skb)->frags[i].size;  			skb->len += skb_shinfo(skb)->frags[i].size;  			skb->data_len += skb_shinfo(skb)->frags[i].size;  			i++;  			skb_shinfo(skb)->nr_frags = i;  		} - -		while (i < frags) { -			int rem; - -			if (i == 0) -				break; - -			rem = skb_shinfo(skb)->frags[i - 1].size / 2; -			if (rem == 0) -				break; - -			skb_shinfo(skb)->frags[i - 1].size -= rem; - -			skb_shinfo(skb)->frags[i] = -			    skb_shinfo(skb)->frags[i - 1]; -			get_page(skb_shinfo(skb)->frags[i].page); -			skb_shinfo(skb)->frags[i].page = -			    skb_shinfo(skb)->frags[i - 1].page; -			skb_shinfo(skb)->frags[i].page_offset += -			    skb_shinfo(skb)->frags[i - 1].size; -			skb_shinfo(skb)->frags[i].size = rem; -			i++; -			skb_shinfo(skb)->nr_frags = i; -		}  	}  	/* Stamp the time, and sequence number, @@ -2917,79 +2852,6 @@ static unsigned int scan_ip6(const char *s, char ip[16])  	return len;  } -static char tohex(char hexdigit) -{ -	return hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0'; -} - -static int fmt_xlong(char *s, unsigned int i) -{ -	char *bak = s; -	*s = tohex((i >> 12) & 0xf); -	if (s != bak || *s != '0') -		++s; -	*s = tohex((i >> 8) & 0xf); -	if (s != bak || *s != '0') -		++s; -	*s = tohex((i >> 4) & 0xf); -	if (s != bak || *s != '0') -		++s; -	*s = tohex(i & 0xf); -	return s - bak + 1; -} - -static unsigned int fmt_ip6(char *s, const char ip[16]) -{ -	unsigned int len; -	unsigned int i; -	unsigned int temp; -	unsigned int compressing; -	int j; - -	len = 0; -	compressing = 0; -	for (j = 0; j < 16; j += 2) { - -#ifdef V4MAPPEDPREFIX -		if (j == 12 && !memcmp(ip, V4mappedprefix, 12)) { -			inet_ntoa_r(*(struct in_addr *)(ip + 12), s); -			temp = strlen(s); -			return len + temp; -		} -#endif -		temp = ((unsigned long)(unsigned char)ip[j] << 8) + -		    (unsigned long)(unsigned char)ip[j + 1]; -		if (temp == 0) { -			if (!compressing) { -				compressing = 1; -				if (j == 0) { -					*s++ = ':'; -					++len; -				} -			} -		} else { -			if (compressing) { -				compressing = 0; -				*s++ = ':'; -				++len; -			} -			i = fmt_xlong(s, temp); -			len += i; -			s += i; -			if (j < 14) { -				*s++ = ':'; -				++len; -			} -		} -	} -	if (compressing) { -		*s++ = ':'; -		++len; -	} -	*s = 0; -	return len; -} -  static struct sk_buff *fill_packet_ipv6(struct net_device *odev,  					struct pktgen_dev *pkt_dev)  { @@ -3682,13 +3544,12 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)  		return -ENOMEM;  	strcpy(pkt_dev->odevname, ifname); -	pkt_dev->flows = vmalloc_node(MAX_CFLOWS * sizeof(struct flow_state), +	pkt_dev->flows = vzalloc_node(MAX_CFLOWS * sizeof(struct flow_state),  				      node);  	if (pkt_dev->flows == NULL) {  		kfree(pkt_dev);  		return -ENOMEM;  	} -	memset(pkt_dev->flows, 0, MAX_CFLOWS * sizeof(struct flow_state));  	pkt_dev->removal_mark = 0;  	pkt_dev->min_pkt_size = ETH_ZLEN; @@ -3846,6 +3707,7 @@ static int __init pg_init(void)  {  	int cpu;  	struct proc_dir_entry *pe; +	int ret = 0;  	pr_info("%s", version); @@ -3856,11 +3718,10 @@ static int __init pg_init(void)  	pe = proc_create(PGCTRL, 0600, pg_proc_dir, &pktgen_fops);  	if (pe == NULL) {  		pr_err("ERROR: cannot create %s procfs entry\n", PGCTRL); -		proc_net_remove(&init_net, PG_PROC_DIR); -		return -EINVAL; +		ret = -EINVAL; +		goto remove_dir;  	} -	/* Register us to receive netdevice events */  	register_netdevice_notifier(&pktgen_notifier_block);  	for_each_online_cpu(cpu) { @@ -3874,13 +3735,18 @@ static int __init pg_init(void)  	if (list_empty(&pktgen_threads)) {  		pr_err("ERROR: Initialization failed for all threads\n"); -		unregister_netdevice_notifier(&pktgen_notifier_block); -		remove_proc_entry(PGCTRL, pg_proc_dir); -		proc_net_remove(&init_net, PG_PROC_DIR); -		return -ENODEV; +		ret = -ENODEV; +		goto unregister;  	}  	return 0; + + unregister: +	unregister_netdevice_notifier(&pktgen_notifier_block); +	remove_proc_entry(PGCTRL, pg_proc_dir); + remove_dir: +	proc_net_remove(&init_net, PG_PROC_DIR); +	return ret;  }  static void __exit pg_cleanup(void) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d7c4bb4b182..d1644e317e7 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1007,10 +1007,11 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)  	s_h = cb->args[0];  	s_idx = cb->args[1]; +	rcu_read_lock();  	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {  		idx = 0;  		head = &net->dev_index_head[h]; -		hlist_for_each_entry(dev, node, head, index_hlist) { +		hlist_for_each_entry_rcu(dev, node, head, index_hlist) {  			if (idx < s_idx)  				goto cont;  			if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, @@ -1023,6 +1024,7 @@ cont:  		}  	}  out: +	rcu_read_unlock();  	cb->args[1] = idx;  	cb->args[0] = h; @@ -1499,6 +1501,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)  	char ifname[IFNAMSIZ];  	struct nlattr *tb[IFLA_MAX+1];  	int err; +	LIST_HEAD(list_kill);  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);  	if (err < 0) @@ -1522,7 +1525,9 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)  	if (!ops)  		return -EOPNOTSUPP; -	ops->dellink(dev, NULL); +	ops->dellink(dev, &list_kill); +	unregister_netdevice_many(&list_kill); +	list_del(&list_kill);  	return 0;  } @@ -1570,12 +1575,6 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net,  	dev->rtnl_link_state = RTNL_LINK_INITIALIZING;  	dev->real_num_tx_queues = real_num_queues; -	if (strchr(dev->name, '%')) { -		err = dev_alloc_name(dev, dev->name); -		if (err < 0) -			goto err_free; -	} -  	if (tb[IFLA_MTU])  		dev->mtu = nla_get_u32(tb[IFLA_MTU]);  	if (tb[IFLA_ADDRESS]) @@ -1595,8 +1594,6 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net,  	return dev; -err_free: -	free_netdev(dev);  err:  	return ERR_PTR(err);  } @@ -1879,7 +1876,6 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  	int min_len;  	int family;  	int type; -	int err;  	type = nlh->nlmsg_type;  	if (type > RTM_MAX) @@ -1906,11 +1902,8 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  		if (dumpit == NULL)  			return -EOPNOTSUPP; -		__rtnl_unlock();  		rtnl = net->rtnl; -		err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL); -		rtnl_lock(); -		return err; +		return netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);  	}  	memset(rta_buf, 0, (rtattr_max * sizeof(struct rtattr *))); @@ -1963,6 +1956,8 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi  	case NETDEV_GOING_DOWN:  	case NETDEV_UNREGISTER:  	case NETDEV_UNREGISTER_BATCH: +	case NETDEV_RELEASE: +	case NETDEV_JOIN:  		break;  	default:  		rtmsg_ifinfo(RTM_NEWLINK, dev, 0); @@ -1980,7 +1975,7 @@ static int __net_init rtnetlink_net_init(struct net *net)  {  	struct sock *sk;  	sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX, -				   rtnetlink_rcv, &rtnl_mutex, THIS_MODULE); +				   rtnetlink_rcv, NULL, THIS_MODULE);  	if (!sk)  		return -ENOMEM;  	net->rtnl = sk; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7ebeed0a877..46cbd28f40f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -57,6 +57,7 @@  #include <linux/init.h>  #include <linux/scatterlist.h>  #include <linux/errqueue.h> +#include <linux/prefetch.h>  #include <net/protocol.h>  #include <net/dst.h> @@ -2993,6 +2994,9 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)  	skb->destructor = sock_rmem_free;  	atomic_add(skb->truesize, &sk->sk_rmem_alloc); +	/* before exiting rcu section, make sure dst is refcounted */ +	skb_dst_force(skb); +  	skb_queue_tail(&sk->sk_error_queue, skb);  	if (!sock_flag(sk, SOCK_DEAD))  		sk->sk_data_ready(sk, skb->len); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 385b6095fdc..a829e3f60ae 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -122,6 +122,15 @@ static struct ctl_table net_core_table[] = {  		.mode		= 0644,  		.proc_handler	= proc_dointvec  	}, +#ifdef CONFIG_BPF_JIT +	{ +		.procname	= "bpf_jit_enable", +		.data		= &bpf_jit_enable, +		.maxlen		= sizeof(int), +		.mode		= 0644, +		.proc_handler	= proc_dointvec +	}, +#endif  	{  		.procname	= "netdev_tstamp_prequeue",  		.data		= &netdev_tstamp_prequeue, diff --git a/net/core/utils.c b/net/core/utils.c index 5fea0ab2190..2012bc797f9 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -296,3 +296,27 @@ void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,  				csum_unfold(*sum)));  }  EXPORT_SYMBOL(inet_proto_csum_replace4); + +int mac_pton(const char *s, u8 *mac) +{ +	int i; + +	/* XX:XX:XX:XX:XX:XX */ +	if (strlen(s) < 3 * ETH_ALEN - 1) +		return 0; + +	/* Don't dirty result unless string is valid MAC. */ +	for (i = 0; i < ETH_ALEN; i++) { +		if (!strchr("0123456789abcdefABCDEF", s[i * 3])) +			return 0; +		if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1])) +			return 0; +		if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':') +			return 0; +	} +	for (i = 0; i < ETH_ALEN; i++) { +		mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]); +	} +	return 1; +} +EXPORT_SYMBOL(mac_pton);  |