|
@@ -1497,6 +1497,8 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po)
|
|
|
f->arr[f->num_members] = sk;
|
|
|
smp_wmb();
|
|
|
f->num_members++;
|
|
|
+ if (f->num_members == 1)
|
|
|
+ dev_add_pack(&f->prot_hook);
|
|
|
spin_unlock(&f->lock);
|
|
|
}
|
|
|
|
|
@@ -1513,6 +1515,8 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po)
|
|
|
BUG_ON(i >= f->num_members);
|
|
|
f->arr[i] = f->arr[f->num_members - 1];
|
|
|
f->num_members--;
|
|
|
+ if (f->num_members == 0)
|
|
|
+ __dev_remove_pack(&f->prot_hook);
|
|
|
spin_unlock(&f->lock);
|
|
|
}
|
|
|
|
|
@@ -1693,7 +1697,6 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
|
|
|
match->prot_hook.func = packet_rcv_fanout;
|
|
|
match->prot_hook.af_packet_priv = match;
|
|
|
match->prot_hook.id_match = match_fanout_group;
|
|
|
- dev_add_pack(&match->prot_hook);
|
|
|
list_add(&match->list, &fanout_list);
|
|
|
}
|
|
|
err = -EINVAL;
|
|
@@ -1718,7 +1721,12 @@ out:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static void fanout_release(struct sock *sk)
|
|
|
+/* If pkt_sk(sk)->fanout->sk_ref is zero, this function removes
|
|
|
+ * pkt_sk(sk)->fanout from fanout_list and returns pkt_sk(sk)->fanout.
|
|
|
+ * It is the responsibility of the caller to call fanout_release_data() and
|
|
|
+ * free the returned packet_fanout (after synchronize_net())
|
|
|
+ */
|
|
|
+static struct packet_fanout *fanout_release(struct sock *sk)
|
|
|
{
|
|
|
struct packet_sock *po = pkt_sk(sk);
|
|
|
struct packet_fanout *f;
|
|
@@ -1728,17 +1736,17 @@ static void fanout_release(struct sock *sk)
|
|
|
if (f) {
|
|
|
po->fanout = NULL;
|
|
|
|
|
|
- if (atomic_dec_and_test(&f->sk_ref)) {
|
|
|
+ if (atomic_dec_and_test(&f->sk_ref))
|
|
|
list_del(&f->list);
|
|
|
- dev_remove_pack(&f->prot_hook);
|
|
|
- fanout_release_data(f);
|
|
|
- kfree(f);
|
|
|
- }
|
|
|
+ else
|
|
|
+ f = NULL;
|
|
|
|
|
|
if (po->rollover)
|
|
|
kfree_rcu(po->rollover, rcu);
|
|
|
}
|
|
|
mutex_unlock(&fanout_mutex);
|
|
|
+
|
|
|
+ return f;
|
|
|
}
|
|
|
|
|
|
static bool packet_extra_vlan_len_allowed(const struct net_device *dev,
|
|
@@ -2912,6 +2920,7 @@ static int packet_release(struct socket *sock)
|
|
|
{
|
|
|
struct sock *sk = sock->sk;
|
|
|
struct packet_sock *po;
|
|
|
+ struct packet_fanout *f;
|
|
|
struct net *net;
|
|
|
union tpacket_req_u req_u;
|
|
|
|
|
@@ -2951,9 +2960,14 @@ static int packet_release(struct socket *sock)
|
|
|
packet_set_ring(sk, &req_u, 1, 1);
|
|
|
}
|
|
|
|
|
|
- fanout_release(sk);
|
|
|
+ f = fanout_release(sk);
|
|
|
|
|
|
synchronize_net();
|
|
|
+
|
|
|
+ if (f) {
|
|
|
+ fanout_release_data(f);
|
|
|
+ kfree(f);
|
|
|
+ }
|
|
|
/*
|
|
|
* Now the socket is dead. No more input will appear.
|
|
|
*/
|
|
@@ -3905,7 +3919,6 @@ static int packet_notifier(struct notifier_block *this,
|
|
|
}
|
|
|
if (msg == NETDEV_UNREGISTER) {
|
|
|
packet_cached_dev_reset(po);
|
|
|
- fanout_release(sk);
|
|
|
po->ifindex = -1;
|
|
|
if (po->prot_hook.dev)
|
|
|
dev_put(po->prot_hook.dev);
|