Commit c9f53e63 authored by Alexander Duyck's avatar Alexander Duyck Committed by Jeff Kirsher

ixgbe: Refactor MAC address configuration code

In the process of tracking down a memory leak when adding/removing FDB
entries I had to go through the MAC address configuration code for ixgbe.
In the process of doing so I found a number of issues that impacted
readability and performance.  This change updates the code in general to
clean it up so it becomes clear what each step is doing.  From what I can
tell there a couple of bugs cleaned up in this code.

First is the fact that the MAC addresses were being double counted for the
PF.  As a result once entries up to 63 had been used you could no longer
add additional filters.

A simple test case for this:
  for i in `seq 0 96`
  do
    ip link add link ens8 name mv$i type macvlan
    ip link set dev mv$i up
  done

Test script:
  ethregs -s 0:8.0 | grep -e "RAH" | grep 8000....$

When things are working correctly RAL/H registers 1 - 97 will be consumed.
In the failing case it will stop at 63 and prevent any further filters from
being added.
Signed-off-by: default avatarAlexander Duyck <aduyck@mirantis.com>
Tested-by: default avatarDarin Miller <darin.j.miller@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 50985b5f
...@@ -587,9 +587,10 @@ static inline u16 ixgbe_desc_unused(struct ixgbe_ring *ring) ...@@ -587,9 +587,10 @@ static inline u16 ixgbe_desc_unused(struct ixgbe_ring *ring)
struct ixgbe_mac_addr { struct ixgbe_mac_addr {
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
u16 queue; u16 pool;
u16 state; /* bitmask */ u16 state; /* bitmask */
}; };
#define IXGBE_MAC_STATE_DEFAULT 0x1 #define IXGBE_MAC_STATE_DEFAULT 0x1
#define IXGBE_MAC_STATE_MODIFIED 0x2 #define IXGBE_MAC_STATE_MODIFIED 0x2
#define IXGBE_MAC_STATE_IN_USE 0x4 #define IXGBE_MAC_STATE_IN_USE 0x4
...@@ -883,9 +884,9 @@ int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, ...@@ -883,9 +884,9 @@ int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id,
void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter); void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter);
#endif #endif
int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
u8 *addr, u16 queue); const u8 *addr, u16 queue);
int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter,
u8 *addr, u16 queue); const u8 *addr, u16 queue);
void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter); void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter);
netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *, netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *,
struct ixgbe_ring *); struct ixgbe_ring *);
......
...@@ -4031,124 +4031,156 @@ static int ixgbe_write_mc_addr_list(struct net_device *netdev) ...@@ -4031,124 +4031,156 @@ static int ixgbe_write_mc_addr_list(struct net_device *netdev)
#ifdef CONFIG_PCI_IOV #ifdef CONFIG_PCI_IOV
void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter) void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter)
{ {
struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
int i; int i;
for (i = 0; i < hw->mac.num_rar_entries; i++) {
if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE) for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
hw->mac.ops.set_rar(hw, i, adapter->mac_table[i].addr, mac_table->state &= ~IXGBE_MAC_STATE_MODIFIED;
adapter->mac_table[i].queue,
if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
hw->mac.ops.set_rar(hw, i,
mac_table->addr,
mac_table->pool,
IXGBE_RAH_AV); IXGBE_RAH_AV);
else else
hw->mac.ops.clear_rar(hw, i); hw->mac.ops.clear_rar(hw, i);
adapter->mac_table[i].state &= ~(IXGBE_MAC_STATE_MODIFIED);
} }
} }
#endif
#endif
static void ixgbe_sync_mac_table(struct ixgbe_adapter *adapter) static void ixgbe_sync_mac_table(struct ixgbe_adapter *adapter)
{ {
struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
int i; int i;
for (i = 0; i < hw->mac.num_rar_entries; i++) {
if (adapter->mac_table[i].state & IXGBE_MAC_STATE_MODIFIED) {
if (adapter->mac_table[i].state &
IXGBE_MAC_STATE_IN_USE)
hw->mac.ops.set_rar(hw, i,
adapter->mac_table[i].addr,
adapter->mac_table[i].queue,
IXGBE_RAH_AV);
else
hw->mac.ops.clear_rar(hw, i);
adapter->mac_table[i].state &= for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
~(IXGBE_MAC_STATE_MODIFIED); if (!(mac_table->state & IXGBE_MAC_STATE_MODIFIED))
} continue;
mac_table->state &= ~IXGBE_MAC_STATE_MODIFIED;
if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
hw->mac.ops.set_rar(hw, i,
mac_table->addr,
mac_table->pool,
IXGBE_RAH_AV);
else
hw->mac.ops.clear_rar(hw, i);
} }
} }
static void ixgbe_flush_sw_mac_table(struct ixgbe_adapter *adapter) static void ixgbe_flush_sw_mac_table(struct ixgbe_adapter *adapter)
{ {
int i; struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
int i;
for (i = 0; i < hw->mac.num_rar_entries; i++) { for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED; mac_table->state |= IXGBE_MAC_STATE_MODIFIED;
adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE; mac_table->state &= ~IXGBE_MAC_STATE_IN_USE;
eth_zero_addr(adapter->mac_table[i].addr);
adapter->mac_table[i].queue = 0;
} }
ixgbe_sync_mac_table(adapter); ixgbe_sync_mac_table(adapter);
} }
static int ixgbe_available_rars(struct ixgbe_adapter *adapter) static int ixgbe_available_rars(struct ixgbe_adapter *adapter, u16 pool)
{ {
struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
int i, count = 0; int i, count = 0;
for (i = 0; i < hw->mac.num_rar_entries; i++) { for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
if (adapter->mac_table[i].state == 0) /* do not count default RAR as available */
count++; if (mac_table->state & IXGBE_MAC_STATE_DEFAULT)
continue;
/* only count unused and addresses that belong to us */
if (mac_table->state & IXGBE_MAC_STATE_IN_USE) {
if (mac_table->pool != pool)
continue;
}
count++;
} }
return count; return count;
} }
/* this function destroys the first RAR entry */ /* this function destroys the first RAR entry */
static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter, static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter)
u8 *addr)
{ {
struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
memcpy(&adapter->mac_table[0].addr, addr, ETH_ALEN); memcpy(&mac_table->addr, hw->mac.addr, ETH_ALEN);
adapter->mac_table[0].queue = VMDQ_P(0); mac_table->pool = VMDQ_P(0);
adapter->mac_table[0].state = (IXGBE_MAC_STATE_DEFAULT |
IXGBE_MAC_STATE_IN_USE); mac_table->state = IXGBE_MAC_STATE_DEFAULT | IXGBE_MAC_STATE_IN_USE;
hw->mac.ops.set_rar(hw, 0, adapter->mac_table[0].addr,
adapter->mac_table[0].queue, hw->mac.ops.set_rar(hw, 0, mac_table->addr, mac_table->pool,
IXGBE_RAH_AV); IXGBE_RAH_AV);
} }
int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue) int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
const u8 *addr, u16 pool)
{ {
struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
int i; int i;
if (is_zero_ether_addr(addr)) if (is_zero_ether_addr(addr))
return -EINVAL; return -EINVAL;
for (i = 0; i < hw->mac.num_rar_entries; i++) { for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE) if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
continue; continue;
adapter->mac_table[i].state |= (IXGBE_MAC_STATE_MODIFIED |
IXGBE_MAC_STATE_IN_USE); ether_addr_copy(mac_table->addr, addr);
ether_addr_copy(adapter->mac_table[i].addr, addr); mac_table->pool = pool;
adapter->mac_table[i].queue = queue;
mac_table->state |= IXGBE_MAC_STATE_MODIFIED |
IXGBE_MAC_STATE_IN_USE;
ixgbe_sync_mac_table(adapter); ixgbe_sync_mac_table(adapter);
return i; return i;
} }
return -ENOMEM; return -ENOMEM;
} }
int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue) int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter,
const u8 *addr, u16 pool)
{ {
/* search table for addr, if found, set to 0 and sync */ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
int i;
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
int i;
if (is_zero_ether_addr(addr)) if (is_zero_ether_addr(addr))
return -EINVAL; return -EINVAL;
for (i = 0; i < hw->mac.num_rar_entries; i++) { /* search table for addr, if found clear IN_USE flag and sync */
if (ether_addr_equal(addr, adapter->mac_table[i].addr) && for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
adapter->mac_table[i].queue == queue) { /* we can only delete an entry if it is in use */
adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED; if (!(mac_table->state & IXGBE_MAC_STATE_IN_USE))
adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE; continue;
eth_zero_addr(adapter->mac_table[i].addr); /* we only care about entries that belong to the given pool */
adapter->mac_table[i].queue = 0; if (mac_table->pool != pool)
ixgbe_sync_mac_table(adapter); continue;
return 0; /* we only care about a specific MAC address */
} if (!ether_addr_equal(addr, mac_table->addr))
continue;
mac_table->state |= IXGBE_MAC_STATE_MODIFIED;
mac_table->state &= ~IXGBE_MAC_STATE_IN_USE;
ixgbe_sync_mac_table(adapter);
return 0;
} }
return -ENOMEM; return -ENOMEM;
} }
/** /**
...@@ -4166,7 +4198,7 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn) ...@@ -4166,7 +4198,7 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn)
int count = 0; int count = 0;
/* return ENOMEM indicating insufficient memory for addresses */ /* return ENOMEM indicating insufficient memory for addresses */
if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter)) if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter, vfn))
return -ENOMEM; return -ENOMEM;
if (!netdev_uc_empty(netdev)) { if (!netdev_uc_empty(netdev)) {
...@@ -5039,7 +5071,6 @@ void ixgbe_reset(struct ixgbe_adapter *adapter) ...@@ -5039,7 +5071,6 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev; struct net_device *netdev = adapter->netdev;
int err; int err;
u8 old_addr[ETH_ALEN];
if (ixgbe_removed(hw->hw_addr)) if (ixgbe_removed(hw->hw_addr))
return; return;
...@@ -5076,9 +5107,8 @@ void ixgbe_reset(struct ixgbe_adapter *adapter) ...@@ -5076,9 +5107,8 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state); clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state);
/* do not flush user set addresses */ /* do not flush user set addresses */
memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len);
ixgbe_flush_sw_mac_table(adapter); ixgbe_flush_sw_mac_table(adapter);
ixgbe_mac_set_default_filter(adapter, old_addr); ixgbe_mac_set_default_filter(adapter);
/* update SAN MAC vmdq pool selection */ /* update SAN MAC vmdq pool selection */
if (hw->mac.san_mac_rar_index) if (hw->mac.san_mac_rar_index)
...@@ -7656,17 +7686,16 @@ static int ixgbe_set_mac(struct net_device *netdev, void *p) ...@@ -7656,17 +7686,16 @@ static int ixgbe_set_mac(struct net_device *netdev, void *p)
struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw *hw = &adapter->hw;
struct sockaddr *addr = p; struct sockaddr *addr = p;
int ret;
if (!is_valid_ether_addr(addr->sa_data)) if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
ixgbe_del_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len); memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
ret = ixgbe_add_mac_filter(adapter, hw->mac.addr, VMDQ_P(0)); ixgbe_mac_set_default_filter(adapter);
return ret > 0 ? 0 : ret;
return 0;
} }
static int static int
...@@ -8867,7 +8896,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -8867,7 +8896,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_sw_init; goto err_sw_init;
} }
ixgbe_mac_set_default_filter(adapter, hw->mac.perm_addr); ixgbe_mac_set_default_filter(adapter);
setup_timer(&adapter->service_timer, &ixgbe_service_timer, setup_timer(&adapter->service_timer, &ixgbe_service_timer,
(unsigned long) adapter); (unsigned long) adapter);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment