diff options
Diffstat (limited to 'net/core/dev.c')
| -rw-r--r-- | net/core/dev.c | 137 | 
1 files changed, 110 insertions, 27 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 416137c64bf8..7098fba52be1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -105,6 +105,7 @@  #include <net/dst.h>  #include <net/dst_metadata.h>  #include <net/pkt_sched.h> +#include <net/pkt_cls.h>  #include <net/checksum.h>  #include <net/xfrm.h>  #include <linux/highmem.h> @@ -142,6 +143,7 @@  #include <linux/hrtimer.h>  #include <linux/netfilter_ingress.h>  #include <linux/crash_dump.h> +#include <linux/sctp.h>  #include "net-sysfs.h" @@ -161,6 +163,7 @@ static int netif_rx_internal(struct sk_buff *skb);  static int call_netdevice_notifiers_info(unsigned long val,  					 struct net_device *dev,  					 struct netdev_notifier_info *info); +static struct napi_struct *napi_by_id(unsigned int napi_id);  /*   * The @dev_base_head list is protected by @dev_base_lock and the rtnl @@ -865,6 +868,31 @@ struct net_device *dev_get_by_index(struct net *net, int ifindex)  EXPORT_SYMBOL(dev_get_by_index);  /** + *	dev_get_by_napi_id - find a device by napi_id + *	@napi_id: ID of the NAPI struct + * + *	Search for an interface by NAPI ID. Returns %NULL if the device + *	is not found or a pointer to the device. The device has not had + *	its reference counter increased so the caller must be careful + *	about locking. The caller must hold RCU lock. + */ + +struct net_device *dev_get_by_napi_id(unsigned int napi_id) +{ +	struct napi_struct *napi; + +	WARN_ON_ONCE(!rcu_read_lock_held()); + +	if (napi_id < MIN_NAPI_ID) +		return NULL; + +	napi = napi_by_id(napi_id); + +	return napi ? napi->dev : NULL; +} +EXPORT_SYMBOL(dev_get_by_napi_id); + +/**   *	netdev_get_name - get a netdevice name, knowing its ifindex.   *	@net: network namespace   *	@name: a pointer to the buffer where the name will be stored. @@ -1834,7 +1862,7 @@ static inline int deliver_skb(struct sk_buff *skb,  {  	if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))  		return -ENOMEM; -	atomic_inc(&skb->users); +	refcount_inc(&skb->users);  	return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);  } @@ -2456,10 +2484,10 @@ void __dev_kfree_skb_irq(struct sk_buff *skb, enum skb_free_reason reason)  	if (unlikely(!skb))  		return; -	if (likely(atomic_read(&skb->users) == 1)) { +	if (likely(refcount_read(&skb->users) == 1)) {  		smp_rmb(); -		atomic_set(&skb->users, 0); -	} else if (likely(!atomic_dec_and_test(&skb->users))) { +		refcount_set(&skb->users, 0); +	} else if (likely(!refcount_dec_and_test(&skb->users))) {  		return;  	}  	get_kfree_skb_cb(skb)->reason = reason; @@ -2612,6 +2640,47 @@ out:  }  EXPORT_SYMBOL(skb_checksum_help); +int skb_crc32c_csum_help(struct sk_buff *skb) +{ +	__le32 crc32c_csum; +	int ret = 0, offset, start; + +	if (skb->ip_summed != CHECKSUM_PARTIAL) +		goto out; + +	if (unlikely(skb_is_gso(skb))) +		goto out; + +	/* Before computing a checksum, we should make sure no frag could +	 * be modified by an external entity : checksum could be wrong. +	 */ +	if (unlikely(skb_has_shared_frag(skb))) { +		ret = __skb_linearize(skb); +		if (ret) +			goto out; +	} +	start = skb_checksum_start_offset(skb); +	offset = start + offsetof(struct sctphdr, checksum); +	if (WARN_ON_ONCE(offset >= skb_headlen(skb))) { +		ret = -EINVAL; +		goto out; +	} +	if (skb_cloned(skb) && +	    !skb_clone_writable(skb, offset + sizeof(__le32))) { +		ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); +		if (ret) +			goto out; +	} +	crc32c_csum = cpu_to_le32(~__skb_checksum(skb, start, +						  skb->len - start, ~(__u32)0, +						  crc32c_csum_stub)); +	*(__le32 *)(skb->data + offset) = crc32c_csum; +	skb->ip_summed = CHECKSUM_NONE; +	skb->csum_not_inet = 0; +out: +	return ret; +} +  __be16 skb_network_protocol(struct sk_buff *skb, int *depth)  {  	__be16 type = skb->protocol; @@ -2954,6 +3023,17 @@ static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,  	return skb;  } +int skb_csum_hwoffload_help(struct sk_buff *skb, +			    const netdev_features_t features) +{ +	if (unlikely(skb->csum_not_inet)) +		return !!(features & NETIF_F_SCTP_CRC) ? 0 : +			skb_crc32c_csum_help(skb); + +	return !!(features & NETIF_F_CSUM_MASK) ? 0 : skb_checksum_help(skb); +} +EXPORT_SYMBOL(skb_csum_hwoffload_help); +  static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev)  {  	netdev_features_t features; @@ -2992,8 +3072,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device  			else  				skb_set_transport_header(skb,  							 skb_checksum_start_offset(skb)); -			if (!(features & NETIF_F_CSUM_MASK) && -			    skb_checksum_help(skb)) +			if (skb_csum_hwoffload_help(skb, features))  				goto out_kfree_skb;  		}  	} @@ -3179,7 +3258,7 @@ sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev)  	/* qdisc_skb_cb(skb)->pkt_len was already set by the caller. */  	qdisc_bstats_cpu_update(cl->q, skb); -	switch (tc_classify(skb, cl, &cl_res, false)) { +	switch (tcf_classify(skb, cl, &cl_res, false)) {  	case TC_ACT_OK:  	case TC_ACT_RECLASSIFY:  		skb->tc_index = TC_H_MIN(cl_res.classid); @@ -3191,6 +3270,7 @@ sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev)  		return NULL;  	case TC_ACT_STOLEN:  	case TC_ACT_QUEUED: +	case TC_ACT_TRAP:  		*ret = NET_XMIT_SUCCESS;  		consume_skb(skb);  		return NULL; @@ -3875,7 +3955,7 @@ static __latent_entropy void net_tx_action(struct softirq_action *h)  			clist = clist->next; -			WARN_ON(atomic_read(&skb->users)); +			WARN_ON(refcount_read(&skb->users));  			if (likely(get_kfree_skb_cb(skb)->reason == SKB_REASON_CONSUMED))  				trace_consume_skb(skb);  			else @@ -3949,7 +4029,7 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret,  	skb->tc_at_ingress = 1;  	qdisc_bstats_cpu_update(cl->q, skb); -	switch (tc_classify(skb, cl, &cl_res, false)) { +	switch (tcf_classify(skb, cl, &cl_res, false)) {  	case TC_ACT_OK:  	case TC_ACT_RECLASSIFY:  		skb->tc_index = TC_H_MIN(cl_res.classid); @@ -3960,6 +4040,7 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret,  		return NULL;  	case TC_ACT_STOLEN:  	case TC_ACT_QUEUED: +	case TC_ACT_TRAP:  		consume_skb(skb);  		return NULL;  	case TC_ACT_REDIRECT: @@ -4261,13 +4342,12 @@ static struct static_key generic_xdp_needed __read_mostly;  static int generic_xdp_install(struct net_device *dev, struct netdev_xdp *xdp)  { +	struct bpf_prog *old = rtnl_dereference(dev->xdp_prog);  	struct bpf_prog *new = xdp->prog;  	int ret = 0;  	switch (xdp->command) { -	case XDP_SETUP_PROG: { -		struct bpf_prog *old = rtnl_dereference(dev->xdp_prog); - +	case XDP_SETUP_PROG:  		rcu_assign_pointer(dev->xdp_prog, new);  		if (old)  			bpf_prog_put(old); @@ -4279,10 +4359,10 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_xdp *xdp)  			dev_disable_lro(dev);  		}  		break; -	}  	case XDP_QUERY_PROG: -		xdp->prog_attached = !!rcu_access_pointer(dev->xdp_prog); +		xdp->prog_attached = !!old; +		xdp->prog_id = old ? old->aux->id : 0;  		break;  	default: @@ -4637,9 +4717,6 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff  	if (netif_elide_gro(skb->dev))  		goto normal; -	if (skb->csum_bad) -		goto normal; -  	gro_list_prepare(napi, skb);  	rcu_read_lock(); @@ -6867,7 +6944,7 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down)  }  EXPORT_SYMBOL(dev_change_proto_down); -bool __dev_xdp_attached(struct net_device *dev, xdp_op_t xdp_op) +u8 __dev_xdp_attached(struct net_device *dev, xdp_op_t xdp_op, u32 *prog_id)  {  	struct netdev_xdp xdp; @@ -6876,18 +6953,25 @@ bool __dev_xdp_attached(struct net_device *dev, xdp_op_t xdp_op)  	/* Query must always succeed. */  	WARN_ON(xdp_op(dev, &xdp) < 0); +	if (prog_id) +		*prog_id = xdp.prog_id; +  	return xdp.prog_attached;  }  static int dev_xdp_install(struct net_device *dev, xdp_op_t xdp_op, -			   struct netlink_ext_ack *extack, +			   struct netlink_ext_ack *extack, u32 flags,  			   struct bpf_prog *prog)  {  	struct netdev_xdp xdp;  	memset(&xdp, 0, sizeof(xdp)); -	xdp.command = XDP_SETUP_PROG; +	if (flags & XDP_FLAGS_HW_MODE) +		xdp.command = XDP_SETUP_PROG_HW; +	else +		xdp.command = XDP_SETUP_PROG;  	xdp.extack = extack; +	xdp.flags = flags;  	xdp.prog = prog;  	return xdp_op(dev, &xdp); @@ -6913,7 +6997,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,  	ASSERT_RTNL();  	xdp_op = xdp_chk = ops->ndo_xdp; -	if (!xdp_op && (flags & XDP_FLAGS_DRV_MODE)) +	if (!xdp_op && (flags & (XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE)))  		return -EOPNOTSUPP;  	if (!xdp_op || (flags & XDP_FLAGS_SKB_MODE))  		xdp_op = generic_xdp_install; @@ -6921,10 +7005,10 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,  		xdp_chk = generic_xdp_install;  	if (fd >= 0) { -		if (xdp_chk && __dev_xdp_attached(dev, xdp_chk)) +		if (xdp_chk && __dev_xdp_attached(dev, xdp_chk, NULL))  			return -EEXIST;  		if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && -		    __dev_xdp_attached(dev, xdp_op)) +		    __dev_xdp_attached(dev, xdp_op, NULL))  			return -EBUSY;  		prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP); @@ -6932,7 +7016,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,  			return PTR_ERR(prog);  	} -	err = dev_xdp_install(dev, xdp_op, extack, prog); +	err = dev_xdp_install(dev, xdp_op, extack, flags, prog);  	if (err < 0 && prog)  		bpf_prog_put(prog); @@ -7023,7 +7107,7 @@ static void rollback_registered_many(struct list_head *head)  		if (!dev->rtnl_link_ops ||  		    dev->rtnl_link_state == RTNL_LINK_INITIALIZED) -			skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, +			skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0,  						     GFP_KERNEL);  		/* @@ -7751,7 +7835,7 @@ void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,  {  #if BITS_PER_LONG == 64  	BUILD_BUG_ON(sizeof(*stats64) < sizeof(*netdev_stats)); -	memcpy(stats64, netdev_stats, sizeof(*stats64)); +	memcpy(stats64, netdev_stats, sizeof(*netdev_stats));  	/* zero out counters that only exist in rtnl_link_stats64 */  	memset((char *)stats64 + sizeof(*netdev_stats), 0,  	       sizeof(*stats64) - sizeof(*netdev_stats)); @@ -8608,7 +8692,6 @@ static int __init net_dev_init(void)  	rc = cpuhp_setup_state_nocalls(CPUHP_NET_DEV_DEAD, "net/dev:dead",  				       NULL, dev_cpu_dead);  	WARN_ON(rc < 0); -	dst_subsys_init();  	rc = 0;  out:  	return rc;  | 
