Merge tag 'media/v5.5-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-2.6-microblaze.git] / net / bridge / br_fdb.c
index b1d3248..4877a0d 100644 (file)
@@ -75,8 +75,9 @@ static inline unsigned long hold_time(const struct net_bridge *br)
 static inline int has_expired(const struct net_bridge *br,
                                  const struct net_bridge_fdb_entry *fdb)
 {
-       return !fdb->is_static && !fdb->added_by_external_learn &&
-               time_before_eq(fdb->updated + hold_time(br), jiffies);
+       return !test_bit(BR_FDB_STATIC, &fdb->flags) &&
+              !test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags) &&
+              time_before_eq(fdb->updated + hold_time(br), jiffies);
 }
 
 static void fdb_rcu_free(struct rcu_head *head)
@@ -197,7 +198,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f,
 {
        trace_fdb_delete(br, f);
 
-       if (f->is_static)
+       if (test_bit(BR_FDB_STATIC, &f->flags))
                fdb_del_hw_addr(br, f->key.addr.addr);
 
        hlist_del_init_rcu(&f->fdb_node);
@@ -224,7 +225,7 @@ static void fdb_delete_local(struct net_bridge *br,
                if (op != p && ether_addr_equal(op->dev->dev_addr, addr) &&
                    (!vid || br_vlan_find(vg, vid))) {
                        f->dst = op;
-                       f->added_by_user = 0;
+                       clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
                        return;
                }
        }
@@ -235,7 +236,7 @@ static void fdb_delete_local(struct net_bridge *br,
        if (p && ether_addr_equal(br->dev->dev_addr, addr) &&
            (!vid || (v && br_vlan_should_use(v)))) {
                f->dst = NULL;
-               f->added_by_user = 0;
+               clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
                return;
        }
 
@@ -250,7 +251,8 @@ void br_fdb_find_delete_local(struct net_bridge *br,
 
        spin_lock_bh(&br->hash_lock);
        f = br_fdb_find(br, addr, vid);
-       if (f && f->is_local && !f->added_by_user && f->dst == p)
+       if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
+           !test_bit(BR_FDB_ADDED_BY_USER, &f->flags) && f->dst == p)
                fdb_delete_local(br, p, f);
        spin_unlock_bh(&br->hash_lock);
 }
@@ -265,7 +267,8 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
        spin_lock_bh(&br->hash_lock);
        vg = nbp_vlan_group(p);
        hlist_for_each_entry(f, &br->fdb_list, fdb_node) {
-               if (f->dst == p && f->is_local && !f->added_by_user) {
+               if (f->dst == p && test_bit(BR_FDB_LOCAL, &f->flags) &&
+                   !test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) {
                        /* delete old one */
                        fdb_delete_local(br, p, f);
 
@@ -306,7 +309,8 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
 
        /* If old entry was unassociated with any port, then delete it. */
        f = br_fdb_find(br, br->dev->dev_addr, 0);
-       if (f && f->is_local && !f->dst && !f->added_by_user)
+       if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
+           !f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
                fdb_delete_local(br, NULL, f);
 
        fdb_insert(br, NULL, newaddr, 0);
@@ -321,7 +325,8 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
                if (!br_vlan_should_use(v))
                        continue;
                f = br_fdb_find(br, br->dev->dev_addr, v->vid);
-               if (f && f->is_local && !f->dst && !f->added_by_user)
+               if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
+                   !f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
                        fdb_delete_local(br, NULL, f);
                fdb_insert(br, NULL, newaddr, v->vid);
        }
@@ -346,7 +351,8 @@ void br_fdb_cleanup(struct work_struct *work)
        hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
                unsigned long this_timer;
 
-               if (f->is_static || f->added_by_external_learn)
+               if (test_bit(BR_FDB_STATIC, &f->flags) ||
+                   test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags))
                        continue;
                this_timer = f->updated + delay;
                if (time_after(this_timer, now)) {
@@ -373,7 +379,7 @@ void br_fdb_flush(struct net_bridge *br)
 
        spin_lock_bh(&br->hash_lock);
        hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
-               if (!f->is_static)
+               if (!test_bit(BR_FDB_STATIC, &f->flags))
                        fdb_delete(br, f, true);
        }
        spin_unlock_bh(&br->hash_lock);
@@ -397,10 +403,11 @@ void br_fdb_delete_by_port(struct net_bridge *br,
                        continue;
 
                if (!do_all)
-                       if (f->is_static || (vid && f->key.vlan_id != vid))
+                       if (test_bit(BR_FDB_STATIC, &f->flags) ||
+                           (vid && f->key.vlan_id != vid))
                                continue;
 
-               if (f->is_local)
+               if (test_bit(BR_FDB_LOCAL, &f->flags))
                        fdb_delete_local(br, p, f);
                else
                        fdb_delete(br, f, true);
@@ -469,8 +476,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
                fe->port_no = f->dst->port_no;
                fe->port_hi = f->dst->port_no >> 8;
 
-               fe->is_local = f->is_local;
-               if (!f->is_static)
+               fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags);
+               if (!test_bit(BR_FDB_STATIC, &f->flags))
                        fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated);
                ++fe;
                ++num;
@@ -484,8 +491,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
                                               struct net_bridge_port *source,
                                               const unsigned char *addr,
                                               __u16 vid,
-                                              unsigned char is_local,
-                                              unsigned char is_static)
+                                              unsigned long flags)
 {
        struct net_bridge_fdb_entry *fdb;
 
@@ -494,12 +500,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
                memcpy(fdb->key.addr.addr, addr, ETH_ALEN);
                fdb->dst = source;
                fdb->key.vlan_id = vid;
-               fdb->is_local = is_local;
-               fdb->is_static = is_static;
-               fdb->added_by_user = 0;
-               fdb->added_by_external_learn = 0;
-               fdb->offloaded = 0;
-               fdb->is_sticky = 0;
+               fdb->flags = flags;
                fdb->updated = fdb->used = jiffies;
                if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl,
                                                  &fdb->rhnode,
@@ -526,14 +527,15 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
                /* it is okay to have multiple ports with same
                 * address, just use the first one.
                 */
-               if (fdb->is_local)
+               if (test_bit(BR_FDB_LOCAL, &fdb->flags))
                        return 0;
                br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n",
                       source ? source->dev->name : br->dev->name, addr, vid);
                fdb_delete(br, fdb, true);
        }
 
-       fdb = fdb_create(br, source, addr, vid, 1, 1);
+       fdb = fdb_create(br, source, addr, vid,
+                        BIT(BR_FDB_LOCAL) | BIT(BR_FDB_STATIC));
        if (!fdb)
                return -ENOMEM;
 
@@ -555,7 +557,7 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
 }
 
 void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
-                  const unsigned char *addr, u16 vid, bool added_by_user)
+                  const unsigned char *addr, u16 vid, unsigned long flags)
 {
        struct net_bridge_fdb_entry *fdb;
        bool fdb_modified = false;
@@ -564,15 +566,10 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
        if (hold_time(br) == 0)
                return;
 
-       /* ignore packets unless we are using this port */
-       if (!(source->state == BR_STATE_LEARNING ||
-             source->state == BR_STATE_FORWARDING))
-               return;
-
        fdb = fdb_find_rcu(&br->fdb_hash_tbl, addr, vid);
        if (likely(fdb)) {
                /* attempt to update an entry for a local interface */
-               if (unlikely(fdb->is_local)) {
+               if (unlikely(test_bit(BR_FDB_LOCAL, &fdb->flags))) {
                        if (net_ratelimit())
                                br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n",
                                        source->dev->name, addr, vid);
@@ -580,30 +577,30 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
                        unsigned long now = jiffies;
 
                        /* fastpath: update of existing entry */
-                       if (unlikely(source != fdb->dst && !fdb->is_sticky)) {
+                       if (unlikely(source != fdb->dst &&
+                                    !test_bit(BR_FDB_STICKY, &fdb->flags))) {
                                fdb->dst = source;
                                fdb_modified = true;
                                /* Take over HW learned entry */
-                               if (unlikely(fdb->added_by_external_learn))
-                                       fdb->added_by_external_learn = 0;
+                               if (unlikely(test_bit(BR_FDB_ADDED_BY_EXT_LEARN,
+                                                     &fdb->flags)))
+                                       clear_bit(BR_FDB_ADDED_BY_EXT_LEARN,
+                                                 &fdb->flags);
                        }
                        if (now != fdb->updated)
                                fdb->updated = now;
-                       if (unlikely(added_by_user))
-                               fdb->added_by_user = 1;
+                       if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags)))
+                               set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
                        if (unlikely(fdb_modified)) {
-                               trace_br_fdb_update(br, source, addr, vid, added_by_user);
+                               trace_br_fdb_update(br, source, addr, vid, flags);
                                fdb_notify(br, fdb, RTM_NEWNEIGH, true);
                        }
                }
        } else {
                spin_lock(&br->hash_lock);
-               fdb = fdb_create(br, source, addr, vid, 0, 0);
+               fdb = fdb_create(br, source, addr, vid, flags);
                if (fdb) {
-                       if (unlikely(added_by_user))
-                               fdb->added_by_user = 1;
-                       trace_br_fdb_update(br, source, addr, vid,
-                                           added_by_user);
+                       trace_br_fdb_update(br, source, addr, vid, flags);
                        fdb_notify(br, fdb, RTM_NEWNEIGH, true);
                }
                /* else  we lose race and someone else inserts
@@ -616,9 +613,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
 static int fdb_to_nud(const struct net_bridge *br,
                      const struct net_bridge_fdb_entry *fdb)
 {
-       if (fdb->is_local)
+       if (test_bit(BR_FDB_LOCAL, &fdb->flags))
                return NUD_PERMANENT;
-       else if (fdb->is_static)
+       else if (test_bit(BR_FDB_STATIC, &fdb->flags))
                return NUD_NOARP;
        else if (has_expired(br, fdb))
                return NUD_STALE;
@@ -648,11 +645,11 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
        ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
        ndm->ndm_state   = fdb_to_nud(br, fdb);
 
-       if (fdb->offloaded)
+       if (test_bit(BR_FDB_OFFLOADED, &fdb->flags))
                ndm->ndm_flags |= NTF_OFFLOADED;
-       if (fdb->added_by_external_learn)
+       if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
                ndm->ndm_flags |= NTF_EXT_LEARNED;
-       if (fdb->is_sticky)
+       if (test_bit(BR_FDB_STICKY, &fdb->flags))
                ndm->ndm_flags |= NTF_STICKY;
 
        if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->key.addr))
@@ -799,7 +796,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
                         const u8 *addr, u16 state, u16 flags, u16 vid,
                         u8 ndm_flags)
 {
-       u8 is_sticky = !!(ndm_flags & NTF_STICKY);
+       bool is_sticky = !!(ndm_flags & NTF_STICKY);
        struct net_bridge_fdb_entry *fdb;
        bool modified = false;
 
@@ -823,7 +820,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
                if (!(flags & NLM_F_CREATE))
                        return -ENOENT;
 
-               fdb = fdb_create(br, source, addr, vid, 0, 0);
+               fdb = fdb_create(br, source, addr, vid, 0);
                if (!fdb)
                        return -ENOMEM;
 
@@ -840,34 +837,28 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
 
        if (fdb_to_nud(br, fdb) != state) {
                if (state & NUD_PERMANENT) {
-                       fdb->is_local = 1;
-                       if (!fdb->is_static) {
-                               fdb->is_static = 1;
+                       set_bit(BR_FDB_LOCAL, &fdb->flags);
+                       if (!test_and_set_bit(BR_FDB_STATIC, &fdb->flags))
                                fdb_add_hw_addr(br, addr);
-                       }
                } else if (state & NUD_NOARP) {
-                       fdb->is_local = 0;
-                       if (!fdb->is_static) {
-                               fdb->is_static = 1;
+                       clear_bit(BR_FDB_LOCAL, &fdb->flags);
+                       if (!test_and_set_bit(BR_FDB_STATIC, &fdb->flags))
                                fdb_add_hw_addr(br, addr);
-                       }
                } else {
-                       fdb->is_local = 0;
-                       if (fdb->is_static) {
-                               fdb->is_static = 0;
+                       clear_bit(BR_FDB_LOCAL, &fdb->flags);
+                       if (test_and_clear_bit(BR_FDB_STATIC, &fdb->flags))
                                fdb_del_hw_addr(br, addr);
-                       }
                }
 
                modified = true;
        }
 
-       if (is_sticky != fdb->is_sticky) {
-               fdb->is_sticky = is_sticky;
+       if (is_sticky != test_bit(BR_FDB_STICKY, &fdb->flags)) {
+               change_bit(BR_FDB_STICKY, &fdb->flags);
                modified = true;
        }
 
-       fdb->added_by_user = 1;
+       set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
 
        fdb->used = jiffies;
        if (modified) {
@@ -890,9 +881,12 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
                                br->dev->name);
                        return -EINVAL;
                }
+               if (!nbp_state_should_learn(p))
+                       return 0;
+
                local_bh_disable();
                rcu_read_lock();
-               br_fdb_update(br, p, addr, vid, true);
+               br_fdb_update(br, p, addr, vid, BIT(BR_FDB_ADDED_BY_USER));
                rcu_read_unlock();
                local_bh_enable();
        } else if (ndm->ndm_flags & NTF_EXT_LEARNED) {
@@ -1064,7 +1058,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
        rcu_read_lock();
        hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
                /* We only care for static entries */
-               if (!f->is_static)
+               if (!test_bit(BR_FDB_STATIC, &f->flags))
                        continue;
                err = dev_uc_add(p->dev, f->key.addr.addr);
                if (err)
@@ -1078,7 +1072,7 @@ done:
 rollback:
        hlist_for_each_entry_rcu(tmp, &br->fdb_list, fdb_node) {
                /* We only care for static entries */
-               if (!tmp->is_static)
+               if (!test_bit(BR_FDB_STATIC, &tmp->flags))
                        continue;
                if (tmp == f)
                        break;
@@ -1097,7 +1091,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
        rcu_read_lock();
        hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
                /* We only care for static entries */
-               if (!f->is_static)
+               if (!test_bit(BR_FDB_STATIC, &f->flags))
                        continue;
 
                dev_uc_del(p->dev, f->key.addr.addr);
@@ -1119,14 +1113,15 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
 
        fdb = br_fdb_find(br, addr, vid);
        if (!fdb) {
-               fdb = fdb_create(br, p, addr, vid, 0, 0);
+               unsigned long flags = BIT(BR_FDB_ADDED_BY_EXT_LEARN);
+
+               if (swdev_notify)
+                       flags |= BIT(BR_FDB_ADDED_BY_USER);
+               fdb = fdb_create(br, p, addr, vid, flags);
                if (!fdb) {
                        err = -ENOMEM;
                        goto err_unlock;
                }
-               if (swdev_notify)
-                       fdb->added_by_user = 1;
-               fdb->added_by_external_learn = 1;
                fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
        } else {
                fdb->updated = jiffies;
@@ -1136,17 +1131,17 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
                        modified = true;
                }
 
-               if (fdb->added_by_external_learn) {
+               if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) {
                        /* Refresh entry */
                        fdb->used = jiffies;
-               } else if (!fdb->added_by_user) {
+               } else if (!test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags)) {
                        /* Take over SW learned entry */
-                       fdb->added_by_external_learn = 1;
+                       set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags);
                        modified = true;
                }
 
                if (swdev_notify)
-                       fdb->added_by_user = 1;
+                       set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
 
                if (modified)
                        fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
@@ -1168,7 +1163,7 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
        spin_lock_bh(&br->hash_lock);
 
        fdb = br_fdb_find(br, addr, vid);
-       if (fdb && fdb->added_by_external_learn)
+       if (fdb && test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
                fdb_delete(br, fdb, swdev_notify);
        else
                err = -ENOENT;
@@ -1186,8 +1181,8 @@ void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
        spin_lock_bh(&br->hash_lock);
 
        fdb = br_fdb_find(br, addr, vid);
-       if (fdb)
-               fdb->offloaded = offloaded;
+       if (fdb && offloaded != test_bit(BR_FDB_OFFLOADED, &fdb->flags))
+               change_bit(BR_FDB_OFFLOADED, &fdb->flags);
 
        spin_unlock_bh(&br->hash_lock);
 }
@@ -1206,7 +1201,7 @@ void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
        spin_lock_bh(&p->br->hash_lock);
        hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) {
                if (f->dst == p && f->key.vlan_id == vid)
-                       f->offloaded = 0;
+                       clear_bit(BR_FDB_OFFLOADED, &f->flags);
        }
        spin_unlock_bh(&p->br->hash_lock);
 }