avoid calling if_enqueue from an smr critical section.
claudio@ is right that as a rule of thumb it is a bad idea to call
arbitrary code from an smr crit section because the scope of what
is called is very hard to keep in your head. in this particular
case sashan@ points out that if_enqueue can call vport handlers,
which calls if_vinput, which will push a packet into the network
stack, which will call pf and try to take an rwlock. you can't sleep
in an smr crit section.
SMRs in this situation are protecting references to ports in the
list of span and actual ports attached to a veb. when we needed to
send a packet to an unknown unicast, broadcast, or multicast packet
the code would SMR_TAILQ_FOREACH over all the ports, duplicating
the mbuf and calling if_enqueue against the port. span port handling
is basically the same, but we unconditionally send to them.
this replaces the SMR_TAILQ with maps (arrays) of ports. the veb
port map data structure contains a struct refcnt and the number of
ports. the forwarding paths use an SMR crit section to get a reference
to the map, increase the refcnt, and then leaves the smr crit section
before iterating over the array of ports in the map. after the
iteration it releases the refcnt.
this does add a couple of atomic ops in the forwarding path, but
only in the uncommon case (most packets are (should be) to known
unicast addresses), and it's only one set of ops for all ports
instead of ops per port. the known unicast case follows this pattern
too.
reported by Barbaros Bilek on bugs@
fix tested by me and hrvoje popovski
ok claudio@ sashan@ bluhm@ (who also did a lot of the initial analysis)