mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 10:50:53 +07:00
net: bridge: mcast: add support for group query retransmit
We need to be able to retransmit group-specific and group-and-source specific queries. The new timer takes care of those. v3: add IPv6 support Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
438ef2d027
commit
42c11ccfe8
@ -50,6 +50,7 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
|
|||||||
__be32 group,
|
__be32 group,
|
||||||
__u16 vid,
|
__u16 vid,
|
||||||
const unsigned char *src);
|
const unsigned char *src);
|
||||||
|
static void br_multicast_port_group_rexmit(struct timer_list *t);
|
||||||
|
|
||||||
static void __del_port_router(struct net_bridge_port *p);
|
static void __del_port_router(struct net_bridge_port *p);
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
@ -184,6 +185,7 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
|
|||||||
rcu_assign_pointer(*pp, pg->next);
|
rcu_assign_pointer(*pp, pg->next);
|
||||||
hlist_del_init(&pg->mglist);
|
hlist_del_init(&pg->mglist);
|
||||||
del_timer(&pg->timer);
|
del_timer(&pg->timer);
|
||||||
|
del_timer(&pg->rexmit_timer);
|
||||||
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
|
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
|
||||||
br_multicast_del_group_src(ent);
|
br_multicast_del_group_src(ent);
|
||||||
br_mdb_notify(br->dev, pg->port, &pg->addr, RTM_DELMDB, pg->flags);
|
br_mdb_notify(br->dev, pg->port, &pg->addr, RTM_DELMDB, pg->flags);
|
||||||
@ -237,7 +239,8 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
|
|||||||
struct net_bridge_port_group *pg,
|
struct net_bridge_port_group *pg,
|
||||||
__be32 ip_dst, __be32 group,
|
__be32 ip_dst, __be32 group,
|
||||||
bool with_srcs, bool over_lmqt,
|
bool with_srcs, bool over_lmqt,
|
||||||
u8 sflag, u8 *igmp_type)
|
u8 sflag, u8 *igmp_type,
|
||||||
|
bool *need_rexmit)
|
||||||
{
|
{
|
||||||
struct net_bridge_port *p = pg ? pg->port : NULL;
|
struct net_bridge_port *p = pg ? pg->port : NULL;
|
||||||
struct net_bridge_group_src *ent;
|
struct net_bridge_group_src *ent;
|
||||||
@ -352,6 +355,8 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
|
|||||||
ent->src_query_rexmit_cnt > 0) {
|
ent->src_query_rexmit_cnt > 0) {
|
||||||
ihv3->srcs[lmqt_srcs++] = ent->addr.u.ip4;
|
ihv3->srcs[lmqt_srcs++] = ent->addr.u.ip4;
|
||||||
ent->src_query_rexmit_cnt--;
|
ent->src_query_rexmit_cnt--;
|
||||||
|
if (need_rexmit && ent->src_query_rexmit_cnt)
|
||||||
|
*need_rexmit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (WARN_ON(lmqt_srcs != ntohs(ihv3->nsrcs))) {
|
if (WARN_ON(lmqt_srcs != ntohs(ihv3->nsrcs))) {
|
||||||
@ -380,7 +385,8 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
|
|||||||
const struct in6_addr *ip6_dst,
|
const struct in6_addr *ip6_dst,
|
||||||
const struct in6_addr *group,
|
const struct in6_addr *group,
|
||||||
bool with_srcs, bool over_llqt,
|
bool with_srcs, bool over_llqt,
|
||||||
u8 sflag, u8 *igmp_type)
|
u8 sflag, u8 *igmp_type,
|
||||||
|
bool *need_rexmit)
|
||||||
{
|
{
|
||||||
struct net_bridge_port *p = pg ? pg->port : NULL;
|
struct net_bridge_port *p = pg ? pg->port : NULL;
|
||||||
struct net_bridge_group_src *ent;
|
struct net_bridge_group_src *ent;
|
||||||
@ -510,6 +516,8 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
|
|||||||
ent->src_query_rexmit_cnt > 0) {
|
ent->src_query_rexmit_cnt > 0) {
|
||||||
mld2q->mld2q_srcs[llqt_srcs++] = ent->addr.u.ip6;
|
mld2q->mld2q_srcs[llqt_srcs++] = ent->addr.u.ip6;
|
||||||
ent->src_query_rexmit_cnt--;
|
ent->src_query_rexmit_cnt--;
|
||||||
|
if (need_rexmit && ent->src_query_rexmit_cnt)
|
||||||
|
*need_rexmit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (WARN_ON(llqt_srcs != ntohs(mld2q->mld2q_nsrcs))) {
|
if (WARN_ON(llqt_srcs != ntohs(mld2q->mld2q_nsrcs))) {
|
||||||
@ -540,7 +548,8 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
|
|||||||
struct br_ip *ip_dst,
|
struct br_ip *ip_dst,
|
||||||
struct br_ip *group,
|
struct br_ip *group,
|
||||||
bool with_srcs, bool over_lmqt,
|
bool with_srcs, bool over_lmqt,
|
||||||
u8 sflag, u8 *igmp_type)
|
u8 sflag, u8 *igmp_type,
|
||||||
|
bool *need_rexmit)
|
||||||
{
|
{
|
||||||
__be32 ip4_dst;
|
__be32 ip4_dst;
|
||||||
|
|
||||||
@ -550,7 +559,8 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
|
|||||||
return br_ip4_multicast_alloc_query(br, pg,
|
return br_ip4_multicast_alloc_query(br, pg,
|
||||||
ip4_dst, group->u.ip4,
|
ip4_dst, group->u.ip4,
|
||||||
with_srcs, over_lmqt,
|
with_srcs, over_lmqt,
|
||||||
sflag, igmp_type);
|
sflag, igmp_type,
|
||||||
|
need_rexmit);
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
case htons(ETH_P_IPV6): {
|
case htons(ETH_P_IPV6): {
|
||||||
struct in6_addr ip6_dst;
|
struct in6_addr ip6_dst;
|
||||||
@ -564,7 +574,8 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
|
|||||||
return br_ip6_multicast_alloc_query(br, pg,
|
return br_ip6_multicast_alloc_query(br, pg,
|
||||||
&ip6_dst, &group->u.ip6,
|
&ip6_dst, &group->u.ip6,
|
||||||
with_srcs, over_lmqt,
|
with_srcs, over_lmqt,
|
||||||
sflag, igmp_type);
|
sflag, igmp_type,
|
||||||
|
need_rexmit);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -708,8 +719,9 @@ struct net_bridge_port_group *br_multicast_new_port_group(
|
|||||||
p->filter_mode = filter_mode;
|
p->filter_mode = filter_mode;
|
||||||
INIT_HLIST_HEAD(&p->src_list);
|
INIT_HLIST_HEAD(&p->src_list);
|
||||||
rcu_assign_pointer(p->next, next);
|
rcu_assign_pointer(p->next, next);
|
||||||
hlist_add_head(&p->mglist, &port->mglist);
|
|
||||||
timer_setup(&p->timer, br_multicast_port_group_expired, 0);
|
timer_setup(&p->timer, br_multicast_port_group_expired, 0);
|
||||||
|
timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0);
|
||||||
|
hlist_add_head(&p->mglist, &port->mglist);
|
||||||
|
|
||||||
if (src)
|
if (src)
|
||||||
memcpy(p->eth_addr, src, ETH_ALEN);
|
memcpy(p->eth_addr, src, ETH_ALEN);
|
||||||
@ -943,7 +955,8 @@ static void __br_multicast_send_query(struct net_bridge *br,
|
|||||||
struct br_ip *ip_dst,
|
struct br_ip *ip_dst,
|
||||||
struct br_ip *group,
|
struct br_ip *group,
|
||||||
bool with_srcs,
|
bool with_srcs,
|
||||||
u8 sflag)
|
u8 sflag,
|
||||||
|
bool *need_rexmit)
|
||||||
{
|
{
|
||||||
bool over_lmqt = !!sflag;
|
bool over_lmqt = !!sflag;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
@ -951,7 +964,8 @@ static void __br_multicast_send_query(struct net_bridge *br,
|
|||||||
|
|
||||||
again_under_lmqt:
|
again_under_lmqt:
|
||||||
skb = br_multicast_alloc_query(br, pg, ip_dst, group, with_srcs,
|
skb = br_multicast_alloc_query(br, pg, ip_dst, group, with_srcs,
|
||||||
over_lmqt, sflag, &igmp_type);
|
over_lmqt, sflag, &igmp_type,
|
||||||
|
need_rexmit);
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1004,7 +1018,8 @@ static void br_multicast_send_query(struct net_bridge *br,
|
|||||||
if (!other_query || timer_pending(&other_query->timer))
|
if (!other_query || timer_pending(&other_query->timer))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
__br_multicast_send_query(br, port, NULL, NULL, &br_group, false, 0);
|
__br_multicast_send_query(br, port, NULL, NULL, &br_group, false, 0,
|
||||||
|
NULL);
|
||||||
|
|
||||||
time = jiffies;
|
time = jiffies;
|
||||||
time += own_query->startup_sent < br->multicast_startup_query_count ?
|
time += own_query->startup_sent < br->multicast_startup_query_count ?
|
||||||
@ -1049,6 +1064,44 @@ static void br_ip6_multicast_port_query_expired(struct timer_list *t)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void br_multicast_port_group_rexmit(struct timer_list *t)
|
||||||
|
{
|
||||||
|
struct net_bridge_port_group *pg = from_timer(pg, t, rexmit_timer);
|
||||||
|
struct bridge_mcast_other_query *other_query = NULL;
|
||||||
|
struct net_bridge *br = pg->port->br;
|
||||||
|
bool need_rexmit = false;
|
||||||
|
|
||||||
|
spin_lock(&br->multicast_lock);
|
||||||
|
if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) ||
|
||||||
|
!br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
|
||||||
|
!br_opt_get(br, BROPT_MULTICAST_QUERIER))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (pg->addr.proto == htons(ETH_P_IP))
|
||||||
|
other_query = &br->ip4_other_query;
|
||||||
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
else
|
||||||
|
other_query = &br->ip6_other_query;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!other_query || timer_pending(&other_query->timer))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (pg->grp_query_rexmit_cnt) {
|
||||||
|
pg->grp_query_rexmit_cnt--;
|
||||||
|
__br_multicast_send_query(br, pg->port, pg, &pg->addr,
|
||||||
|
&pg->addr, false, 1, NULL);
|
||||||
|
}
|
||||||
|
__br_multicast_send_query(br, pg->port, pg, &pg->addr,
|
||||||
|
&pg->addr, true, 0, &need_rexmit);
|
||||||
|
|
||||||
|
if (pg->grp_query_rexmit_cnt || need_rexmit)
|
||||||
|
mod_timer(&pg->rexmit_timer, jiffies +
|
||||||
|
br->multicast_last_member_interval);
|
||||||
|
out:
|
||||||
|
spin_unlock(&br->multicast_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static void br_mc_disabled_update(struct net_device *dev, bool value)
|
static void br_mc_disabled_update(struct net_device *dev, bool value)
|
||||||
{
|
{
|
||||||
struct switchdev_attr attr = {
|
struct switchdev_attr attr = {
|
||||||
@ -1658,7 +1711,7 @@ br_multicast_leave_group(struct net_bridge *br,
|
|||||||
|
|
||||||
if (br_opt_get(br, BROPT_MULTICAST_QUERIER)) {
|
if (br_opt_get(br, BROPT_MULTICAST_QUERIER)) {
|
||||||
__br_multicast_send_query(br, port, NULL, NULL, &mp->addr,
|
__br_multicast_send_query(br, port, NULL, NULL, &mp->addr,
|
||||||
false, 0);
|
false, 0, NULL);
|
||||||
|
|
||||||
time = jiffies + br->multicast_last_member_count *
|
time = jiffies + br->multicast_last_member_count *
|
||||||
br->multicast_last_member_interval;
|
br->multicast_last_member_interval;
|
||||||
|
@ -240,10 +240,12 @@ struct net_bridge_port_group {
|
|||||||
unsigned char eth_addr[ETH_ALEN] __aligned(2);
|
unsigned char eth_addr[ETH_ALEN] __aligned(2);
|
||||||
unsigned char flags;
|
unsigned char flags;
|
||||||
unsigned char filter_mode;
|
unsigned char filter_mode;
|
||||||
|
unsigned char grp_query_rexmit_cnt;
|
||||||
|
|
||||||
struct hlist_head src_list;
|
struct hlist_head src_list;
|
||||||
unsigned int src_ents;
|
unsigned int src_ents;
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
|
struct timer_list rexmit_timer;
|
||||||
struct hlist_node mglist;
|
struct hlist_node mglist;
|
||||||
|
|
||||||
struct rcu_head rcu;
|
struct rcu_head rcu;
|
||||||
@ -868,6 +870,12 @@ static inline int br_multicast_igmp_type(const struct sk_buff *skb)
|
|||||||
{
|
{
|
||||||
return BR_INPUT_SKB_CB(skb)->igmp;
|
return BR_INPUT_SKB_CB(skb)->igmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned long br_multicast_lmqt(const struct net_bridge *br)
|
||||||
|
{
|
||||||
|
return br->multicast_last_member_interval *
|
||||||
|
br->multicast_last_member_count;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
static inline int br_multicast_rcv(struct net_bridge *br,
|
static inline int br_multicast_rcv(struct net_bridge *br,
|
||||||
struct net_bridge_port *port,
|
struct net_bridge_port *port,
|
||||||
|
Loading…
Reference in New Issue
Block a user