Skip to content

Commit

Permalink
igb: make use of the uta to allow for promiscous mode filter
Browse files Browse the repository at this point in the history
In order to support functions such as vlan tag stripping when SR-IOV is
enabled any given packet must match at least one filter.  However in the
case of promiscous mode being enabled on the PF the traffic routed to it
may not match any filters and is just sent to the PF by default.  In order
to make certain that this traffic is processed we can set all bits in the
UTA registers to create a pseudo promiscous mode filter that accepts all
packets.

Signed-off-by: Alexander Duyck <[email protected]>
Signed-off-by: Jeff Kirsher <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Alexander Duyck authored and davem330 committed Oct 6, 2009
1 parent 26ad917 commit 68d480c
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 47 deletions.
5 changes: 5 additions & 0 deletions drivers/net/igb/e1000_82575.c
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,11 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
for (i = 0; i < mac->mta_reg_count; i++)
array_wr32(E1000_MTA, i, 0);

/* Zero out the Unicast HASH table */
hw_dbg("Zeroing the UTA\n");
for (i = 0; i < mac->uta_reg_count; i++)
array_wr32(E1000_UTA, i, 0);

/* Setup link and flow control */
ret_val = igb_setup_link(hw);

Expand Down
1 change: 1 addition & 0 deletions drivers/net/igb/e1000_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ struct e1000_mac_info {
u16 ifs_ratio;
u16 ifs_step_size;
u16 mta_reg_count;
u16 uta_reg_count;

/* Maximum size of the MTA register table in all supported adapters */
#define MAX_MTA_REG 128
Expand Down
1 change: 1 addition & 0 deletions drivers/net/igb/e1000_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ enum {
#define E1000_QDE 0x02408 /* Queue Drop Enable - RW */
#define E1000_DTXSWC 0x03500 /* DMA Tx Switch Control - RW */
#define E1000_RPLOLR 0x05AF0 /* Replication Offload - RW */
#define E1000_UTA 0x0A000 /* Unicast Table Array - RW */
#define E1000_IOVTCL 0x05BBC /* IOV Control Register */
/* These act per VF so an array friendly macro is used */
#define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n)))
Expand Down
203 changes: 156 additions & 47 deletions drivers/net/igb/igb_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb,
static struct net_device_stats *igb_get_stats(struct net_device *);
static int igb_change_mtu(struct net_device *, int);
static int igb_set_mac(struct net_device *, void *);
static void igb_set_uta(struct igb_adapter *adapter);
static irqreturn_t igb_intr(int irq, void *);
static irqreturn_t igb_intr_msi(int irq, void *);
static irqreturn_t igb_msix_other(int irq, void *);
Expand Down Expand Up @@ -141,7 +142,6 @@ static inline void igb_set_vmolr(struct e1000_hw *hw, int vfn)

reg_data = rd32(E1000_VMOLR(vfn));
reg_data |= E1000_VMOLR_BAM | /* Accept broadcast */
E1000_VMOLR_ROPE | /* Accept packets matched in UTA */
E1000_VMOLR_ROMPE | /* Accept packets matched in MTA */
E1000_VMOLR_AUPE | /* Accept untagged packets */
E1000_VMOLR_STRVLAN; /* Strip vlan tags */
Expand Down Expand Up @@ -2286,6 +2286,9 @@ static void igb_configure_rx(struct igb_adapter *adapter)
/* Set the default pool for the PF's first queue */
igb_configure_vt_default_pool(adapter);

/* set UTA to appropriate mode */
igb_set_uta(adapter);

/* set the correct pool for the PF default MAC address in entry 0 */
igb_rar_set_qsel(adapter, adapter->hw.mac.addr, 0,
adapter->vfs_allocated_count);
Expand Down Expand Up @@ -2521,44 +2524,72 @@ static int igb_set_mac(struct net_device *netdev, void *p)
}

/**
* igb_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
* igb_write_mc_addr_list - write multicast addresses to MTA
* @netdev: network interface device structure
*
* The set_rx_mode entry point is called whenever the unicast or multicast
* address lists or the network interface flags are updated. This routine is
* responsible for configuring the hardware for proper unicast, multicast,
* promiscuous mode, and all-multi behavior.
* Writes multicast address list to the MTA hash table.
* Returns: -ENOMEM on failure
* 0 on no addresses written
* X on writing X addresses to MTA
**/
static void igb_set_rx_mode(struct net_device *netdev)
static int igb_write_mc_addr_list(struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
unsigned int rar_entries = hw->mac.rar_entry_count -
(adapter->vfs_allocated_count + 1);
struct dev_mc_list *mc_ptr = netdev->mc_list;
u8 *mta_list = NULL;
u32 rctl;
u8 *mta_list;
u32 vmolr = 0;
int i;

/* Check for Promiscuous and All Multicast modes */
rctl = rd32(E1000_RCTL);
if (!netdev->mc_count) {
/* nothing to program, so clear mc list */
igb_update_mc_addr_list(hw, NULL, 0);
igb_restore_vf_multicasts(adapter);
return 0;
}

if (netdev->flags & IFF_PROMISC) {
rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE);
rctl &= ~E1000_RCTL_VFE;
} else {
if (netdev->flags & IFF_ALLMULTI)
rctl |= E1000_RCTL_MPE;
else
rctl &= ~E1000_RCTL_MPE;
mta_list = kzalloc(netdev->mc_count * 6, GFP_ATOMIC);
if (!mta_list)
return -ENOMEM;

if (netdev->uc.count > rar_entries)
rctl |= E1000_RCTL_UPE;
else
rctl &= ~E1000_RCTL_UPE;
rctl |= E1000_RCTL_VFE;
/* set vmolr receive overflow multicast bit */
vmolr |= E1000_VMOLR_ROMPE;

/* The shared function expects a packed array of only addresses. */
mc_ptr = netdev->mc_list;

for (i = 0; i < netdev->mc_count; i++) {
if (!mc_ptr)
break;
memcpy(mta_list + (i*ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN);
mc_ptr = mc_ptr->next;
}
wr32(E1000_RCTL, rctl);
igb_update_mc_addr_list(hw, mta_list, i);
kfree(mta_list);

return netdev->mc_count;
}

/**
* igb_write_uc_addr_list - write unicast addresses to RAR table
* @netdev: network interface device structure
*
* Writes unicast address list to the RAR table.
* Returns: -ENOMEM on failure/insufficient address space
* 0 on no addresses written
* X on writing X addresses to the RAR table
**/
static int igb_write_uc_addr_list(struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
unsigned int vfn = adapter->vfs_allocated_count;
unsigned int rar_entries = hw->mac.rar_entry_count - (vfn + 1);
int count = 0;

/* return ENOMEM indicating insufficient memory for addresses */
if (netdev->uc.count > rar_entries)
return -ENOMEM;

if (netdev->uc.count && rar_entries) {
struct netdev_hw_addr *ha;
Expand All @@ -2567,7 +2598,8 @@ static void igb_set_rx_mode(struct net_device *netdev)
break;
igb_rar_set_qsel(adapter, ha->addr,
rar_entries--,
adapter->vfs_allocated_count);
vfn);
count++;
}
}
/* write the addresses in reverse order to avoid write combining */
Expand All @@ -2577,29 +2609,79 @@ static void igb_set_rx_mode(struct net_device *netdev)
}
wrfl();

if (!netdev->mc_count) {
/* nothing to program, so clear mc list */
igb_update_mc_addr_list(hw, NULL, 0);
igb_restore_vf_multicasts(adapter);
return;
return count;
}

/**
* igb_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
* @netdev: network interface device structure
*
* The set_rx_mode entry point is called whenever the unicast or multicast
* address lists or the network interface flags are updated. This routine is
* responsible for configuring the hardware for proper unicast, multicast,
* promiscuous mode, and all-multi behavior.
**/
static void igb_set_rx_mode(struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
unsigned int vfn = adapter->vfs_allocated_count;
u32 rctl, vmolr = 0;
int count;

/* Check for Promiscuous and All Multicast modes */
rctl = rd32(E1000_RCTL);

/* clear the effected bits */
rctl &= ~(E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_VFE);

if (netdev->flags & IFF_PROMISC) {
rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE);
vmolr |= (E1000_VMOLR_ROPE | E1000_VMOLR_MPME);
} else {
if (netdev->flags & IFF_ALLMULTI) {
rctl |= E1000_RCTL_MPE;
vmolr |= E1000_VMOLR_MPME;
} else {
/*
* Write addresses to the MTA, if the attempt fails
* then we should just turn on promiscous mode so
* that we can at least receive multicast traffic
*/
count = igb_write_mc_addr_list(netdev);
if (count < 0) {
rctl |= E1000_RCTL_MPE;
vmolr |= E1000_VMOLR_MPME;
} else if (count) {
vmolr |= E1000_VMOLR_ROMPE;
}
}
/*
* Write addresses to available RAR registers, if there is not
* sufficient space to store all the addresses then enable
* unicast promiscous mode
*/
count = igb_write_uc_addr_list(netdev);
if (count < 0) {
rctl |= E1000_RCTL_UPE;
vmolr |= E1000_VMOLR_ROPE;
}
rctl |= E1000_RCTL_VFE;
}
wr32(E1000_RCTL, rctl);

mta_list = kzalloc(netdev->mc_count * 6, GFP_ATOMIC);
if (!mta_list) {
dev_err(&adapter->pdev->dev,
"failed to allocate multicast filter list\n");
/*
* In order to support SR-IOV and eventually VMDq it is necessary to set
* the VMOLR to enable the appropriate modes. Without this workaround
* we will have issues with VLAN tag stripping not being done for frames
* that are only arriving because we are the default pool
*/
if (hw->mac.type < e1000_82576)
return;
}

/* The shared function expects a packed array of only addresses. */
for (i = 0; i < netdev->mc_count; i++) {
if (!mc_ptr)
break;
memcpy(mta_list + (i*ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN);
mc_ptr = mc_ptr->next;
}
igb_update_mc_addr_list(hw, mta_list, i);
kfree(mta_list);
vmolr |= rd32(E1000_VMOLR(vfn)) &
~(E1000_VMOLR_ROPE | E1000_VMOLR_MPME | E1000_VMOLR_ROMPE);
wr32(E1000_VMOLR(vfn), vmolr);
igb_restore_vf_multicasts(adapter);
}

Expand Down Expand Up @@ -4263,6 +4345,33 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
return retval;
}

/**
* igb_set_uta - Set unicast filter table address
* @adapter: board private structure
*
* The unicast table address is a register array of 32-bit registers.
* The table is meant to be used in a way similar to how the MTA is used
* however due to certain limitations in the hardware it is necessary to
* set all the hash bits to 1 and use the VMOLR ROPE bit as a promiscous
* enable bit to allow vlan tag stripping when promiscous mode is enabled
**/
static void igb_set_uta(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
int i;

/* The UTA table only exists on 82576 hardware and newer */
if (hw->mac.type < e1000_82576)
return;

/* we only need to do this if VMDq is enabled */
if (!adapter->vfs_allocated_count)
return;

for (i = 0; i < hw->mac.uta_reg_count; i++)
array_wr32(E1000_UTA, i, ~0);
}

/**
* igb_intr_msi - Interrupt Handler
* @irq: interrupt number
Expand Down

0 comments on commit 68d480c

Please sign in to comment.