e1000: Correct promisc multicast filter handling

There are a number of issues in the e1000 multicast filter handling
that have been present for a long time. Take the updated approach from
ixgbe(4) which does not have the issues.

The issues are outlined in the PR, in particular this solves crossing
over and under the hardware's filter limit, not programming the
hardware filter when we are above its limit, disabling SBP (show bad
packets) when the tunable is enabled and exiting promiscuous mode, and
an off-by-one error in the em_copy_maddr function.

PR:		140647
Reported by:	jtl
Reviewed by:	markj
MFC after:	1 month
Differential Revision:	https://reviews.freebsd.org/D29789
This commit is contained in:
Kevin Bowling 2021-04-16 16:15:50 -07:00
parent deecaa1445
commit 4b38eed76d

View File

@ -303,7 +303,6 @@ static int em_enable_phy_wakeup(struct adapter *);
static void em_disable_aspm(struct adapter *);
int em_intr(void *arg);
static void em_disable_promisc(if_ctx_t ctx);
/* MSI-X handlers */
static int em_if_msix_intr_assign(if_ctx_t, int);
@ -1641,11 +1640,20 @@ static int
em_if_set_promisc(if_ctx_t ctx, int flags)
{
struct adapter *adapter = iflib_get_softc(ctx);
struct ifnet *ifp = iflib_get_ifp(ctx);
u32 reg_rctl;
em_disable_promisc(ctx);
int mcnt = 0;
reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
reg_rctl &= ~(E1000_RCTL_SBP | E1000_RCTL_UPE);
if (flags & IFF_ALLMULTI)
mcnt = MAX_NUM_MULTICAST_ADDRESSES;
else
mcnt = min(if_llmaddr_count(ifp), MAX_NUM_MULTICAST_ADDRESSES);
if (mcnt < MAX_NUM_MULTICAST_ADDRESSES)
reg_rctl &= (~E1000_RCTL_MPE);
E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
if (flags & IFF_PROMISC) {
reg_rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE);
@ -1661,37 +1669,15 @@ em_if_set_promisc(if_ctx_t ctx, int flags)
return (0);
}
static void
em_disable_promisc(if_ctx_t ctx)
{
struct adapter *adapter = iflib_get_softc(ctx);
struct ifnet *ifp = iflib_get_ifp(ctx);
u32 reg_rctl;
int mcnt = 0;
reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
reg_rctl &= (~E1000_RCTL_UPE);
if (if_getflags(ifp) & IFF_ALLMULTI)
mcnt = MAX_NUM_MULTICAST_ADDRESSES;
else
mcnt = if_llmaddr_count(ifp);
/* Don't disable if in MAX groups */
if (mcnt < MAX_NUM_MULTICAST_ADDRESSES)
reg_rctl &= (~E1000_RCTL_MPE);
reg_rctl &= (~E1000_RCTL_SBP);
E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
}
static u_int
em_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
em_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int idx)
{
u8 *mta = arg;
if (cnt == MAX_NUM_MULTICAST_ADDRESSES)
return (1);
if (idx == MAX_NUM_MULTICAST_ADDRESSES)
return (0);
bcopy(LLADDR(sdl), &mta[cnt * ETHER_ADDR_LEN], ETHER_ADDR_LEN);
bcopy(LLADDR(sdl), &mta[idx * ETHER_ADDR_LEN], ETHER_ADDR_LEN);
return (1);
}
@ -1702,14 +1688,13 @@ em_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
* This routine is called whenever multicast address list is updated.
*
**********************************************************************/
static void
em_if_multi_set(if_ctx_t ctx)
{
struct adapter *adapter = iflib_get_softc(ctx);
struct ifnet *ifp = iflib_get_ifp(ctx);
u32 reg_rctl = 0;
u8 *mta; /* Multicast array memory */
u32 reg_rctl = 0;
int mcnt = 0;
IOCTL_DEBUGOUT("em_set_multi: begin");
@ -1729,11 +1714,20 @@ em_if_multi_set(if_ctx_t ctx)
mcnt = if_foreach_llmaddr(ifp, em_copy_maddr, mta);
if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES) {
reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
if (if_getflags(ifp) & IFF_PROMISC)
reg_rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE);
else if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES ||
if_getflags(ifp) & IFF_ALLMULTI) {
reg_rctl |= E1000_RCTL_MPE;
E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
reg_rctl &= ~E1000_RCTL_UPE;
} else
reg_rctl &= ~(E1000_RCTL_UPE | E1000_RCTL_MPE);
E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
if (mcnt < MAX_NUM_MULTICAST_ADDRESSES)
e1000_update_mc_addr_list(&adapter->hw, mta, mcnt);
if (adapter->hw.mac.type == e1000_82542 &&