2 * Copyright (c) 2014 Red Hat, Inc.
4 * Based on mac-learning implementation.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 #include "mcast-snooping.h"
26 #include "byte-order.h"
29 #include "openvswitch/list.h"
30 #include "poll-loop.h"
33 #include "unaligned.h"
35 #include "vlan-bitmap.h"
36 #include "openvswitch/vlog.h"
38 COVERAGE_DEFINE(mcast_snooping_learned
);
39 COVERAGE_DEFINE(mcast_snooping_expired
);
41 static struct mcast_port_bundle
*
42 mcast_snooping_port_lookup(struct ovs_list
*list
, void *port
);
43 static struct mcast_mrouter_bundle
*
44 mcast_snooping_mrouter_lookup(struct mcast_snooping
*ms
, uint16_t vlan
,
46 OVS_REQ_RDLOCK(ms
->rwlock
);
49 mcast_snooping_enabled(const struct mcast_snooping
*ms
)
55 mcast_snooping_flood_unreg(const struct mcast_snooping
*ms
)
57 return ms
->flood_unreg
;
61 mcast_snooping_is_query(ovs_be16 igmp_type
)
63 return igmp_type
== htons(IGMP_HOST_MEMBERSHIP_QUERY
);
67 mcast_snooping_is_membership(ovs_be16 igmp_type
)
69 switch (ntohs(igmp_type
)) {
70 case IGMP_HOST_MEMBERSHIP_REPORT
:
71 case IGMPV2_HOST_MEMBERSHIP_REPORT
:
72 case IGMPV3_HOST_MEMBERSHIP_REPORT
:
73 case IGMP_HOST_LEAVE_MESSAGE
:
79 /* Returns the number of seconds since multicast group 'b' was learned in a
82 mcast_bundle_age(const struct mcast_snooping
*ms
,
83 const struct mcast_group_bundle
*b
)
85 time_t remaining
= b
->expires
- time_now();
86 return ms
->idle_time
- remaining
;
90 mcast_table_hash(const struct mcast_snooping
*ms
,
91 const struct in6_addr
*grp_addr
, uint16_t vlan
)
93 return hash_bytes(grp_addr
->s6_addr
, 16,
94 hash_2words(ms
->secret
, vlan
));
97 static struct mcast_group_bundle
*
98 mcast_group_bundle_from_lru_node(struct ovs_list
*list
)
100 return CONTAINER_OF(list
, struct mcast_group_bundle
, bundle_node
);
103 static struct mcast_group
*
104 mcast_group_from_lru_node(struct ovs_list
*list
)
106 return CONTAINER_OF(list
, struct mcast_group
, group_node
);
109 /* Searches 'ms' for and returns an mcast group for destination address
110 * 'dip' in 'vlan'. */
112 mcast_snooping_lookup(const struct mcast_snooping
*ms
,
113 const struct in6_addr
*dip
, uint16_t vlan
)
114 OVS_REQ_RDLOCK(ms
->rwlock
)
116 struct mcast_group
*grp
;
119 hash
= mcast_table_hash(ms
, dip
, vlan
);
120 HMAP_FOR_EACH_WITH_HASH (grp
, hmap_node
, hash
, &ms
->table
) {
121 if (grp
->vlan
== vlan
&& ipv6_addr_equals(&grp
->addr
, dip
)) {
129 mcast_snooping_lookup4(const struct mcast_snooping
*ms
, ovs_be32 ip4
,
131 OVS_REQ_RDLOCK(ms
->rwlock
)
133 struct in6_addr addr
= in6_addr_mapped_ipv4(ip4
);
134 return mcast_snooping_lookup(ms
, &addr
, vlan
);
137 /* If the LRU list is not empty, stores the least-recently-used entry
138 * in '*e' and returns true. Otherwise, if the LRU list is empty,
139 * stores NULL in '*e' and return false. */
141 group_get_lru(const struct mcast_snooping
*ms
, struct mcast_group
**grp
)
142 OVS_REQ_RDLOCK(ms
->rwlock
)
144 if (!ovs_list_is_empty(&ms
->group_lru
)) {
145 *grp
= mcast_group_from_lru_node(ms
->group_lru
.next
);
154 normalize_idle_time(unsigned int idle_time
)
156 return (idle_time
< 15 ? 15
157 : idle_time
> 3600 ? 3600
161 /* Creates and returns a new mcast table with an initial mcast aging
162 * timeout of MCAST_ENTRY_DEFAULT_IDLE_TIME seconds and an initial maximum of
163 * MCAST_DEFAULT_MAX entries. */
164 struct mcast_snooping
*
165 mcast_snooping_create(void)
167 struct mcast_snooping
*ms
;
169 ms
= xmalloc(sizeof *ms
);
170 hmap_init(&ms
->table
);
171 ovs_list_init(&ms
->group_lru
);
172 ovs_list_init(&ms
->mrouter_lru
);
173 ovs_list_init(&ms
->fport_list
);
174 ovs_list_init(&ms
->rport_list
);
175 ms
->secret
= random_uint32();
176 ms
->idle_time
= MCAST_ENTRY_DEFAULT_IDLE_TIME
;
177 ms
->max_entries
= MCAST_DEFAULT_MAX_ENTRIES
;
178 ms
->need_revalidate
= false;
179 ms
->flood_unreg
= true;
180 ovs_refcount_init(&ms
->ref_cnt
);
181 ovs_rwlock_init(&ms
->rwlock
);
185 struct mcast_snooping
*
186 mcast_snooping_ref(const struct mcast_snooping
*ms_
)
188 struct mcast_snooping
*ms
= CONST_CAST(struct mcast_snooping
*, ms_
);
190 ovs_refcount_ref(&ms
->ref_cnt
);
195 /* Unreferences (and possibly destroys) mcast snooping table 'ms'. */
197 mcast_snooping_unref(struct mcast_snooping
*ms
)
199 if (!mcast_snooping_enabled(ms
)) {
203 if (ovs_refcount_unref_relaxed(&ms
->ref_cnt
) == 1) {
204 mcast_snooping_flush(ms
);
205 hmap_destroy(&ms
->table
);
206 ovs_rwlock_destroy(&ms
->rwlock
);
211 /* Changes the mcast aging timeout of 'ms' to 'idle_time' seconds. */
213 mcast_snooping_set_idle_time(struct mcast_snooping
*ms
, unsigned int idle_time
)
214 OVS_REQ_WRLOCK(ms
->rwlock
)
216 struct mcast_group
*grp
;
217 struct mcast_group_bundle
*b
;
220 idle_time
= normalize_idle_time(idle_time
);
221 if (idle_time
!= ms
->idle_time
) {
222 delta
= (int) idle_time
- (int) ms
->idle_time
;
223 LIST_FOR_EACH (grp
, group_node
, &ms
->group_lru
) {
224 LIST_FOR_EACH (b
, bundle_node
, &grp
->bundle_lru
) {
228 ms
->idle_time
= idle_time
;
232 /* Sets the maximum number of entries in 'ms' to 'max_entries', adjusting it
233 * to be within a reasonable range. */
235 mcast_snooping_set_max_entries(struct mcast_snooping
*ms
,
237 OVS_REQ_WRLOCK(ms
->rwlock
)
239 ms
->max_entries
= (max_entries
< 10 ? 10
240 : max_entries
> 1000 * 1000 ? 1000 * 1000
244 /* Sets if unregistered multicast packets should be flooded to
245 * all ports or only to ports connected to multicast routers
247 * Returns true if previous state differs from current state,
248 * false otherwise. */
250 mcast_snooping_set_flood_unreg(struct mcast_snooping
*ms
, bool enable
)
251 OVS_REQ_WRLOCK(ms
->rwlock
)
253 bool prev
= ms
->flood_unreg
;
254 ms
->flood_unreg
= enable
;
255 return prev
!= enable
;
258 static struct mcast_group_bundle
*
259 mcast_group_bundle_lookup(struct mcast_snooping
*ms OVS_UNUSED
,
260 struct mcast_group
*grp
, void *port
)
261 OVS_REQ_RDLOCK(ms
->rwlock
)
263 struct mcast_group_bundle
*b
;
265 LIST_FOR_EACH (b
, bundle_node
, &grp
->bundle_lru
) {
266 if (b
->port
== port
) {
273 /* Insert a new bundle to the mcast group or update its
274 * position and expiration if it is already there. */
275 static struct mcast_group_bundle
*
276 mcast_group_insert_bundle(struct mcast_snooping
*ms OVS_UNUSED
,
277 struct mcast_group
*grp
, void *port
, int idle_time
)
278 OVS_REQ_WRLOCK(ms
->rwlock
)
280 struct mcast_group_bundle
*b
;
282 b
= mcast_group_bundle_lookup(ms
, grp
, port
);
284 ovs_list_remove(&b
->bundle_node
);
286 b
= xmalloc(sizeof *b
);
287 ovs_list_init(&b
->bundle_node
);
289 ms
->need_revalidate
= true;
292 b
->expires
= time_now() + idle_time
;
293 ovs_list_push_back(&grp
->bundle_lru
, &b
->bundle_node
);
297 /* Return true if multicast still has bundles associated.
298 * Return false if there is no bundles. */
300 mcast_group_has_bundles(struct mcast_group
*grp
)
302 return !ovs_list_is_empty(&grp
->bundle_lru
);
305 /* Delete 'grp' from the 'ms' hash table.
306 * Caller is responsible to clean bundle lru first. */
308 mcast_snooping_flush_group__(struct mcast_snooping
*ms
,
309 struct mcast_group
*grp
)
311 ovs_assert(ovs_list_is_empty(&grp
->bundle_lru
));
312 hmap_remove(&ms
->table
, &grp
->hmap_node
);
313 ovs_list_remove(&grp
->group_node
);
317 /* Flush out mcast group and its bundles */
319 mcast_snooping_flush_group(struct mcast_snooping
*ms
, struct mcast_group
*grp
)
320 OVS_REQ_WRLOCK(ms
->rwlock
)
322 struct mcast_group_bundle
*b
;
324 LIST_FOR_EACH_POP (b
, bundle_node
, &grp
->bundle_lru
) {
327 mcast_snooping_flush_group__(ms
, grp
);
328 ms
->need_revalidate
= true;
332 /* Delete bundle returning true if it succeeds,
333 * false if it didn't find the group. */
335 mcast_group_delete_bundle(struct mcast_snooping
*ms OVS_UNUSED
,
336 struct mcast_group
*grp
, void *port
)
337 OVS_REQ_WRLOCK(ms
->rwlock
)
339 struct mcast_group_bundle
*b
;
341 LIST_FOR_EACH (b
, bundle_node
, &grp
->bundle_lru
) {
342 if (b
->port
== port
) {
343 ovs_list_remove(&b
->bundle_node
);
351 /* If any bundle has expired, delete it. Returns the number of deleted
354 mcast_snooping_prune_expired(struct mcast_snooping
*ms
,
355 struct mcast_group
*grp
)
356 OVS_REQ_WRLOCK(ms
->rwlock
)
359 struct mcast_group_bundle
*b
, *next_b
;
360 time_t timenow
= time_now();
363 LIST_FOR_EACH_SAFE (b
, next_b
, bundle_node
, &grp
->bundle_lru
) {
364 /* This list is sorted on expiration time. */
365 if (b
->expires
> timenow
) {
368 ovs_list_remove(&b
->bundle_node
);
373 if (!mcast_group_has_bundles(grp
)) {
374 mcast_snooping_flush_group__(ms
, grp
);
379 ms
->need_revalidate
= true;
380 COVERAGE_ADD(mcast_snooping_expired
, expired
);
386 /* Add a multicast group to the mdb. If it exists, then
387 * move to the last position in the LRU list.
390 mcast_snooping_add_group(struct mcast_snooping
*ms
,
391 const struct in6_addr
*addr
,
392 uint16_t vlan
, void *port
)
393 OVS_REQ_WRLOCK(ms
->rwlock
)
396 struct mcast_group
*grp
;
398 /* Avoid duplicate packets. */
399 if (mcast_snooping_mrouter_lookup(ms
, vlan
, port
)
400 || mcast_snooping_port_lookup(&ms
->fport_list
, port
)) {
405 grp
= mcast_snooping_lookup(ms
, addr
, vlan
);
407 uint32_t hash
= mcast_table_hash(ms
, addr
, vlan
);
409 if (hmap_count(&ms
->table
) >= ms
->max_entries
) {
410 group_get_lru(ms
, &grp
);
411 mcast_snooping_flush_group(ms
, grp
);
414 grp
= xmalloc(sizeof *grp
);
415 hmap_insert(&ms
->table
, &grp
->hmap_node
, hash
);
418 ovs_list_init(&grp
->bundle_lru
);
420 ms
->need_revalidate
= true;
421 COVERAGE_INC(mcast_snooping_learned
);
423 ovs_list_remove(&grp
->group_node
);
425 mcast_group_insert_bundle(ms
, grp
, port
, ms
->idle_time
);
427 /* Mark 'grp' as recently used. */
428 ovs_list_push_back(&ms
->group_lru
, &grp
->group_node
);
433 mcast_snooping_add_group4(struct mcast_snooping
*ms
, ovs_be32 ip4
,
434 uint16_t vlan
, void *port
)
435 OVS_REQ_WRLOCK(ms
->rwlock
)
437 struct in6_addr addr
= in6_addr_mapped_ipv4(ip4
);
438 return mcast_snooping_add_group(ms
, &addr
, vlan
, port
);
442 mcast_snooping_add_report(struct mcast_snooping
*ms
,
443 const struct dp_packet
*p
,
444 uint16_t vlan
, void *port
)
448 const struct igmpv3_header
*igmpv3
;
449 const struct igmpv3_record
*record
;
453 offset
= (char *) dp_packet_l4(p
) - (char *) dp_packet_data(p
);
454 igmpv3
= dp_packet_at(p
, offset
, IGMPV3_HEADER_LEN
);
458 ngrp
= ntohs(igmpv3
->ngrp
);
459 offset
+= IGMPV3_HEADER_LEN
;
462 record
= dp_packet_at(p
, offset
, sizeof(struct igmpv3_record
));
466 /* Only consider known record types. */
467 if (record
->type
< IGMPV3_MODE_IS_INCLUDE
468 || record
->type
> IGMPV3_BLOCK_OLD_SOURCES
) {
471 ip4
= get_16aligned_be32(&record
->maddr
);
473 * If record is INCLUDE MODE and there are no sources, it's equivalent
476 if (ntohs(record
->nsrcs
) == 0
477 && (record
->type
== IGMPV3_MODE_IS_INCLUDE
478 || record
->type
== IGMPV3_CHANGE_TO_INCLUDE_MODE
)) {
479 ret
= mcast_snooping_leave_group4(ms
, ip4
, vlan
, port
);
481 ret
= mcast_snooping_add_group4(ms
, ip4
, vlan
, port
);
486 offset
+= sizeof(*record
)
487 + ntohs(record
->nsrcs
) * sizeof(ovs_be32
) + record
->aux_len
;
493 mcast_snooping_add_mld(struct mcast_snooping
*ms
,
494 const struct dp_packet
*p
,
495 uint16_t vlan
, void *port
)
497 const struct in6_addr
*addr
;
499 const struct mld_header
*mld
;
500 const struct mld2_record
*record
;
505 offset
= (char *) dp_packet_l4(p
) - (char *) dp_packet_data(p
);
506 mld
= dp_packet_at(p
, offset
, MLD_HEADER_LEN
);
510 ngrp
= ntohs(mld
->ngrp
);
511 offset
+= MLD_HEADER_LEN
;
512 addr
= dp_packet_at(p
, offset
, sizeof(struct in6_addr
));
516 ret
= mcast_snooping_add_group(ms
, addr
, vlan
, port
);
522 ret
= mcast_snooping_leave_group(ms
, addr
, vlan
, port
);
529 record
= dp_packet_at(p
, offset
, sizeof(struct mld2_record
));
533 /* Only consider known record types. */
534 if (record
->type
>= IGMPV3_MODE_IS_INCLUDE
535 && record
->type
<= IGMPV3_BLOCK_OLD_SOURCES
) {
536 struct in6_addr maddr
;
537 memcpy(maddr
.s6_addr
, record
->maddr
.be16
, 16);
540 * If record is INCLUDE MODE and there are no sources, it's
541 * equivalent to a LEAVE.
543 if (record
->nsrcs
== htons(0)
544 && (record
->type
== IGMPV3_MODE_IS_INCLUDE
545 || record
->type
== IGMPV3_CHANGE_TO_INCLUDE_MODE
)) {
546 ret
= mcast_snooping_leave_group(ms
, addr
, vlan
, port
);
548 ret
= mcast_snooping_add_group(ms
, addr
, vlan
, port
);
554 offset
+= sizeof(*record
)
555 + ntohs(record
->nsrcs
) * sizeof(struct in6_addr
)
564 mcast_snooping_leave_group(struct mcast_snooping
*ms
,
565 const struct in6_addr
*addr
,
566 uint16_t vlan
, void *port
)
567 OVS_REQ_WRLOCK(ms
->rwlock
)
569 struct mcast_group
*grp
;
571 /* Ports flagged to forward Reports usually have more
572 * than one host behind it, so don't leave the group
573 * on the first message and just let it expire */
574 if (mcast_snooping_port_lookup(&ms
->rport_list
, port
)) {
578 grp
= mcast_snooping_lookup(ms
, addr
, vlan
);
579 if (grp
&& mcast_group_delete_bundle(ms
, grp
, port
)) {
580 ms
->need_revalidate
= true;
587 mcast_snooping_leave_group4(struct mcast_snooping
*ms
, ovs_be32 ip4
,
588 uint16_t vlan
, void *port
)
590 struct in6_addr addr
= in6_addr_mapped_ipv4(ip4
);
591 return mcast_snooping_leave_group(ms
, &addr
, vlan
, port
);
597 /* Returns the number of seconds since the multicast router
598 * was learned in a port. */
600 mcast_mrouter_age(const struct mcast_snooping
*ms OVS_UNUSED
,
601 const struct mcast_mrouter_bundle
*mrouter
)
603 time_t remaining
= mrouter
->expires
- time_now();
604 return MCAST_MROUTER_PORT_IDLE_TIME
- remaining
;
607 static struct mcast_mrouter_bundle
*
608 mcast_mrouter_from_lru_node(struct ovs_list
*list
)
610 return CONTAINER_OF(list
, struct mcast_mrouter_bundle
, mrouter_node
);
613 /* If the LRU list is not empty, stores the least-recently-used mrouter
614 * in '*m' and returns true. Otherwise, if the LRU list is empty,
615 * stores NULL in '*m' and return false. */
617 mrouter_get_lru(const struct mcast_snooping
*ms
,
618 struct mcast_mrouter_bundle
**m
)
619 OVS_REQ_RDLOCK(ms
->rwlock
)
621 if (!ovs_list_is_empty(&ms
->mrouter_lru
)) {
622 *m
= mcast_mrouter_from_lru_node(ms
->mrouter_lru
.next
);
630 static struct mcast_mrouter_bundle
*
631 mcast_snooping_mrouter_lookup(struct mcast_snooping
*ms
, uint16_t vlan
,
633 OVS_REQ_RDLOCK(ms
->rwlock
)
635 struct mcast_mrouter_bundle
*mrouter
;
637 LIST_FOR_EACH (mrouter
, mrouter_node
, &ms
->mrouter_lru
) {
638 if (mrouter
->vlan
== vlan
&& mrouter
->port
== port
) {
646 mcast_snooping_add_mrouter(struct mcast_snooping
*ms
, uint16_t vlan
,
648 OVS_REQ_WRLOCK(ms
->rwlock
)
650 struct mcast_mrouter_bundle
*mrouter
;
652 /* Avoid duplicate packets. */
653 if (mcast_snooping_port_lookup(&ms
->fport_list
, port
)) {
657 mrouter
= mcast_snooping_mrouter_lookup(ms
, vlan
, port
);
659 ovs_list_remove(&mrouter
->mrouter_node
);
661 mrouter
= xmalloc(sizeof *mrouter
);
662 mrouter
->vlan
= vlan
;
663 mrouter
->port
= port
;
664 COVERAGE_INC(mcast_snooping_learned
);
665 ms
->need_revalidate
= true;
668 mrouter
->expires
= time_now() + MCAST_MROUTER_PORT_IDLE_TIME
;
669 ovs_list_push_back(&ms
->mrouter_lru
, &mrouter
->mrouter_node
);
670 return ms
->need_revalidate
;
674 mcast_snooping_flush_mrouter(struct mcast_mrouter_bundle
*mrouter
)
676 ovs_list_remove(&mrouter
->mrouter_node
);
682 static struct mcast_port_bundle
*
683 mcast_port_from_list_node(struct ovs_list
*list
)
685 return CONTAINER_OF(list
, struct mcast_port_bundle
, node
);
688 /* If the list is not empty, stores the fport in '*f' and returns true.
689 * Otherwise, if the list is empty, stores NULL in '*f' and return false. */
691 mcast_snooping_port_get(const struct ovs_list
*list
,
692 struct mcast_port_bundle
**f
)
694 if (!ovs_list_is_empty(list
)) {
695 *f
= mcast_port_from_list_node(list
->next
);
703 static struct mcast_port_bundle
*
704 mcast_snooping_port_lookup(struct ovs_list
*list
, void *port
)
706 struct mcast_port_bundle
*pbundle
;
708 LIST_FOR_EACH (pbundle
, node
, list
) {
709 if (pbundle
->port
== port
) {
717 mcast_snooping_add_port(struct ovs_list
*list
, void *port
)
719 struct mcast_port_bundle
*pbundle
;
721 pbundle
= xmalloc(sizeof *pbundle
);
722 pbundle
->port
= port
;
723 ovs_list_insert(list
, &pbundle
->node
);
727 mcast_snooping_flush_port(struct mcast_port_bundle
*pbundle
)
729 ovs_list_remove(&pbundle
->node
);
736 mcast_snooping_set_port_flood(struct mcast_snooping
*ms
, void *port
,
738 OVS_REQ_WRLOCK(ms
->rwlock
)
740 struct mcast_port_bundle
*fbundle
;
742 fbundle
= mcast_snooping_port_lookup(&ms
->fport_list
, port
);
743 if (flood
&& !fbundle
) {
744 mcast_snooping_add_port(&ms
->fport_list
, port
);
745 ms
->need_revalidate
= true;
746 } else if (!flood
&& fbundle
) {
747 mcast_snooping_flush_port(fbundle
);
748 ms
->need_revalidate
= true;
752 /* Flood Reports ports. */
755 mcast_snooping_set_port_flood_reports(struct mcast_snooping
*ms
, void *port
,
757 OVS_REQ_WRLOCK(ms
->rwlock
)
759 struct mcast_port_bundle
*pbundle
;
761 pbundle
= mcast_snooping_port_lookup(&ms
->rport_list
, port
);
762 if (flood
&& !pbundle
) {
763 mcast_snooping_add_port(&ms
->rport_list
, port
);
764 ms
->need_revalidate
= true;
765 } else if (!flood
&& pbundle
) {
766 mcast_snooping_flush_port(pbundle
);
767 ms
->need_revalidate
= true;
774 mcast_snooping_mdb_flush__(struct mcast_snooping
*ms
)
775 OVS_REQ_WRLOCK(ms
->rwlock
)
777 struct mcast_group
*grp
;
778 struct mcast_mrouter_bundle
*mrouter
;
780 while (group_get_lru(ms
, &grp
)) {
781 mcast_snooping_flush_group(ms
, grp
);
784 hmap_shrink(&ms
->table
);
786 while (mrouter_get_lru(ms
, &mrouter
)) {
787 mcast_snooping_flush_mrouter(mrouter
);
792 mcast_snooping_mdb_flush(struct mcast_snooping
*ms
)
794 if (!mcast_snooping_enabled(ms
)) {
798 ovs_rwlock_wrlock(&ms
->rwlock
);
799 mcast_snooping_mdb_flush__(ms
);
800 ovs_rwlock_unlock(&ms
->rwlock
);
803 /* Flushes mdb and flood ports. */
805 mcast_snooping_flush__(struct mcast_snooping
*ms
)
806 OVS_REQ_WRLOCK(ms
->rwlock
)
808 struct mcast_group
*grp
;
809 struct mcast_mrouter_bundle
*mrouter
;
810 struct mcast_port_bundle
*pbundle
;
812 while (group_get_lru(ms
, &grp
)) {
813 mcast_snooping_flush_group(ms
, grp
);
816 hmap_shrink(&ms
->table
);
818 /* flush multicast routers */
819 while (mrouter_get_lru(ms
, &mrouter
)) {
820 mcast_snooping_flush_mrouter(mrouter
);
823 /* flush flood ports */
824 while (mcast_snooping_port_get(&ms
->fport_list
, &pbundle
)) {
825 mcast_snooping_flush_port(pbundle
);
828 /* flush flood report ports */
829 while (mcast_snooping_port_get(&ms
->rport_list
, &pbundle
)) {
830 mcast_snooping_flush_port(pbundle
);
835 mcast_snooping_flush(struct mcast_snooping
*ms
)
837 if (!mcast_snooping_enabled(ms
)) {
841 ovs_rwlock_wrlock(&ms
->rwlock
);
842 mcast_snooping_flush__(ms
);
843 ovs_rwlock_unlock(&ms
->rwlock
);
847 mcast_snooping_run__(struct mcast_snooping
*ms
)
848 OVS_REQ_WRLOCK(ms
->rwlock
)
850 bool need_revalidate
;
851 struct mcast_group
*grp
;
852 struct mcast_mrouter_bundle
*mrouter
;
855 while (group_get_lru(ms
, &grp
)) {
856 if (hmap_count(&ms
->table
) > ms
->max_entries
) {
857 mcast_snooping_flush_group(ms
, grp
);
859 if (!mcast_snooping_prune_expired(ms
, grp
)) {
865 hmap_shrink(&ms
->table
);
868 while (mrouter_get_lru(ms
, &mrouter
)
869 && time_now() >= mrouter
->expires
) {
870 mcast_snooping_flush_mrouter(mrouter
);
874 if (mrouter_expired
) {
875 ms
->need_revalidate
= true;
876 COVERAGE_ADD(mcast_snooping_expired
, mrouter_expired
);
879 need_revalidate
= ms
->need_revalidate
;
880 ms
->need_revalidate
= false;
881 return need_revalidate
;
884 /* Does periodic work required by 'ms'. Returns true if something changed
885 * that may require flow revalidation. */
887 mcast_snooping_run(struct mcast_snooping
*ms
)
889 bool need_revalidate
;
891 if (!mcast_snooping_enabled(ms
)) {
895 ovs_rwlock_wrlock(&ms
->rwlock
);
896 need_revalidate
= mcast_snooping_run__(ms
);
897 ovs_rwlock_unlock(&ms
->rwlock
);
899 return need_revalidate
;
903 mcast_snooping_wait__(struct mcast_snooping
*ms
)
904 OVS_REQ_RDLOCK(ms
->rwlock
)
906 if (hmap_count(&ms
->table
) > ms
->max_entries
907 || ms
->need_revalidate
) {
908 poll_immediate_wake();
910 struct mcast_group
*grp
;
911 struct mcast_group_bundle
*bundle
;
912 struct mcast_mrouter_bundle
*mrouter
;
913 long long int mrouter_msec
;
914 long long int msec
= 0;
916 if (!ovs_list_is_empty(&ms
->group_lru
)) {
917 grp
= mcast_group_from_lru_node(ms
->group_lru
.next
);
918 bundle
= mcast_group_bundle_from_lru_node(grp
->bundle_lru
.next
);
919 msec
= bundle
->expires
* 1000LL;
922 if (!ovs_list_is_empty(&ms
->mrouter_lru
)) {
923 mrouter
= mcast_mrouter_from_lru_node(ms
->mrouter_lru
.next
);
924 mrouter_msec
= mrouter
->expires
* 1000LL;
925 msec
= msec
? MIN(msec
, mrouter_msec
) : mrouter_msec
;
929 poll_timer_wait_until(msec
);
935 mcast_snooping_wait(struct mcast_snooping
*ms
)
937 if (!mcast_snooping_enabled(ms
)) {
941 ovs_rwlock_rdlock(&ms
->rwlock
);
942 mcast_snooping_wait__(ms
);
943 ovs_rwlock_unlock(&ms
->rwlock
);