On 22/06/2023 15:27, Nikolay Aleksandrov wrote: > On 20/06/2023 16:35, Johannes Nixdorf wrote: >> On Tue, Jun 20, 2023 at 09:55:31AM +0300, Nikolay Aleksandrov wrote: >>> On 6/19/23 10:14, Johannes Nixdorf wrote: >>>> +/* Set a FDB flag that implies the entry was not learned, and account >>>> + * for changes in the learned status. >>>> + */ >>>> +static void __fdb_set_flag_not_learned(struct net_bridge *br, >>>> + struct net_bridge_fdb_entry *fdb, >>>> + long nr) >>>> +{ >>>> + WARN_ON_ONCE(!(BIT(nr) & BR_FDB_NOT_LEARNED_MASK)); >>> >>> Please use *_bit >> >> Can you tell me which *_bit helper you had in mind? The shortest option I could >> come up with the ones I found seemed needlessly verbose and wasteful: >> >> static const unsigned long br_fdb_not_learned_mask = BR_FDB_NOT_LEARNED_MASK; >> ... >> WARN_ON_ONCE(test_bit(nr, &br_fdb_not_learned_mask)); >> >>>> + >>>> + /* learned before, but we set a flag that implies it's manually added */ >>>> + if (!(fdb->flags & BR_FDB_NOT_LEARNED_MASK)) >>> >>> Please use *_bit >> >> This will be fixed by the redesign to get rid of my use of hash_lock >> (proposed later in this mail), as I'll only have to test one bit and can >> use test_and_clear_bit then. >> >>>> + br->fdb_cur_learned_entries--; >>>> + set_bit(nr, &fdb->flags); >>>> +} >>> >>> Having a helper that conditionally decrements only is counterintuitive and >>> people can get confused. Either add 2 helpers for inc/dec and use >>> them where appropriate or don't use helpers at all. >> >> The *_set_bit helper can only cause the count to drop, as there >> is currently no flag that could turn a manually added entry back into >> a dynamically learned one. >> >> The analogous helper that increments the value would be *_clear_bit, >> which I did not add because it has no users. >> >>>> + spin_unlock_bh(&br->hash_lock); >>>> +} >>>> + >>>> /* When a static FDB entry is deleted, the HW address from that entry is >>>> * also removed from the bridge private HW address list and updates all >>>> * the ports with needed information. >>>> @@ -321,6 +353,8 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) >>>> static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f, >>>> bool swdev_notify) >>>> { >>>> + bool learned = !(f->flags & BR_FDB_NOT_LEARNED_MASK); >>> >>> *_bit >> >> I do not know a *_bit helper that would help me test the intersection >> of multiple bits on both sides. Do you have any in mind? >> >>>> + >>>> return fdb; >>>> } >>>> @@ -894,7 +940,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, >>>> } >>>> if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) >>>> - set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); >>>> + fdb_set_flag_not_learned(br, fdb, BR_FDB_ADDED_BY_USER); >>> >>> Unacceptable to take hash_lock and block all learning here, eventual >>> consistency is ok or some other method that is much lighter and doesn't >>> block all learning or requires a lock. >> >> At the time of writing v2, this seemed difficult because we want to test >> multiple bits and increment a counter, but remembering that clear_bit >> is never called for the bits I care about I came up with the following >> approach: >> >> a) Add a new flag BR_FDB_DYNAMIC_LEARNED, which is set to 1 iff >> BR_FDB_ADDED_BY_USER or BR_FDB_LOCAL are set in br_create. >> Every time BR_FDB_ADDED_BY_USER or BR_FDB_LOCAL is set, also clear >> BR_FDB_DYNAMIC_LEARNED, and decrement the count if it was 1 before. >> This solves the problem of testing two bits at once, and would not >> have been possible if we had a code path that could clear both bits, >> as it is not as easy to decide when to set BR_FDB_DYNAMIC_LEARNED >> again in that case. > > I think you can try without adding any new flags, the places that add dynamic > entries are known for the inc part of the problem, and an entry can become > local/added_by_user again only through well known paths as well. You may be able to > infer whether to inc/dec and make it work with careful fn argument passing. > Could you please look into that way? I'd prefer that we don't add new flags as > there are already so many. > To clarify - just look into it if it is possible and looks sane, if not do go ahead with the new flag. >> b) Replace the current count with an atomic_t. >> > > Sounds good. > >> I'll change it this way for v3. >> >>>> return -EMSGSIZE; >>>> #ifdef CONFIG_BRIDGE_VLAN_FILTERING >>>> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h >>>> index 2119729ded2b..df079191479e 100644 >>>> --- a/net/bridge/br_private.h >>>> +++ b/net/bridge/br_private.h >>>> @@ -275,6 +275,8 @@ enum { >>>> BR_FDB_LOCKED, >>>> }; >>>> +#define BR_FDB_NOT_LEARNED_MASK (BIT(BR_FDB_LOCAL) | BIT(BR_FDB_ADDED_BY_USER)) >>> >>> Not learned sounds confusing and doesn't accurately describe the entry. >>> BR_FDB_DYNAMIC_LEARNED perhaps or some other name, that doesn't cause >>> double negatives (not not learned). >> >> Your proposal would not have captured the mask, as it describes all the >> opposite cases, which were _not_ dynamically learned. >> >> But with the proposed new flag from the hash_lock comment we can trivially >> flip the meaning, so I went with your proposed name there. >