diff options
| author | Mark Brown <broonie@kernel.org> | 2020-08-25 11:01:46 +0100 | 
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2020-08-25 11:01:46 +0100 | 
| commit | 3bec5b6aae830355e786e204b20a7cea38c3a8ed (patch) | |
| tree | fd597b87faf55ceb2a207ee94f4feca6276696db /net/core/dev.c | |
| parent | a577f3456c0a2fac3dee037c483753e6e68f3e49 (diff) | |
| parent | d012a7190fc1fd72ed48911e77ca97ba4521bccd (diff) | |
Merge tag 'v5.9-rc2' into regulator-5.9
Linux 5.9-rc2
Diffstat (limited to 'net/core/dev.c')
| -rw-r--r-- | net/core/dev.c | 594 | 
1 files changed, 459 insertions, 135 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 6bc2388141f6..d42c9ea0c3c0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -143,6 +143,7 @@  #include <linux/net_namespace.h>  #include <linux/indirect_call_wrapper.h>  #include <net/devlink.h> +#include <linux/pm_runtime.h>  #include "net-sysfs.h" @@ -1492,8 +1493,13 @@ static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)  	ASSERT_RTNL(); -	if (!netif_device_present(dev)) -		return -ENODEV; +	if (!netif_device_present(dev)) { +		/* may be detached because parent is runtime-suspended */ +		if (dev->dev.parent) +			pm_runtime_resume(dev->dev.parent); +		if (!netif_device_present(dev)) +			return -ENODEV; +	}  	/* Block netpoll from trying to do any rx path servicing.  	 * If we don't do this there is a chance ndo_poll_controller @@ -3448,10 +3454,9 @@ static netdev_features_t net_mpls_features(struct sk_buff *skb,  static netdev_features_t harmonize_features(struct sk_buff *skb,  	netdev_features_t features)  { -	int tmp;  	__be16 type; -	type = skb_network_protocol(skb, &tmp); +	type = skb_network_protocol(skb, NULL);  	features = net_mpls_features(skb, features, type);  	if (skb->ip_summed != CHECKSUM_NONE && @@ -4192,10 +4197,12 @@ int dev_direct_xmit(struct sk_buff *skb, u16 queue_id)  	local_bh_disable(); +	dev_xmit_recursion_inc();  	HARD_TX_LOCK(dev, txq, smp_processor_id());  	if (!netif_xmit_frozen_or_drv_stopped(txq))  		ret = netdev_start_xmit(skb, dev, txq, false);  	HARD_TX_UNLOCK(dev, txq); +	dev_xmit_recursion_dec();  	local_bh_enable(); @@ -5440,6 +5447,8 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp)  		for (i = 0; i < new->aux->used_map_cnt; i++) {  			if (dev_map_can_have_prog(new->aux->used_maps[i]))  				return -EINVAL; +			if (cpu_map_prog_allowed(new->aux->used_maps[i])) +				return -EINVAL;  		}  	} @@ -5458,10 +5467,6 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp)  		}  		break; -	case XDP_QUERY_PROG: -		xdp->prog_id = old ? old->aux->id : 0; -		break; -  	default:  		ret = -EINVAL;  		break; @@ -5583,7 +5588,7 @@ void netif_receive_skb_list(struct list_head *head)  }  EXPORT_SYMBOL(netif_receive_skb_list); -DEFINE_PER_CPU(struct work_struct, flush_works); +static DEFINE_PER_CPU(struct work_struct, flush_works);  /* Network device is going away, flush any packets still pending */  static void flush_backlog(struct work_struct *work) @@ -5599,7 +5604,7 @@ static void flush_backlog(struct work_struct *work)  	skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) {  		if (skb->dev->reg_state == NETREG_UNREGISTERING) {  			__skb_unlink(skb, &sd->input_pkt_queue); -			kfree_skb(skb); +			dev_kfree_skb_irq(skb);  			input_queue_head_incr(sd);  		}  	} @@ -6683,7 +6688,9 @@ static int napi_poll(struct napi_struct *n, struct list_head *repoll)  		trace_napi_poll(n, work, weight);  	} -	WARN_ON_ONCE(work > weight); +	if (unlikely(work > weight)) +		pr_err_once("NAPI poll function %pS returned %d, exceeding its budget of %d.\n", +			    n->poll, work, weight);  	if (likely(work < weight))  		goto out_unlock; @@ -7898,6 +7905,7 @@ EXPORT_SYMBOL(netdev_bonding_info_change);  /**   * netdev_get_xmit_slave - Get the xmit slave of master device + * @dev: device   * @skb: The packet   * @all_slaves: assume all the slaves are active   * @@ -8703,182 +8711,491 @@ int dev_change_proto_down_generic(struct net_device *dev, bool proto_down)  }  EXPORT_SYMBOL(dev_change_proto_down_generic); -u32 __dev_xdp_query(struct net_device *dev, bpf_op_t bpf_op, -		    enum bpf_netdev_command cmd) +/** + *	dev_change_proto_down_reason - proto down reason + * + *	@dev: device + *	@mask: proto down mask + *	@value: proto down value + */ +void dev_change_proto_down_reason(struct net_device *dev, unsigned long mask, +				  u32 value)  { -	struct netdev_bpf xdp; +	int b; -	if (!bpf_op) -		return 0; +	if (!mask) { +		dev->proto_down_reason = value; +	} else { +		for_each_set_bit(b, &mask, 32) { +			if (value & (1 << b)) +				dev->proto_down_reason |= BIT(b); +			else +				dev->proto_down_reason &= ~BIT(b); +		} +	} +} +EXPORT_SYMBOL(dev_change_proto_down_reason); -	memset(&xdp, 0, sizeof(xdp)); -	xdp.command = cmd; +struct bpf_xdp_link { +	struct bpf_link link; +	struct net_device *dev; /* protected by rtnl_lock, no refcnt held */ +	int flags; +}; + +static enum bpf_xdp_mode dev_xdp_mode(struct net_device *dev, u32 flags) +{ +	if (flags & XDP_FLAGS_HW_MODE) +		return XDP_MODE_HW; +	if (flags & XDP_FLAGS_DRV_MODE) +		return XDP_MODE_DRV; +	if (flags & XDP_FLAGS_SKB_MODE) +		return XDP_MODE_SKB; +	return dev->netdev_ops->ndo_bpf ? XDP_MODE_DRV : XDP_MODE_SKB; +} + +static bpf_op_t dev_xdp_bpf_op(struct net_device *dev, enum bpf_xdp_mode mode) +{ +	switch (mode) { +	case XDP_MODE_SKB: +		return generic_xdp_install; +	case XDP_MODE_DRV: +	case XDP_MODE_HW: +		return dev->netdev_ops->ndo_bpf; +	default: +		return NULL; +	}; +} + +static struct bpf_xdp_link *dev_xdp_link(struct net_device *dev, +					 enum bpf_xdp_mode mode) +{ +	return dev->xdp_state[mode].link; +} + +static struct bpf_prog *dev_xdp_prog(struct net_device *dev, +				     enum bpf_xdp_mode mode) +{ +	struct bpf_xdp_link *link = dev_xdp_link(dev, mode); -	/* Query must always succeed. */ -	WARN_ON(bpf_op(dev, &xdp) < 0 && cmd == XDP_QUERY_PROG); +	if (link) +		return link->link.prog; +	return dev->xdp_state[mode].prog; +} + +u32 dev_xdp_prog_id(struct net_device *dev, enum bpf_xdp_mode mode) +{ +	struct bpf_prog *prog = dev_xdp_prog(dev, mode); -	return xdp.prog_id; +	return prog ? prog->aux->id : 0;  } -static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, -			   struct netlink_ext_ack *extack, u32 flags, -			   struct bpf_prog *prog) +static void dev_xdp_set_link(struct net_device *dev, enum bpf_xdp_mode mode, +			     struct bpf_xdp_link *link) +{ +	dev->xdp_state[mode].link = link; +	dev->xdp_state[mode].prog = NULL; +} + +static void dev_xdp_set_prog(struct net_device *dev, enum bpf_xdp_mode mode, +			     struct bpf_prog *prog) +{ +	dev->xdp_state[mode].link = NULL; +	dev->xdp_state[mode].prog = prog; +} + +static int dev_xdp_install(struct net_device *dev, enum bpf_xdp_mode mode, +			   bpf_op_t bpf_op, struct netlink_ext_ack *extack, +			   u32 flags, struct bpf_prog *prog)  { -	bool non_hw = !(flags & XDP_FLAGS_HW_MODE); -	struct bpf_prog *prev_prog = NULL;  	struct netdev_bpf xdp;  	int err; -	if (non_hw) { -		prev_prog = bpf_prog_by_id(__dev_xdp_query(dev, bpf_op, -							   XDP_QUERY_PROG)); -		if (IS_ERR(prev_prog)) -			prev_prog = NULL; -	} -  	memset(&xdp, 0, sizeof(xdp)); -	if (flags & XDP_FLAGS_HW_MODE) -		xdp.command = XDP_SETUP_PROG_HW; -	else -		xdp.command = XDP_SETUP_PROG; +	xdp.command = mode == XDP_MODE_HW ? XDP_SETUP_PROG_HW : XDP_SETUP_PROG;  	xdp.extack = extack;  	xdp.flags = flags;  	xdp.prog = prog; +	/* Drivers assume refcnt is already incremented (i.e, prog pointer is +	 * "moved" into driver), so they don't increment it on their own, but +	 * they do decrement refcnt when program is detached or replaced. +	 * Given net_device also owns link/prog, we need to bump refcnt here +	 * to prevent drivers from underflowing it. +	 */ +	if (prog) +		bpf_prog_inc(prog);  	err = bpf_op(dev, &xdp); -	if (!err && non_hw) -		bpf_prog_change_xdp(prev_prog, prog); +	if (err) { +		if (prog) +			bpf_prog_put(prog); +		return err; +	} -	if (prev_prog) -		bpf_prog_put(prev_prog); +	if (mode != XDP_MODE_HW) +		bpf_prog_change_xdp(dev_xdp_prog(dev, mode), prog); -	return err; +	return 0;  }  static void dev_xdp_uninstall(struct net_device *dev)  { -	struct netdev_bpf xdp; -	bpf_op_t ndo_bpf; +	struct bpf_xdp_link *link; +	struct bpf_prog *prog; +	enum bpf_xdp_mode mode; +	bpf_op_t bpf_op; -	/* Remove generic XDP */ -	WARN_ON(dev_xdp_install(dev, generic_xdp_install, NULL, 0, NULL)); +	ASSERT_RTNL(); -	/* Remove from the driver */ -	ndo_bpf = dev->netdev_ops->ndo_bpf; -	if (!ndo_bpf) -		return; +	for (mode = XDP_MODE_SKB; mode < __MAX_XDP_MODE; mode++) { +		prog = dev_xdp_prog(dev, mode); +		if (!prog) +			continue; -	memset(&xdp, 0, sizeof(xdp)); -	xdp.command = XDP_QUERY_PROG; -	WARN_ON(ndo_bpf(dev, &xdp)); -	if (xdp.prog_id) -		WARN_ON(dev_xdp_install(dev, ndo_bpf, NULL, xdp.prog_flags, -					NULL)); +		bpf_op = dev_xdp_bpf_op(dev, mode); +		if (!bpf_op) +			continue; -	/* Remove HW offload */ -	memset(&xdp, 0, sizeof(xdp)); -	xdp.command = XDP_QUERY_PROG_HW; -	if (!ndo_bpf(dev, &xdp) && xdp.prog_id) -		WARN_ON(dev_xdp_install(dev, ndo_bpf, NULL, xdp.prog_flags, -					NULL)); +		WARN_ON(dev_xdp_install(dev, mode, bpf_op, NULL, 0, NULL)); + +		/* auto-detach link from net device */ +		link = dev_xdp_link(dev, mode); +		if (link) +			link->dev = NULL; +		else +			bpf_prog_put(prog); + +		dev_xdp_set_link(dev, mode, NULL); +	}  } -/** - *	dev_change_xdp_fd - set or clear a bpf program for a device rx path - *	@dev: device - *	@extack: netlink extended ack - *	@fd: new program fd or negative value to clear - *	@expected_fd: old program fd that userspace expects to replace or clear - *	@flags: xdp-related flags - * - *	Set or clear a bpf program for a device - */ -int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, -		      int fd, int expected_fd, u32 flags) +static int dev_xdp_attach(struct net_device *dev, struct netlink_ext_ack *extack, +			  struct bpf_xdp_link *link, struct bpf_prog *new_prog, +			  struct bpf_prog *old_prog, u32 flags)  { -	const struct net_device_ops *ops = dev->netdev_ops; -	enum bpf_netdev_command query; -	u32 prog_id, expected_id = 0; -	bpf_op_t bpf_op, bpf_chk; -	struct bpf_prog *prog; -	bool offload; +	struct bpf_prog *cur_prog; +	enum bpf_xdp_mode mode; +	bpf_op_t bpf_op;  	int err;  	ASSERT_RTNL(); -	offload = flags & XDP_FLAGS_HW_MODE; -	query = offload ? XDP_QUERY_PROG_HW : XDP_QUERY_PROG; +	/* either link or prog attachment, never both */ +	if (link && (new_prog || old_prog)) +		return -EINVAL; +	/* link supports only XDP mode flags */ +	if (link && (flags & ~XDP_FLAGS_MODES)) { +		NL_SET_ERR_MSG(extack, "Invalid XDP flags for BPF link attachment"); +		return -EINVAL; +	} +	/* just one XDP mode bit should be set, zero defaults to SKB mode */ +	if (hweight32(flags & XDP_FLAGS_MODES) > 1) { +		NL_SET_ERR_MSG(extack, "Only one XDP mode flag can be set"); +		return -EINVAL; +	} +	/* old_prog != NULL implies XDP_FLAGS_REPLACE is set */ +	if (old_prog && !(flags & XDP_FLAGS_REPLACE)) { +		NL_SET_ERR_MSG(extack, "XDP_FLAGS_REPLACE is not specified"); +		return -EINVAL; +	} -	bpf_op = bpf_chk = ops->ndo_bpf; -	if (!bpf_op && (flags & (XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE))) { -		NL_SET_ERR_MSG(extack, "underlying driver does not support XDP in native mode"); -		return -EOPNOTSUPP; +	mode = dev_xdp_mode(dev, flags); +	/* can't replace attached link */ +	if (dev_xdp_link(dev, mode)) { +		NL_SET_ERR_MSG(extack, "Can't replace active BPF XDP link"); +		return -EBUSY;  	} -	if (!bpf_op || (flags & XDP_FLAGS_SKB_MODE)) -		bpf_op = generic_xdp_install; -	if (bpf_op == bpf_chk) -		bpf_chk = generic_xdp_install; - -	prog_id = __dev_xdp_query(dev, bpf_op, query); -	if (flags & XDP_FLAGS_REPLACE) { -		if (expected_fd >= 0) { -			prog = bpf_prog_get_type_dev(expected_fd, -						     BPF_PROG_TYPE_XDP, -						     bpf_op == ops->ndo_bpf); -			if (IS_ERR(prog)) -				return PTR_ERR(prog); -			expected_id = prog->aux->id; -			bpf_prog_put(prog); -		} -		if (prog_id != expected_id) { -			NL_SET_ERR_MSG(extack, "Active program does not match expected"); -			return -EEXIST; -		} +	cur_prog = dev_xdp_prog(dev, mode); +	/* can't replace attached prog with link */ +	if (link && cur_prog) { +		NL_SET_ERR_MSG(extack, "Can't replace active XDP program with BPF link"); +		return -EBUSY;  	} -	if (fd >= 0) { -		if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) { -			NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time"); -			return -EEXIST; -		} +	if ((flags & XDP_FLAGS_REPLACE) && cur_prog != old_prog) { +		NL_SET_ERR_MSG(extack, "Active program does not match expected"); +		return -EEXIST; +	} + +	/* put effective new program into new_prog */ +	if (link) +		new_prog = link->link.prog; + +	if (new_prog) { +		bool offload = mode == XDP_MODE_HW; +		enum bpf_xdp_mode other_mode = mode == XDP_MODE_SKB +					       ? XDP_MODE_DRV : XDP_MODE_SKB; -		if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && prog_id) { +		if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && cur_prog) {  			NL_SET_ERR_MSG(extack, "XDP program already attached");  			return -EBUSY;  		} - -		prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP, -					     bpf_op == ops->ndo_bpf); -		if (IS_ERR(prog)) -			return PTR_ERR(prog); - -		if (!offload && bpf_prog_is_dev_bound(prog->aux)) { -			NL_SET_ERR_MSG(extack, "using device-bound program without HW_MODE flag is not supported"); -			bpf_prog_put(prog); +		if (!offload && dev_xdp_prog(dev, other_mode)) { +			NL_SET_ERR_MSG(extack, "Native and generic XDP can't be active at the same time"); +			return -EEXIST; +		} +		if (!offload && bpf_prog_is_dev_bound(new_prog->aux)) { +			NL_SET_ERR_MSG(extack, "Using device-bound program without HW_MODE flag is not supported");  			return -EINVAL;  		} - -		if (prog->expected_attach_type == BPF_XDP_DEVMAP) { +		if (new_prog->expected_attach_type == BPF_XDP_DEVMAP) {  			NL_SET_ERR_MSG(extack, "BPF_XDP_DEVMAP programs can not be attached to a device"); -			bpf_prog_put(prog);  			return -EINVAL;  		} +		if (new_prog->expected_attach_type == BPF_XDP_CPUMAP) { +			NL_SET_ERR_MSG(extack, "BPF_XDP_CPUMAP programs can not be attached to a device"); +			return -EINVAL; +		} +	} -		/* prog->aux->id may be 0 for orphaned device-bound progs */ -		if (prog->aux->id && prog->aux->id == prog_id) { -			bpf_prog_put(prog); -			return 0; +	/* don't call drivers if the effective program didn't change */ +	if (new_prog != cur_prog) { +		bpf_op = dev_xdp_bpf_op(dev, mode); +		if (!bpf_op) { +			NL_SET_ERR_MSG(extack, "Underlying driver does not support XDP in native mode"); +			return -EOPNOTSUPP;  		} -	} else { -		if (!prog_id) -			return 0; -		prog = NULL; + +		err = dev_xdp_install(dev, mode, bpf_op, extack, flags, new_prog); +		if (err) +			return err;  	} -	err = dev_xdp_install(dev, bpf_op, extack, flags, prog); -	if (err < 0 && prog) -		bpf_prog_put(prog); +	if (link) +		dev_xdp_set_link(dev, mode, link); +	else +		dev_xdp_set_prog(dev, mode, new_prog); +	if (cur_prog) +		bpf_prog_put(cur_prog); + +	return 0; +} + +static int dev_xdp_attach_link(struct net_device *dev, +			       struct netlink_ext_ack *extack, +			       struct bpf_xdp_link *link) +{ +	return dev_xdp_attach(dev, extack, link, NULL, NULL, link->flags); +} + +static int dev_xdp_detach_link(struct net_device *dev, +			       struct netlink_ext_ack *extack, +			       struct bpf_xdp_link *link) +{ +	enum bpf_xdp_mode mode; +	bpf_op_t bpf_op; + +	ASSERT_RTNL(); + +	mode = dev_xdp_mode(dev, link->flags); +	if (dev_xdp_link(dev, mode) != link) +		return -EINVAL; + +	bpf_op = dev_xdp_bpf_op(dev, mode); +	WARN_ON(dev_xdp_install(dev, mode, bpf_op, NULL, 0, NULL)); +	dev_xdp_set_link(dev, mode, NULL); +	return 0; +} + +static void bpf_xdp_link_release(struct bpf_link *link) +{ +	struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); + +	rtnl_lock(); + +	/* if racing with net_device's tear down, xdp_link->dev might be +	 * already NULL, in which case link was already auto-detached +	 */ +	if (xdp_link->dev) { +		WARN_ON(dev_xdp_detach_link(xdp_link->dev, NULL, xdp_link)); +		xdp_link->dev = NULL; +	} + +	rtnl_unlock(); +} + +static int bpf_xdp_link_detach(struct bpf_link *link) +{ +	bpf_xdp_link_release(link); +	return 0; +} + +static void bpf_xdp_link_dealloc(struct bpf_link *link) +{ +	struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); + +	kfree(xdp_link); +} + +static void bpf_xdp_link_show_fdinfo(const struct bpf_link *link, +				     struct seq_file *seq) +{ +	struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); +	u32 ifindex = 0; + +	rtnl_lock(); +	if (xdp_link->dev) +		ifindex = xdp_link->dev->ifindex; +	rtnl_unlock(); + +	seq_printf(seq, "ifindex:\t%u\n", ifindex); +} + +static int bpf_xdp_link_fill_link_info(const struct bpf_link *link, +				       struct bpf_link_info *info) +{ +	struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); +	u32 ifindex = 0; + +	rtnl_lock(); +	if (xdp_link->dev) +		ifindex = xdp_link->dev->ifindex; +	rtnl_unlock(); + +	info->xdp.ifindex = ifindex; +	return 0; +} + +static int bpf_xdp_link_update(struct bpf_link *link, struct bpf_prog *new_prog, +			       struct bpf_prog *old_prog) +{ +	struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); +	enum bpf_xdp_mode mode; +	bpf_op_t bpf_op; +	int err = 0; + +	rtnl_lock(); + +	/* link might have been auto-released already, so fail */ +	if (!xdp_link->dev) { +		err = -ENOLINK; +		goto out_unlock; +	} + +	if (old_prog && link->prog != old_prog) { +		err = -EPERM; +		goto out_unlock; +	} +	old_prog = link->prog; +	if (old_prog == new_prog) { +		/* no-op, don't disturb drivers */ +		bpf_prog_put(new_prog); +		goto out_unlock; +	} + +	mode = dev_xdp_mode(xdp_link->dev, xdp_link->flags); +	bpf_op = dev_xdp_bpf_op(xdp_link->dev, mode); +	err = dev_xdp_install(xdp_link->dev, mode, bpf_op, NULL, +			      xdp_link->flags, new_prog); +	if (err) +		goto out_unlock; +	old_prog = xchg(&link->prog, new_prog); +	bpf_prog_put(old_prog); + +out_unlock: +	rtnl_unlock(); +	return err; +} + +static const struct bpf_link_ops bpf_xdp_link_lops = { +	.release = bpf_xdp_link_release, +	.dealloc = bpf_xdp_link_dealloc, +	.detach = bpf_xdp_link_detach, +	.show_fdinfo = bpf_xdp_link_show_fdinfo, +	.fill_link_info = bpf_xdp_link_fill_link_info, +	.update_prog = bpf_xdp_link_update, +}; + +int bpf_xdp_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) +{ +	struct net *net = current->nsproxy->net_ns; +	struct bpf_link_primer link_primer; +	struct bpf_xdp_link *link; +	struct net_device *dev; +	int err, fd; + +	dev = dev_get_by_index(net, attr->link_create.target_ifindex); +	if (!dev) +		return -EINVAL; + +	link = kzalloc(sizeof(*link), GFP_USER); +	if (!link) { +		err = -ENOMEM; +		goto out_put_dev; +	} + +	bpf_link_init(&link->link, BPF_LINK_TYPE_XDP, &bpf_xdp_link_lops, prog); +	link->dev = dev; +	link->flags = attr->link_create.flags; + +	err = bpf_link_prime(&link->link, &link_primer); +	if (err) { +		kfree(link); +		goto out_put_dev; +	} + +	rtnl_lock(); +	err = dev_xdp_attach_link(dev, NULL, link); +	rtnl_unlock(); + +	if (err) { +		bpf_link_cleanup(&link_primer); +		goto out_put_dev; +	} + +	fd = bpf_link_settle(&link_primer); +	/* link itself doesn't hold dev's refcnt to not complicate shutdown */ +	dev_put(dev); +	return fd; + +out_put_dev: +	dev_put(dev); +	return err; +} + +/** + *	dev_change_xdp_fd - set or clear a bpf program for a device rx path + *	@dev: device + *	@extack: netlink extended ack + *	@fd: new program fd or negative value to clear + *	@expected_fd: old program fd that userspace expects to replace or clear + *	@flags: xdp-related flags + * + *	Set or clear a bpf program for a device + */ +int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, +		      int fd, int expected_fd, u32 flags) +{ +	enum bpf_xdp_mode mode = dev_xdp_mode(dev, flags); +	struct bpf_prog *new_prog = NULL, *old_prog = NULL; +	int err; + +	ASSERT_RTNL(); + +	if (fd >= 0) { +		new_prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP, +						 mode != XDP_MODE_SKB); +		if (IS_ERR(new_prog)) +			return PTR_ERR(new_prog); +	} + +	if (expected_fd >= 0) { +		old_prog = bpf_prog_get_type_dev(expected_fd, BPF_PROG_TYPE_XDP, +						 mode != XDP_MODE_SKB); +		if (IS_ERR(old_prog)) { +			err = PTR_ERR(old_prog); +			old_prog = NULL; +			goto err_out; +		} +	} + +	err = dev_xdp_attach(dev, extack, NULL, new_prog, old_prog, flags); + +err_out: +	if (err && new_prog) +		bpf_prog_put(new_prog); +	if (old_prog) +		bpf_prog_put(old_prog);  	return err;  } @@ -9547,6 +9864,13 @@ int register_netdevice(struct net_device *dev)  		rcu_barrier();  		dev->reg_state = NETREG_UNREGISTERED; +		/* We should put the kobject that hold in +		 * netdev_unregister_kobject(), otherwise +		 * the net device cannot be freed when +		 * driver calls free_netdev(), because the +		 * kobject is being hold. +		 */ +		kobject_put(&dev->dev.kobj);  	}  	/*  	 *	Prevent userspace races by waiting until the network  | 
