static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
                            struct net_device *dev)
 {
+       int idx = 0, s_idx = cb->args[1], err = 0;
        struct net_bridge *br = netdev_priv(dev);
-       struct net_bridge_mdb_htable *mdb;
+       struct net_bridge_mdb_entry *mp;
        struct nlattr *nest, *nest2;
-       int i, err = 0;
-       int idx = 0, s_idx = cb->args[1];
 
        if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
                return 0;
 
-       mdb = rcu_dereference(br->mdb);
-       if (!mdb)
-               return 0;
-
        nest = nla_nest_start(skb, MDBA_MDB);
        if (nest == NULL)
                return -EMSGSIZE;
 
-       for (i = 0; i < mdb->max; i++) {
-               struct net_bridge_mdb_entry *mp;
+       hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
                struct net_bridge_port_group *p;
                struct net_bridge_port_group __rcu **pp;
                struct net_bridge_port *port;
 
-               hlist_for_each_entry_rcu(mp, &mdb->mhash[i], hlist[mdb->ver]) {
-                       if (idx < s_idx)
-                               goto skip;
+               if (idx < s_idx)
+                       goto skip;
 
-                       nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
-                       if (nest2 == NULL) {
-                               err = -EMSGSIZE;
-                               goto out;
-                       }
+               nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
+               if (!nest2) {
+                       err = -EMSGSIZE;
+                       break;
+               }
 
-                       for (pp = &mp->ports;
-                            (p = rcu_dereference(*pp)) != NULL;
-                             pp = &p->next) {
-                               struct nlattr *nest_ent;
-                               struct br_mdb_entry e;
-
-                               port = p->port;
-                               if (!port)
-                                       continue;
-
-                               memset(&e, 0, sizeof(e));
-                               e.ifindex = port->dev->ifindex;
-                               e.vid = p->addr.vid;
-                               __mdb_entry_fill_flags(&e, p->flags);
-                               if (p->addr.proto == htons(ETH_P_IP))
-                                       e.addr.u.ip4 = p->addr.u.ip4;
+               for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL;
+                     pp = &p->next) {
+                       struct nlattr *nest_ent;
+                       struct br_mdb_entry e;
+
+                       port = p->port;
+                       if (!port)
+                               continue;
+
+                       memset(&e, 0, sizeof(e));
+                       e.ifindex = port->dev->ifindex;
+                       e.vid = p->addr.vid;
+                       __mdb_entry_fill_flags(&e, p->flags);
+                       if (p->addr.proto == htons(ETH_P_IP))
+                               e.addr.u.ip4 = p->addr.u.ip4;
 #if IS_ENABLED(CONFIG_IPV6)
-                               if (p->addr.proto == htons(ETH_P_IPV6))
-                                       e.addr.u.ip6 = p->addr.u.ip6;
+                       if (p->addr.proto == htons(ETH_P_IPV6))
+                               e.addr.u.ip6 = p->addr.u.ip6;
 #endif
-                               e.addr.proto = p->addr.proto;
-                               nest_ent = nla_nest_start(skb,
-                                                         MDBA_MDB_ENTRY_INFO);
-                               if (!nest_ent) {
-                                       nla_nest_cancel(skb, nest2);
-                                       err = -EMSGSIZE;
-                                       goto out;
-                               }
-                               if (nla_put_nohdr(skb, sizeof(e), &e) ||
-                                   nla_put_u32(skb,
-                                               MDBA_MDB_EATTR_TIMER,
-                                               br_timer_value(&p->timer))) {
-                                       nla_nest_cancel(skb, nest_ent);
-                                       nla_nest_cancel(skb, nest2);
-                                       err = -EMSGSIZE;
-                                       goto out;
-                               }
-                               nla_nest_end(skb, nest_ent);
+                       e.addr.proto = p->addr.proto;
+                       nest_ent = nla_nest_start(skb, MDBA_MDB_ENTRY_INFO);
+                       if (!nest_ent) {
+                               nla_nest_cancel(skb, nest2);
+                               err = -EMSGSIZE;
+                               goto out;
                        }
-                       nla_nest_end(skb, nest2);
-               skip:
-                       idx++;
+                       if (nla_put_nohdr(skb, sizeof(e), &e) ||
+                           nla_put_u32(skb,
+                                       MDBA_MDB_EATTR_TIMER,
+                                       br_timer_value(&p->timer))) {
+                               nla_nest_cancel(skb, nest_ent);
+                               nla_nest_cancel(skb, nest2);
+                               err = -EMSGSIZE;
+                               goto out;
+                       }
+                       nla_nest_end(skb, nest_ent);
                }
+               nla_nest_end(skb, nest2);
+skip:
+               idx++;
        }
 
 out:
 
        rcu_read_lock();
 
-       /* In theory this could be wrapped to 0... */
-       cb->seq = net->dev_base_seq + br_mdb_rehash_seq;
+       cb->seq = net->dev_base_seq;
 
        for_each_netdev_rcu(net, dev) {
                if (dev->priv_flags & IFF_EBRIDGE) {
        struct br_mdb_complete_info *data = priv;
        struct net_bridge_port_group __rcu **pp;
        struct net_bridge_port_group *p;
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port *port = data->port;
        struct net_bridge *br = port->br;
                goto err;
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, &data->ip);
+       mp = br_mdb_ip_get(br, &data->ip);
        if (!mp)
                goto out;
        for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
-       struct net_bridge_mdb_htable *mdb;
        unsigned long now = jiffies;
        int err;
 
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, group);
+       mp = br_mdb_ip_get(br, group);
        if (!mp) {
-               mp = br_multicast_new_group(br, port, group);
+               mp = br_multicast_new_group(br, group);
                err = PTR_ERR_OR_ZERO(mp);
                if (err)
                        return err;
 
 static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
        __mdb_entry_to_br_ip(entry, &ip);
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-
-       mp = br_mdb_ip_get(mdb, &ip);
+       mp = br_mdb_ip_get(br, &ip);
        if (!mp)
                goto unlock;
 
 
 
 #include "br_private.h"
 
+static const struct rhashtable_params br_mdb_rht_params = {
+       .head_offset = offsetof(struct net_bridge_mdb_entry, rhnode),
+       .key_offset = offsetof(struct net_bridge_mdb_entry, addr),
+       .key_len = sizeof(struct br_ip),
+       .automatic_shrinking = true,
+       .locks_mul = 1,
+};
+
 static void br_multicast_start_querier(struct net_bridge *br,
                                       struct bridge_mcast_own_query *query);
 static void br_multicast_add_router(struct net_bridge *br,
                                         const struct in6_addr *group,
                                         __u16 vid, const unsigned char *src);
 #endif
-unsigned int br_mdb_rehash_seq;
 
 static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
 {
        return 0;
 }
 
-static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip,
-                               __u16 vid)
-{
-       return jhash_2words((__force u32)ip, vid, mdb->secret) & (mdb->max - 1);
-}
-
-#if IS_ENABLED(CONFIG_IPV6)
-static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb,
-                               const struct in6_addr *ip,
-                               __u16 vid)
+static struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br,
+                                                     struct br_ip *dst)
 {
-       return jhash_2words(ipv6_addr_hash(ip), vid,
-                           mdb->secret) & (mdb->max - 1);
+       return rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
 }
-#endif
 
-static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb,
-                            struct br_ip *ip)
-{
-       switch (ip->proto) {
-       case htons(ETH_P_IP):
-               return __br_ip4_hash(mdb, ip->u.ip4, ip->vid);
-#if IS_ENABLED(CONFIG_IPV6)
-       case htons(ETH_P_IPV6):
-               return __br_ip6_hash(mdb, &ip->u.ip6, ip->vid);
-#endif
-       }
-       return 0;
-}
-
-static struct net_bridge_mdb_entry *__br_mdb_ip_get(
-       struct net_bridge_mdb_htable *mdb, struct br_ip *dst, int hash)
+struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge *br,
+                                          struct br_ip *dst)
 {
-       struct net_bridge_mdb_entry *mp;
-
-       hlist_for_each_entry_rcu(mp, &mdb->mhash[hash], hlist[mdb->ver]) {
-               if (br_ip_equal(&mp->addr, dst))
-                       return mp;
-       }
+       struct net_bridge_mdb_entry *ent;
 
-       return NULL;
-}
+       lockdep_assert_held_once(&br->multicast_lock);
 
-struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge_mdb_htable *mdb,
-                                          struct br_ip *dst)
-{
-       if (!mdb)
-               return NULL;
+       rcu_read_lock();
+       ent = rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
+       rcu_read_unlock();
 
-       return __br_mdb_ip_get(mdb, dst, br_ip_hash(mdb, dst));
+       return ent;
 }
 
-static struct net_bridge_mdb_entry *br_mdb_ip4_get(
-       struct net_bridge_mdb_htable *mdb, __be32 dst, __u16 vid)
+static struct net_bridge_mdb_entry *br_mdb_ip4_get(struct net_bridge *br,
+                                                  __be32 dst, __u16 vid)
 {
        struct br_ip br_dst;
 
+       memset(&br_dst, 0, sizeof(br_dst));
        br_dst.u.ip4 = dst;
        br_dst.proto = htons(ETH_P_IP);
        br_dst.vid = vid;
 
-       return br_mdb_ip_get(mdb, &br_dst);
+       return br_mdb_ip_get(br, &br_dst);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-static struct net_bridge_mdb_entry *br_mdb_ip6_get(
-       struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst,
-       __u16 vid)
+static struct net_bridge_mdb_entry *br_mdb_ip6_get(struct net_bridge *br,
+                                                  const struct in6_addr *dst,
+                                                  __u16 vid)
 {
        struct br_ip br_dst;
 
+       memset(&br_dst, 0, sizeof(br_dst));
        br_dst.u.ip6 = *dst;
        br_dst.proto = htons(ETH_P_IPV6);
        br_dst.vid = vid;
 
-       return br_mdb_ip_get(mdb, &br_dst);
+       return br_mdb_ip_get(br, &br_dst);
 }
 #endif
 
 struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
                                        struct sk_buff *skb, u16 vid)
 {
-       struct net_bridge_mdb_htable *mdb = rcu_dereference(br->mdb);
        struct br_ip ip;
 
        if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
        if (BR_INPUT_SKB_CB(skb)->igmp)
                return NULL;
 
+       memset(&ip, 0, sizeof(ip));
        ip.proto = skb->protocol;
        ip.vid = vid;
 
                return NULL;
        }
 
-       return br_mdb_ip_get(mdb, &ip);
-}
-
-static void br_mdb_free(struct rcu_head *head)
-{
-       struct net_bridge_mdb_htable *mdb =
-               container_of(head, struct net_bridge_mdb_htable, rcu);
-       struct net_bridge_mdb_htable *old = mdb->old;
-
-       mdb->old = NULL;
-       kfree(old->mhash);
-       kfree(old);
-}
-
-static int br_mdb_copy(struct net_bridge_mdb_htable *new,
-                      struct net_bridge_mdb_htable *old,
-                      int elasticity)
-{
-       struct net_bridge_mdb_entry *mp;
-       int maxlen;
-       int len;
-       int i;
-
-       for (i = 0; i < old->max; i++)
-               hlist_for_each_entry(mp, &old->mhash[i], hlist[old->ver])
-                       hlist_add_head(&mp->hlist[new->ver],
-                                      &new->mhash[br_ip_hash(new, &mp->addr)]);
-
-       if (!elasticity)
-               return 0;
-
-       maxlen = 0;
-       for (i = 0; i < new->max; i++) {
-               len = 0;
-               hlist_for_each_entry(mp, &new->mhash[i], hlist[new->ver])
-                       len++;
-               if (len > maxlen)
-                       maxlen = len;
-       }
-
-       return maxlen > elasticity ? -EINVAL : 0;
+       return br_mdb_ip_get_rcu(br, &ip);
 }
 
 void br_multicast_free_pg(struct rcu_head *head)
 {
        struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer);
        struct net_bridge *br = mp->br;
-       struct net_bridge_mdb_htable *mdb;
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) || timer_pending(&mp->timer))
        if (mp->ports)
                goto out;
 
-       mdb = mlock_dereference(br->mdb, br);
-
-       hlist_del_rcu(&mp->hlist[mdb->ver]);
-       mdb->size--;
+       rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                              br_mdb_rht_params);
+       hlist_del_rcu(&mp->mdb_node);
 
        call_rcu_bh(&mp->rcu, br_multicast_free_group);
 
 static void br_multicast_del_pg(struct net_bridge *br,
                                struct net_bridge_port_group *pg)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
 
-       mdb = mlock_dereference(br->mdb, br);
-
-       mp = br_mdb_ip_get(mdb, &pg->addr);
+       mp = br_mdb_ip_get(br, &pg->addr);
        if (WARN_ON(!mp))
                return;
 
        spin_unlock(&br->multicast_lock);
 }
 
-static int br_mdb_rehash(struct net_bridge_mdb_htable __rcu **mdbp, int max,
-                        int elasticity)
-{
-       struct net_bridge_mdb_htable *old = rcu_dereference_protected(*mdbp, 1);
-       struct net_bridge_mdb_htable *mdb;
-       int err;
-
-       mdb = kmalloc(sizeof(*mdb), GFP_ATOMIC);
-       if (!mdb)
-               return -ENOMEM;
-
-       mdb->max = max;
-       mdb->old = old;
-
-       mdb->mhash = kcalloc(max, sizeof(*mdb->mhash), GFP_ATOMIC);
-       if (!mdb->mhash) {
-               kfree(mdb);
-               return -ENOMEM;
-       }
-
-       mdb->size = old ? old->size : 0;
-       mdb->ver = old ? old->ver ^ 1 : 0;
-
-       if (!old || elasticity)
-               get_random_bytes(&mdb->secret, sizeof(mdb->secret));
-       else
-               mdb->secret = old->secret;
-
-       if (!old)
-               goto out;
-
-       err = br_mdb_copy(mdb, old, elasticity);
-       if (err) {
-               kfree(mdb->mhash);
-               kfree(mdb);
-               return err;
-       }
-
-       br_mdb_rehash_seq++;
-       call_rcu_bh(&mdb->rcu, br_mdb_free);
-
-out:
-       rcu_assign_pointer(*mdbp, mdb);
-
-       return 0;
-}
-
 static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
                                                    __be32 group,
                                                    u8 *igmp_type)
        return NULL;
 }
 
-static struct net_bridge_mdb_entry *br_multicast_get_group(
-       struct net_bridge *br, struct net_bridge_port *port,
-       struct br_ip *group, int hash)
-{
-       struct net_bridge_mdb_htable *mdb;
-       struct net_bridge_mdb_entry *mp;
-       unsigned int count = 0;
-       unsigned int max;
-       int elasticity;
-       int err;
-
-       mdb = rcu_dereference_protected(br->mdb, 1);
-       hlist_for_each_entry(mp, &mdb->mhash[hash], hlist[mdb->ver]) {
-               count++;
-               if (unlikely(br_ip_equal(group, &mp->addr)))
-                       return mp;
-       }
-
-       elasticity = 0;
-       max = mdb->max;
-
-       if (unlikely(count > br->hash_elasticity && count)) {
-               if (net_ratelimit())
-                       br_info(br, "Multicast hash table "
-                               "chain limit reached: %s\n",
-                               port ? port->dev->name : br->dev->name);
-
-               elasticity = br->hash_elasticity;
-       }
-
-       if (mdb->size >= max) {
-               max *= 2;
-               if (unlikely(max > br->hash_max)) {
-                       br_warn(br, "Multicast hash table maximum of %d "
-                               "reached, disabling snooping: %s\n",
-                               br->hash_max,
-                               port ? port->dev->name : br->dev->name);
-                       err = -E2BIG;
-disable:
-                       br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
-                       goto err;
-               }
-       }
-
-       if (max > mdb->max || elasticity) {
-               if (mdb->old) {
-                       if (net_ratelimit())
-                               br_info(br, "Multicast hash table "
-                                       "on fire: %s\n",
-                                       port ? port->dev->name : br->dev->name);
-                       err = -EEXIST;
-                       goto err;
-               }
-
-               err = br_mdb_rehash(&br->mdb, max, elasticity);
-               if (err) {
-                       br_warn(br, "Cannot rehash multicast "
-                               "hash table, disabling snooping: %s, %d, %d\n",
-                               port ? port->dev->name : br->dev->name,
-                               mdb->size, err);
-                       goto disable;
-               }
-
-               err = -EAGAIN;
-               goto err;
-       }
-
-       return NULL;
-
-err:
-       mp = ERR_PTR(err);
-       return mp;
-}
-
 struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
-                                                   struct net_bridge_port *p,
                                                    struct br_ip *group)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
-       int hash;
        int err;
 
-       mdb = rcu_dereference_protected(br->mdb, 1);
-       if (!mdb) {
-               err = br_mdb_rehash(&br->mdb, BR_HASH_SIZE, 0);
-               if (err)
-                       return ERR_PTR(err);
-               goto rehash;
-       }
-
-       hash = br_ip_hash(mdb, group);
-       mp = br_multicast_get_group(br, p, group, hash);
-       switch (PTR_ERR(mp)) {
-       case 0:
-               break;
+       mp = br_mdb_ip_get(br, group);
+       if (mp)
+               return mp;
 
-       case -EAGAIN:
-rehash:
-               mdb = rcu_dereference_protected(br->mdb, 1);
-               hash = br_ip_hash(mdb, group);
-               break;
-
-       default:
-               goto out;
+       if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) {
+               br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
+               return ERR_PTR(-E2BIG);
        }
 
        mp = kzalloc(sizeof(*mp), GFP_ATOMIC);
        mp->br = br;
        mp->addr = *group;
        timer_setup(&mp->timer, br_multicast_group_expired, 0);
+       err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                                           br_mdb_rht_params);
+       if (err) {
+               kfree(mp);
+               mp = ERR_PTR(err);
+       } else {
+               hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list);
+       }
 
-       hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]);
-       mdb->size++;
-
-out:
        return mp;
 }
 
            (port && port->state == BR_STATE_DISABLED))
                goto out;
 
-       mp = br_multicast_new_group(br, port, group);
+       mp = br_multicast_new_group(br, group);
        err = PTR_ERR(mp);
        if (IS_ERR(mp))
                goto err;
        if (ipv6_addr_is_ll_all_nodes(group))
                return 0;
 
+       memset(&br_group, 0, sizeof(br_group));
        br_group.u.ip6 = *group;
        br_group.proto = htons(ETH_P_IPV6);
        br_group.vid = vid;
                goto out;
        }
 
-       mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
+       mp = br_mdb_ip4_get(br, group, vid);
        if (!mp)
                goto out;
 
                goto out;
        }
 
-       mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
+       mp = br_mdb_ip6_get(br, group, vid);
        if (!mp)
                goto out;
 
                         struct bridge_mcast_own_query *own_query,
                         const unsigned char *src)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        unsigned long now;
            (port && port->state == BR_STATE_DISABLED))
                goto out;
 
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, group);
+       mp = br_mdb_ip_get(br, group);
        if (!mp)
                goto out;
 
        timer_setup(&br->ip6_own_query.timer,
                    br_ip6_multicast_query_expired, 0);
 #endif
+       INIT_HLIST_HEAD(&br->mdb_list);
 }
 
 static void __br_multicast_open(struct net_bridge *br,
 
 void br_multicast_dev_del(struct net_bridge *br)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
-       struct hlist_node *n;
-       u32 ver;
-       int i;
+       struct hlist_node *tmp;
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-       if (!mdb)
-               goto out;
-
-       br->mdb = NULL;
-
-       ver = mdb->ver;
-       for (i = 0; i < mdb->max; i++) {
-               hlist_for_each_entry_safe(mp, n, &mdb->mhash[i],
-                                         hlist[ver]) {
-                       del_timer(&mp->timer);
-                       call_rcu_bh(&mp->rcu, br_multicast_free_group);
-               }
-       }
-
-       if (mdb->old) {
-               spin_unlock_bh(&br->multicast_lock);
-               rcu_barrier_bh();
-               spin_lock_bh(&br->multicast_lock);
-               WARN_ON(mdb->old);
+       hlist_for_each_entry_safe(mp, tmp, &br->mdb_list, mdb_node) {
+               del_timer(&mp->timer);
+               rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                                      br_mdb_rht_params);
+               hlist_del_rcu(&mp->mdb_node);
+               call_rcu_bh(&mp->rcu, br_multicast_free_group);
        }
-
-       mdb->old = mdb;
-       call_rcu_bh(&mdb->rcu, br_mdb_free);
-
-out:
        spin_unlock_bh(&br->multicast_lock);
+
+       rcu_barrier_bh();
 }
 
 int br_multicast_set_router(struct net_bridge *br, unsigned long val)
 
 int br_multicast_toggle(struct net_bridge *br, unsigned long val)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_port *port;
        int err = 0;
 
        if (!netif_running(br->dev))
                goto unlock;
 
-       mdb = mlock_dereference(br->mdb, br);
-       if (mdb) {
-               if (mdb->old) {
-                       err = -EEXIST;
-rollback:
-                       br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
-                       goto unlock;
-               }
-
-               err = br_mdb_rehash(&br->mdb, mdb->max,
-                                   br->hash_elasticity);
-               if (err)
-                       goto rollback;
-       }
-
        br_multicast_open(br);
        list_for_each_entry(port, &br->port_list, list)
                __br_multicast_enable_port(port);
        return 0;
 }
 
-int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
-{
-       int err = -EINVAL;
-       u32 old;
-       struct net_bridge_mdb_htable *mdb;
-
-       spin_lock_bh(&br->multicast_lock);
-       if (!is_power_of_2(val))
-               goto unlock;
-
-       mdb = mlock_dereference(br->mdb, br);
-       if (mdb && val < mdb->size)
-               goto unlock;
-
-       err = 0;
-
-       old = br->hash_max;
-       br->hash_max = val;
-
-       if (mdb) {
-               if (mdb->old) {
-                       err = -EEXIST;
-rollback:
-                       br->hash_max = old;
-                       goto unlock;
-               }
-
-               err = br_mdb_rehash(&br->mdb, br->hash_max,
-                                   br->hash_elasticity);
-               if (err)
-                       goto rollback;
-       }
-
-unlock:
-       spin_unlock_bh(&br->multicast_lock);
-
-       return err;
-}
-
 int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
 {
        /* Currently we support only version 2 and 3 */
        }
        memcpy(dest, &tdst, sizeof(*dest));
 }
+
+int br_mdb_hash_init(struct net_bridge *br)
+{
+       return rhashtable_init(&br->mdb_hash_tbl, &br_mdb_rht_params);
+}
+
+void br_mdb_hash_fini(struct net_bridge *br)
+{
+       rhashtable_destroy(&br->mdb_hash_tbl);
+}
 
 };
 
 struct net_bridge_mdb_entry {
-       struct hlist_node               hlist[2];
+       struct rhash_head               rhnode;
        struct net_bridge               *br;
        struct net_bridge_port_group __rcu *ports;
        struct rcu_head                 rcu;
        struct timer_list               timer;
        struct br_ip                    addr;
        bool                            host_joined;
-};
-
-struct net_bridge_mdb_htable {
-       struct hlist_head               *mhash;
-       struct rcu_head                 rcu;
-       struct net_bridge_mdb_htable    *old;
-       u32                             size;
-       u32                             max;
-       u32                             secret;
-       u32                             ver;
+       struct hlist_node               mdb_node;
 };
 
 struct net_bridge_port {
        unsigned long                   multicast_query_response_interval;
        unsigned long                   multicast_startup_query_interval;
 
-       struct net_bridge_mdb_htable __rcu *mdb;
+       struct rhashtable               mdb_hash_tbl;
+
+       struct hlist_head               mdb_list;
        struct hlist_head               router_list;
 
        struct timer_list               multicast_router_timer;
 
 /* br_multicast.c */
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
-extern unsigned int br_mdb_rehash_seq;
 int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
                     struct sk_buff *skb, u16 vid);
 struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
 int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val);
 #endif
 struct net_bridge_mdb_entry *
-br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst);
+br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
 struct net_bridge_mdb_entry *
-br_multicast_new_group(struct net_bridge *br, struct net_bridge_port *port,
-                      struct br_ip *group);
+br_multicast_new_group(struct net_bridge *br, struct br_ip *group);
 void br_multicast_free_pg(struct rcu_head *head);
 struct net_bridge_port_group *
 br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
                            struct net_bridge_port_group __rcu *next,
                            unsigned char flags, const unsigned char *src);
-void br_mdb_init(void);
-void br_mdb_uninit(void);
+int br_mdb_hash_init(struct net_bridge *br);
+void br_mdb_hash_fini(struct net_bridge *br);
 void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
                   struct br_ip *group, int type, u8 flags);
 void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
 void br_multicast_get_stats(const struct net_bridge *br,
                            const struct net_bridge_port *p,
                            struct br_mcast_stats *dest);
+void br_mdb_init(void);
+void br_mdb_uninit(void);
 
 #define mlock_dereference(X, br) \
        rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
 {
 }
 
+static inline int br_mdb_hash_init(struct net_bridge *br)
+{
+       return 0;
+}
+
+static inline void br_mdb_hash_fini(struct net_bridge *br)
+{
+}
+
 static inline void br_multicast_count(struct net_bridge *br,
                                      const struct net_bridge_port *p,
                                      const struct sk_buff *skb,