diff options
| author | David S. Miller <davem@davemloft.net> | 2019-07-08 19:48:57 -0700 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2019-07-08 19:48:57 -0700 | 
| commit | af144a983402f7fd324ce556d9f9011a8b3e01fe (patch) | |
| tree | 7a0250b960a36976bc683789d9fe86b9f60a97a5 /net/xdp | |
| parent | 6413139dfc641aaaa30580b59696a5f7ea274194 (diff) | |
| parent | e858faf556d4e14c750ba1e8852783c6f9520a0e (diff) | |
| download | linux-af144a983402f7fd324ce556d9f9011a8b3e01fe.tar.bz2 | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Two cases of overlapping changes, nothing fancy.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/xdp')
| -rw-r--r-- | net/xdp/xdp_umem.c | 21 | ||||
| -rw-r--r-- | net/xdp/xdp_umem.h | 1 | ||||
| -rw-r--r-- | net/xdp/xsk.c | 87 | ||||
| -rw-r--r-- | net/xdp/xsk_queue.h | 2 | 
4 files changed, 89 insertions, 22 deletions
| diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c index 9c6de4f114f8..20c91f02d3d8 100644 --- a/net/xdp/xdp_umem.c +++ b/net/xdp/xdp_umem.c @@ -105,6 +105,9 @@ int xdp_umem_assign_dev(struct xdp_umem *umem, struct net_device *dev,  	umem->dev = dev;  	umem->queue_id = queue_id; + +	dev_hold(dev); +  	if (force_copy)  		/* For copy-mode, we are done. */  		goto out_rtnl_unlock; @@ -124,7 +127,6 @@ int xdp_umem_assign_dev(struct xdp_umem *umem, struct net_device *dev,  		goto err_unreg_umem;  	rtnl_unlock(); -	dev_hold(dev);  	umem->zc = true;  	return 0; @@ -138,11 +140,13 @@ out_rtnl_unlock:  	return err;  } -static void xdp_umem_clear_dev(struct xdp_umem *umem) +void xdp_umem_clear_dev(struct xdp_umem *umem)  {  	struct netdev_bpf bpf;  	int err; +	ASSERT_RTNL(); +  	if (!umem->dev)  		return; @@ -151,22 +155,17 @@ static void xdp_umem_clear_dev(struct xdp_umem *umem)  		bpf.xsk.umem = NULL;  		bpf.xsk.queue_id = umem->queue_id; -		rtnl_lock();  		err = umem->dev->netdev_ops->ndo_bpf(umem->dev, &bpf); -		rtnl_unlock();  		if (err)  			WARN(1, "failed to disable umem!\n");  	} -	rtnl_lock();  	xdp_clear_umem_at_qid(umem->dev, umem->queue_id); -	rtnl_unlock(); -	if (umem->zc) { -		dev_put(umem->dev); -		umem->zc = false; -	} +	dev_put(umem->dev); +	umem->dev = NULL; +	umem->zc = false;  }  static void xdp_umem_unpin_pages(struct xdp_umem *umem) @@ -194,7 +193,9 @@ static void xdp_umem_unaccount_pages(struct xdp_umem *umem)  static void xdp_umem_release(struct xdp_umem *umem)  { +	rtnl_lock();  	xdp_umem_clear_dev(umem); +	rtnl_unlock();  	ida_simple_remove(&umem_ida, umem->id); diff --git a/net/xdp/xdp_umem.h b/net/xdp/xdp_umem.h index 27603227601b..a63a9fb251f5 100644 --- a/net/xdp/xdp_umem.h +++ b/net/xdp/xdp_umem.h @@ -10,6 +10,7 @@  int xdp_umem_assign_dev(struct xdp_umem *umem, struct net_device *dev,  			u16 queue_id, u16 flags); +void xdp_umem_clear_dev(struct xdp_umem *umem);  bool xdp_umem_validate_queues(struct xdp_umem *umem);  void xdp_get_umem(struct xdp_umem *umem);  void xdp_put_umem(struct xdp_umem *umem); diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 0574f008954c..d4d6f10aa936 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -349,6 +349,22 @@ static int xsk_init_queue(u32 entries, struct xsk_queue **queue,  	return 0;  } +static void xsk_unbind_dev(struct xdp_sock *xs) +{ +	struct net_device *dev = xs->dev; + +	if (!dev || xs->state != XSK_BOUND) +		return; + +	xs->state = XSK_UNBOUND; + +	/* Wait for driver to stop using the xdp socket. */ +	xdp_del_sk_umem(xs->umem, xs); +	xs->dev = NULL; +	synchronize_net(); +	dev_put(dev); +} +  static int xsk_release(struct socket *sock)  {  	struct sock *sk = sock->sk; @@ -368,15 +384,7 @@ static int xsk_release(struct socket *sock)  	sock_prot_inuse_add(net, sk->sk_prot, -1);  	local_bh_enable(); -	if (xs->dev) { -		struct net_device *dev = xs->dev; - -		/* Wait for driver to stop using the xdp socket. */ -		xdp_del_sk_umem(xs->umem, xs); -		xs->dev = NULL; -		synchronize_net(); -		dev_put(dev); -	} +	xsk_unbind_dev(xs);  	xskq_destroy(xs->rx);  	xskq_destroy(xs->tx); @@ -426,7 +434,7 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len)  		return -EINVAL;  	mutex_lock(&xs->mutex); -	if (xs->dev) { +	if (xs->state != XSK_READY) {  		err = -EBUSY;  		goto out_release;  	} @@ -506,6 +514,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len)  out_unlock:  	if (err)  		dev_put(dev); +	else +		xs->state = XSK_BOUND;  out_release:  	mutex_unlock(&xs->mutex);  	return err; @@ -534,6 +544,10 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,  			return -EFAULT;  		mutex_lock(&xs->mutex); +		if (xs->state != XSK_READY) { +			mutex_unlock(&xs->mutex); +			return -EBUSY; +		}  		q = (optname == XDP_TX_RING) ? &xs->tx : &xs->rx;  		err = xsk_init_queue(entries, q, false);  		mutex_unlock(&xs->mutex); @@ -548,7 +562,7 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,  			return -EFAULT;  		mutex_lock(&xs->mutex); -		if (xs->umem) { +		if (xs->state != XSK_READY || xs->umem) {  			mutex_unlock(&xs->mutex);  			return -EBUSY;  		} @@ -575,6 +589,10 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,  			return -EFAULT;  		mutex_lock(&xs->mutex); +		if (xs->state != XSK_READY) { +			mutex_unlock(&xs->mutex); +			return -EBUSY; +		}  		if (!xs->umem) {  			mutex_unlock(&xs->mutex);  			return -EINVAL; @@ -696,6 +714,9 @@ static int xsk_mmap(struct file *file, struct socket *sock,  	unsigned long pfn;  	struct page *qpg; +	if (xs->state != XSK_READY) +		return -EBUSY; +  	if (offset == XDP_PGOFF_RX_RING) {  		q = READ_ONCE(xs->rx);  	} else if (offset == XDP_PGOFF_TX_RING) { @@ -727,6 +748,38 @@ static int xsk_mmap(struct file *file, struct socket *sock,  			       size, vma->vm_page_prot);  } +static int xsk_notifier(struct notifier_block *this, +			unsigned long msg, void *ptr) +{ +	struct net_device *dev = netdev_notifier_info_to_dev(ptr); +	struct net *net = dev_net(dev); +	struct sock *sk; + +	switch (msg) { +	case NETDEV_UNREGISTER: +		mutex_lock(&net->xdp.lock); +		sk_for_each(sk, &net->xdp.list) { +			struct xdp_sock *xs = xdp_sk(sk); + +			mutex_lock(&xs->mutex); +			if (xs->dev == dev) { +				sk->sk_err = ENETDOWN; +				if (!sock_flag(sk, SOCK_DEAD)) +					sk->sk_error_report(sk); + +				xsk_unbind_dev(xs); + +				/* Clear device references in umem. */ +				xdp_umem_clear_dev(xs->umem); +			} +			mutex_unlock(&xs->mutex); +		} +		mutex_unlock(&net->xdp.lock); +		break; +	} +	return NOTIFY_DONE; +} +  static struct proto xsk_proto = {  	.name =		"XDP",  	.owner =	THIS_MODULE, @@ -798,6 +851,7 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,  	sock_set_flag(sk, SOCK_RCU_FREE);  	xs = xdp_sk(sk); +	xs->state = XSK_READY;  	mutex_init(&xs->mutex);  	spin_lock_init(&xs->rx_lock);  	spin_lock_init(&xs->tx_completion_lock); @@ -819,6 +873,10 @@ static const struct net_proto_family xsk_family_ops = {  	.owner	= THIS_MODULE,  }; +static struct notifier_block xsk_netdev_notifier = { +	.notifier_call	= xsk_notifier, +}; +  static int __net_init xsk_net_init(struct net *net)  {  	mutex_init(&net->xdp.lock); @@ -851,8 +909,15 @@ static int __init xsk_init(void)  	err = register_pernet_subsys(&xsk_net_ops);  	if (err)  		goto out_sk; + +	err = register_netdevice_notifier(&xsk_netdev_notifier); +	if (err) +		goto out_pernet; +  	return 0; +out_pernet: +	unregister_pernet_subsys(&xsk_net_ops);  out_sk:  	sock_unregister(PF_XDP);  out_proto: diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 12b49784a6d5..909c5168ed0f 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -302,7 +302,7 @@ static inline void xskq_produce_flush_desc(struct xsk_queue *q)  	/* Order producer and data */  	smp_wmb(); /* B, matches C */ -	q->prod_tail = q->prod_head, +	q->prod_tail = q->prod_head;  	WRITE_ONCE(q->ring->producer, q->prod_tail);  } |