mod_delayed_work(system_long_wq, &br->gc_work, work_delay);
 }
 
-/* Completely flush all dynamic entries in forwarding database.*/
-void br_fdb_flush(struct net_bridge *br)
+static bool __fdb_flush_matches(const struct net_bridge *br,
+                               const struct net_bridge_fdb_entry *f,
+                               const struct net_bridge_fdb_flush_desc *desc)
+{
+       const struct net_bridge_port *dst = READ_ONCE(f->dst);
+       int port_ifidx = dst ? dst->dev->ifindex : br->dev->ifindex;
+
+       if (desc->vlan_id && desc->vlan_id != f->key.vlan_id)
+               return false;
+       if (desc->port_ifindex && desc->port_ifindex != port_ifidx)
+               return false;
+       if (desc->flags_mask && (f->flags & desc->flags_mask) != desc->flags)
+               return false;
+
+       return true;
+}
+
+/* Flush forwarding database entries matching the description */
+void br_fdb_flush(struct net_bridge *br,
+                 const struct net_bridge_fdb_flush_desc *desc)
 {
        struct net_bridge_fdb_entry *f;
-       struct hlist_node *tmp;
 
-       spin_lock_bh(&br->hash_lock);
-       hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
-               if (!test_bit(BR_FDB_STATIC, &f->flags))
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
+               if (!__fdb_flush_matches(br, f, desc))
+                       continue;
+
+               spin_lock_bh(&br->hash_lock);
+               if (!hlist_unhashed(&f->fdb_node))
                        fdb_delete(br, f, true);
+               spin_unlock_bh(&br->hash_lock);
        }
-       spin_unlock_bh(&br->hash_lock);
+       rcu_read_unlock();
 }
 
 int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
                       struct net_device *dev, u16 vid,
                       struct netlink_ext_ack *extack)
 {
+       struct net_bridge_fdb_flush_desc desc = {
+               .flags_mask = BR_FDB_STATIC
+       };
        struct net_bridge_port *p = NULL;
        struct net_bridge *br;
 
                br = p->br;
        }
 
-       br_fdb_flush(br);
+       br_fdb_flush(br, &desc);
 
        return 0;
 }
 
                br_recalculate_fwd_mask(br);
        }
 
-       if (data[IFLA_BR_FDB_FLUSH])
-               br_fdb_flush(br);
+       if (data[IFLA_BR_FDB_FLUSH]) {
+               struct net_bridge_fdb_flush_desc desc = {
+                       .flags_mask = BR_FDB_STATIC
+               };
+
+               br_fdb_flush(br, &desc);
+       }
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
        if (data[IFLA_BR_MCAST_ROUTER]) {
 
        struct rcu_head                 rcu;
 };
 
+struct net_bridge_fdb_flush_desc {
+       unsigned long                   flags;
+       unsigned long                   flags_mask;
+       int                             port_ifindex;
+       u16                             vlan_id;
+};
+
 #define MDB_PG_FLAGS_PERMANENT BIT(0)
 #define MDB_PG_FLAGS_OFFLOAD   BIT(1)
 #define MDB_PG_FLAGS_FAST_LEAVE        BIT(2)
 void br_fdb_fini(void);
 int br_fdb_hash_init(struct net_bridge *br);
 void br_fdb_hash_fini(struct net_bridge *br);
-void br_fdb_flush(struct net_bridge *br);
+void br_fdb_flush(struct net_bridge *br,
+                 const struct net_bridge_fdb_flush_desc *desc);
 void br_fdb_find_delete_local(struct net_bridge *br,
                              const struct net_bridge_port *p,
                              const unsigned char *addr, u16 vid);