|
@@ -244,11 +244,15 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po);
|
|
|
static void register_prot_hook(struct sock *sk)
|
|
|
{
|
|
|
struct packet_sock *po = pkt_sk(sk);
|
|
|
+
|
|
|
if (!po->running) {
|
|
|
- if (po->fanout)
|
|
|
+ if (po->fanout) {
|
|
|
__fanout_link(sk, po);
|
|
|
- else
|
|
|
+ } else {
|
|
|
dev_add_pack(&po->prot_hook);
|
|
|
+ rcu_assign_pointer(po->cached_dev, po->prot_hook.dev);
|
|
|
+ }
|
|
|
+
|
|
|
sock_hold(sk);
|
|
|
po->running = 1;
|
|
|
}
|
|
@@ -266,10 +270,13 @@ static void __unregister_prot_hook(struct sock *sk, bool sync)
|
|
|
struct packet_sock *po = pkt_sk(sk);
|
|
|
|
|
|
po->running = 0;
|
|
|
- if (po->fanout)
|
|
|
+ if (po->fanout) {
|
|
|
__fanout_unlink(sk, po);
|
|
|
- else
|
|
|
+ } else {
|
|
|
__dev_remove_pack(&po->prot_hook);
|
|
|
+ RCU_INIT_POINTER(po->cached_dev, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
__sock_put(sk);
|
|
|
|
|
|
if (sync) {
|
|
@@ -2052,12 +2059,24 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
|
|
|
return tp_len;
|
|
|
}
|
|
|
|
|
|
+static struct net_device *packet_cached_dev_get(struct packet_sock *po)
|
|
|
+{
|
|
|
+ struct net_device *dev;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ dev = rcu_dereference(po->cached_dev);
|
|
|
+ if (dev)
|
|
|
+ dev_hold(dev);
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return dev;
|
|
|
+}
|
|
|
+
|
|
|
static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
|
|
|
{
|
|
|
struct sk_buff *skb;
|
|
|
struct net_device *dev;
|
|
|
__be16 proto;
|
|
|
- bool need_rls_dev = false;
|
|
|
int err, reserve = 0;
|
|
|
void *ph;
|
|
|
struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
|
|
@@ -2070,7 +2089,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
|
|
|
mutex_lock(&po->pg_vec_lock);
|
|
|
|
|
|
if (saddr == NULL) {
|
|
|
- dev = po->prot_hook.dev;
|
|
|
+ dev = packet_cached_dev_get(po);
|
|
|
proto = po->num;
|
|
|
addr = NULL;
|
|
|
} else {
|
|
@@ -2084,19 +2103,17 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
|
|
|
proto = saddr->sll_protocol;
|
|
|
addr = saddr->sll_addr;
|
|
|
dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
|
|
|
- need_rls_dev = true;
|
|
|
}
|
|
|
|
|
|
err = -ENXIO;
|
|
|
if (unlikely(dev == NULL))
|
|
|
goto out;
|
|
|
-
|
|
|
- reserve = dev->hard_header_len;
|
|
|
-
|
|
|
err = -ENETDOWN;
|
|
|
if (unlikely(!(dev->flags & IFF_UP)))
|
|
|
goto out_put;
|
|
|
|
|
|
+ reserve = dev->hard_header_len;
|
|
|
+
|
|
|
size_max = po->tx_ring.frame_size
|
|
|
- (po->tp_hdrlen - sizeof(struct sockaddr_ll));
|
|
|
|
|
@@ -2173,8 +2190,7 @@ out_status:
|
|
|
__packet_set_status(po, ph, status);
|
|
|
kfree_skb(skb);
|
|
|
out_put:
|
|
|
- if (need_rls_dev)
|
|
|
- dev_put(dev);
|
|
|
+ dev_put(dev);
|
|
|
out:
|
|
|
mutex_unlock(&po->pg_vec_lock);
|
|
|
return err;
|
|
@@ -2212,7 +2228,6 @@ static int packet_snd(struct socket *sock,
|
|
|
struct sk_buff *skb;
|
|
|
struct net_device *dev;
|
|
|
__be16 proto;
|
|
|
- bool need_rls_dev = false;
|
|
|
unsigned char *addr;
|
|
|
int err, reserve = 0;
|
|
|
struct virtio_net_hdr vnet_hdr = { 0 };
|
|
@@ -2228,7 +2243,7 @@ static int packet_snd(struct socket *sock,
|
|
|
*/
|
|
|
|
|
|
if (saddr == NULL) {
|
|
|
- dev = po->prot_hook.dev;
|
|
|
+ dev = packet_cached_dev_get(po);
|
|
|
proto = po->num;
|
|
|
addr = NULL;
|
|
|
} else {
|
|
@@ -2240,19 +2255,17 @@ static int packet_snd(struct socket *sock,
|
|
|
proto = saddr->sll_protocol;
|
|
|
addr = saddr->sll_addr;
|
|
|
dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
|
|
|
- need_rls_dev = true;
|
|
|
}
|
|
|
|
|
|
err = -ENXIO;
|
|
|
- if (dev == NULL)
|
|
|
+ if (unlikely(dev == NULL))
|
|
|
goto out_unlock;
|
|
|
- if (sock->type == SOCK_RAW)
|
|
|
- reserve = dev->hard_header_len;
|
|
|
-
|
|
|
err = -ENETDOWN;
|
|
|
- if (!(dev->flags & IFF_UP))
|
|
|
+ if (unlikely(!(dev->flags & IFF_UP)))
|
|
|
goto out_unlock;
|
|
|
|
|
|
+ if (sock->type == SOCK_RAW)
|
|
|
+ reserve = dev->hard_header_len;
|
|
|
if (po->has_vnet_hdr) {
|
|
|
vnet_hdr_len = sizeof(vnet_hdr);
|
|
|
|
|
@@ -2386,15 +2399,14 @@ static int packet_snd(struct socket *sock,
|
|
|
if (err > 0 && (err = net_xmit_errno(err)) != 0)
|
|
|
goto out_unlock;
|
|
|
|
|
|
- if (need_rls_dev)
|
|
|
- dev_put(dev);
|
|
|
+ dev_put(dev);
|
|
|
|
|
|
return len;
|
|
|
|
|
|
out_free:
|
|
|
kfree_skb(skb);
|
|
|
out_unlock:
|
|
|
- if (dev && need_rls_dev)
|
|
|
+ if (dev)
|
|
|
dev_put(dev);
|
|
|
out:
|
|
|
return err;
|
|
@@ -2614,6 +2626,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol,
|
|
|
po = pkt_sk(sk);
|
|
|
sk->sk_family = PF_PACKET;
|
|
|
po->num = proto;
|
|
|
+ RCU_INIT_POINTER(po->cached_dev, NULL);
|
|
|
|
|
|
sk->sk_destruct = packet_sock_destruct;
|
|
|
sk_refcnt_debug_inc(sk);
|