summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2020-05-23 16:47:41 -0700
committerDavid S. Miller <davem@davemloft.net>2020-05-23 16:47:41 -0700
commit098205f3c688885394ed1f670a6a7cb4a58728a3 (patch)
treee81be56b36f0e74e29f3135775d67569dcd98bb5
parent46c54f9500afad6128e19138c7d97fa4900331f6 (diff)
parente086ba2fccda4c196b84a167493f67f089d0ebdc (diff)
Merge branch '1GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue
Jeff Kirsher says: ==================== 1GbE Intel Wired LAN Driver Updates 2020-05-22 This series contains updates to e1000e, igc and igb. Many of the patches in this series are fixes, but many of the igc fixes are based on the recent filter rule handling Andre has been working, which will not backport to earlier/stable kernels. The remaining fixes for e1000e and igb have CC'd stable where applicable. Andre continue with his refactoring of the filter rule code to help with reducing the complexity, in multiple patches. Fix the inconsistent size of a struct field. Fixed an issue where filter rules stay active in the hardware, even after it was deleted, so make sure to disable the filter rule before deleting. Fixed an issue with NFC rules which were dropping valid multicast MAC address. Fixed how the NFC rules are restored after the NIC is reset or brought up, so that they are restored in the same order they were initially setup in. Fix a potential memory leak when the driver is unloaded and the NFC rules are not flushed from memory properly. Fixed how NFC rule validation handles when a request to overwrite an existing rule. Changed the locking around the NFC rule API calls from spin_locks to mutex locks to avoid unnecessary busy waiting on lock contention. Sasha clean up more unused code in the igc driver. Kai-Heng Feng from Canonical provides three fixes, first has igb report the speed and duplex as unknown when in runtime suspend. Fixed e1000e to pass up the error when disabling ULP mode. Fixed e1000e performance by disabling TSO by default for certain MACs. Vitaly disables S0ix entry and exit flows for ME systems. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c6
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c49
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c3
-rw-r--r--drivers/net/ethernet/intel/igc/igc.h26
-rw-r--r--drivers/net/ethernet/intel/igc/igc_defines.h4
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ethtool.c294
-rw-r--r--drivers/net/ethernet/intel/igc/igc_main.c246
7 files changed, 362 insertions, 266 deletions
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 735bf25952fc..f999cca37a8a 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -300,7 +300,11 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
* so forcibly disable it.
*/
hw->dev_spec.ich8lan.ulp_state = e1000_ulp_state_unknown;
- e1000_disable_ulp_lpt_lp(hw, true);
+ ret_val = e1000_disable_ulp_lpt_lp(hw, true);
+ if (ret_val) {
+ e_warn("Failed to disable ULP\n");
+ goto out;
+ }
ret_val = hw->phy.ops.acquire(hw);
if (ret_val) {
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index e0b074820b47..32f23a15ff64 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -107,6 +107,45 @@ static const struct e1000_reg_info e1000_reg_info_tbl[] = {
{0, NULL}
};
+struct e1000e_me_supported {
+ u16 device_id; /* supported device ID */
+};
+
+static const struct e1000e_me_supported me_supported[] = {
+ {E1000_DEV_ID_PCH_LPT_I217_LM},
+ {E1000_DEV_ID_PCH_LPTLP_I218_LM},
+ {E1000_DEV_ID_PCH_I218_LM2},
+ {E1000_DEV_ID_PCH_I218_LM3},
+ {E1000_DEV_ID_PCH_SPT_I219_LM},
+ {E1000_DEV_ID_PCH_SPT_I219_LM2},
+ {E1000_DEV_ID_PCH_LBG_I219_LM3},
+ {E1000_DEV_ID_PCH_SPT_I219_LM4},
+ {E1000_DEV_ID_PCH_SPT_I219_LM5},
+ {E1000_DEV_ID_PCH_CNP_I219_LM6},
+ {E1000_DEV_ID_PCH_CNP_I219_LM7},
+ {E1000_DEV_ID_PCH_ICP_I219_LM8},
+ {E1000_DEV_ID_PCH_ICP_I219_LM9},
+ {E1000_DEV_ID_PCH_CMP_I219_LM10},
+ {E1000_DEV_ID_PCH_CMP_I219_LM11},
+ {E1000_DEV_ID_PCH_CMP_I219_LM12},
+ {E1000_DEV_ID_PCH_TGP_I219_LM13},
+ {E1000_DEV_ID_PCH_TGP_I219_LM14},
+ {E1000_DEV_ID_PCH_TGP_I219_LM15},
+ {0}
+};
+
+static bool e1000e_check_me(u16 device_id)
+{
+ struct e1000e_me_supported *id;
+
+ for (id = (struct e1000e_me_supported *)me_supported;
+ id->device_id; id++)
+ if (device_id == id->device_id)
+ return true;
+
+ return false;
+}
+
/**
* __ew32_prepare - prepare to write to MAC CSR register on certain parts
* @hw: pointer to the HW structure
@@ -5294,6 +5333,10 @@ static void e1000_watchdog_task(struct work_struct *work)
/* oops */
break;
}
+ if (hw->mac.type == e1000_pch_spt) {
+ netdev->features &= ~NETIF_F_TSO;
+ netdev->features &= ~NETIF_F_TSO6;
+ }
}
/* enable transmits in the hardware, need to do this
@@ -6912,7 +6955,8 @@ static int e1000e_pm_suspend(struct device *dev)
e1000e_pm_thaw(dev);
/* Introduce S0ix implementation */
- if (hw->mac.type >= e1000_pch_cnp)
+ if (hw->mac.type >= e1000_pch_cnp &&
+ !e1000e_check_me(hw->adapter->pdev->device))
e1000e_s0ix_entry_flow(adapter);
return rc;
@@ -6927,7 +6971,8 @@ static int e1000e_pm_resume(struct device *dev)
int rc;
/* Introduce S0ix implementation */
- if (hw->mac.type >= e1000_pch_cnp)
+ if (hw->mac.type >= e1000_pch_cnp &&
+ !e1000e_check_me(hw->adapter->pdev->device))
e1000e_s0ix_exit_flow(adapter);
rc = __e1000_resume(pdev);
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 39d3b76a6f5d..2cd003c5ad43 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -143,7 +143,8 @@ static int igb_get_link_ksettings(struct net_device *netdev,
u32 speed;
u32 supported, advertising;
- status = rd32(E1000_STATUS);
+ status = pm_runtime_suspended(&adapter->pdev->dev) ?
+ 0 : rd32(E1000_STATUS);
if (hw->phy.media_type == e1000_media_type_copper) {
supported = (SUPPORTED_10baseT_Half |
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h
index fcc6261d7f67..14f9edaaaf83 100644
--- a/drivers/net/ethernet/intel/igc/igc.h
+++ b/drivers/net/ethernet/intel/igc/igc.h
@@ -190,8 +190,8 @@ struct igc_adapter {
/* Any access to elements in nfc_rule_list is protected by the
* nfc_rule_lock.
*/
- spinlock_t nfc_rule_lock;
- struct hlist_head nfc_rule_list;
+ struct mutex nfc_rule_lock;
+ struct list_head nfc_rule_list;
unsigned int nfc_rule_count;
u8 rss_indir_tbl[IGC_RETA_SIZE];
@@ -232,16 +232,6 @@ void igc_write_rss_indir_tbl(struct igc_adapter *adapter);
bool igc_has_link(struct igc_adapter *adapter);
void igc_reset(struct igc_adapter *adapter);
int igc_set_spd_dplx(struct igc_adapter *adapter, u32 spd, u8 dplx);
-int igc_add_mac_filter(struct igc_adapter *adapter,
- enum igc_mac_filter_type type, const u8 *addr,
- int queue);
-int igc_del_mac_filter(struct igc_adapter *adapter,
- enum igc_mac_filter_type type, const u8 *addr);
-int igc_add_vlan_prio_filter(struct igc_adapter *adapter, int prio,
- int queue);
-void igc_del_vlan_prio_filter(struct igc_adapter *adapter, int prio);
-int igc_add_etype_filter(struct igc_adapter *adapter, u16 etype, int queue);
-int igc_del_etype_filter(struct igc_adapter *adapter, u16 etype);
void igc_update_stats(struct igc_adapter *adapter);
/* igc_dump declarations */
@@ -461,9 +451,9 @@ struct igc_nfc_filter {
};
struct igc_nfc_rule {
- struct hlist_node nfc_node;
+ struct list_head list;
struct igc_nfc_filter filter;
- u16 sw_idx;
+ u32 location;
u16 action;
};
@@ -544,10 +534,10 @@ static inline s32 igc_read_phy_reg(struct igc_hw *hw, u32 offset, u16 *data)
}
void igc_reinit_locked(struct igc_adapter *);
-int igc_enable_nfc_rule(struct igc_adapter *adapter,
- const struct igc_nfc_rule *rule);
-int igc_disable_nfc_rule(struct igc_adapter *adapter,
- const struct igc_nfc_rule *rule);
+struct igc_nfc_rule *igc_get_nfc_rule(struct igc_adapter *adapter,
+ u32 location);
+int igc_add_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule);
+void igc_del_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule);
void igc_ptp_init(struct igc_adapter *adapter);
void igc_ptp_reset(struct igc_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h
index 45b567587ca9..3d8d40d6fa3f 100644
--- a/drivers/net/ethernet/intel/igc/igc_defines.h
+++ b/drivers/net/ethernet/intel/igc/igc_defines.h
@@ -265,13 +265,9 @@
#define IGC_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */
#define IGC_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */
#define IGC_TXD_CMD_EOP 0x01000000 /* End of Packet */
-#define IGC_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
#define IGC_TXD_CMD_IC 0x04000000 /* Insert Checksum */
-#define IGC_TXD_CMD_RS 0x08000000 /* Report Status */
-#define IGC_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */
#define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */
#define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */
-#define IGC_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */
#define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */
#define IGC_TXD_STAT_EC 0x00000002 /* Excess Collisions */
#define IGC_TXD_STAT_LC 0x00000004 /* Late Collisions */
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
index 66e0760a8f9e..946e775e34ae 100644
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
@@ -939,16 +939,11 @@ static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,
cmd->data = IGC_MAX_RXNFC_RULES;
- hlist_for_each_entry(rule, &adapter->nfc_rule_list, nfc_node) {
- if (fsp->location <= rule->sw_idx)
- break;
- }
-
- if (!rule || fsp->location != rule->sw_idx)
- return -EINVAL;
+ mutex_lock(&adapter->nfc_rule_lock);
- if (!rule->filter.match_flags)
- return -EINVAL;
+ rule = igc_get_nfc_rule(adapter, fsp->location);
+ if (!rule)
+ goto out;
fsp->flow_type = ETHER_FLOW;
fsp->ring_cookie = rule->action;
@@ -976,7 +971,12 @@ static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,
eth_broadcast_addr(fsp->m_u.ether_spec.h_source);
}
+ mutex_unlock(&adapter->nfc_rule_lock);
return 0;
+
+out:
+ mutex_unlock(&adapter->nfc_rule_lock);
+ return -EINVAL;
}
static int igc_ethtool_get_nfc_rules(struct igc_adapter *adapter,
@@ -988,13 +988,19 @@ static int igc_ethtool_get_nfc_rules(struct igc_adapter *adapter,
cmd->data = IGC_MAX_RXNFC_RULES;
- hlist_for_each_entry(rule, &adapter->nfc_rule_list, nfc_node) {
- if (cnt == cmd->rule_cnt)
+ mutex_lock(&adapter->nfc_rule_lock);
+
+ list_for_each_entry(rule, &adapter->nfc_rule_list, list) {
+ if (cnt == cmd->rule_cnt) {
+ mutex_unlock(&adapter->nfc_rule_lock);
return -EMSGSIZE;
- rule_locs[cnt] = rule->sw_idx;
+ }
+ rule_locs[cnt] = rule->location;
cnt++;
}
+ mutex_unlock(&adapter->nfc_rule_lock);
+
cmd->rule_cnt = cnt;
return 0;
@@ -1177,111 +1183,81 @@ static int igc_ethtool_set_rss_hash_opt(struct igc_adapter *adapter,
return 0;
}
-int igc_enable_nfc_rule(struct igc_adapter *adapter,
- const struct igc_nfc_rule *rule)
+static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule,
+ const struct ethtool_rx_flow_spec *fsp)
{
- int err = -EINVAL;
+ INIT_LIST_HEAD(&rule->list);
- if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) {
- err = igc_add_etype_filter(adapter, rule->filter.etype,
- rule->action);
- if (err)
- return err;
- }
+ rule->action = fsp->ring_cookie;
+ rule->location = fsp->location;
- if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
- err = igc_add_mac_filter(adapter, IGC_MAC_FILTER_TYPE_SRC,
- rule->filter.src_addr, rule->action);
- if (err)
- return err;
+ if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) {
+ rule->filter.vlan_tci = ntohs(fsp->h_ext.vlan_tci);
+ rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI;
}
- if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
- err = igc_add_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST,
- rule->filter.dst_addr, rule->action);
- if (err)
- return err;
+ if (fsp->m_u.ether_spec.h_proto == ETHER_TYPE_FULL_MASK) {
+ rule->filter.etype = ntohs(fsp->h_u.ether_spec.h_proto);
+ rule->filter.match_flags = IGC_FILTER_FLAG_ETHER_TYPE;
}
- if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
- int prio = (rule->filter.vlan_tci & VLAN_PRIO_MASK) >>
- VLAN_PRIO_SHIFT;
-
- err = igc_add_vlan_prio_filter(adapter, prio, rule->action);
- if (err)
- return err;
+ /* Both source and destination address filters only support the full
+ * mask.
+ */
+ if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_source)) {
+ rule->filter.match_flags |= IGC_FILTER_FLAG_SRC_MAC_ADDR;
+ ether_addr_copy(rule->filter.src_addr,
+ fsp->h_u.ether_spec.h_source);
}
- return 0;
-}
-
-int igc_disable_nfc_rule(struct igc_adapter *adapter,
- const struct igc_nfc_rule *rule)
-{
- if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE)
- igc_del_etype_filter(adapter, rule->filter.etype);
-
- if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
- int prio = (rule->filter.vlan_tci & VLAN_PRIO_MASK) >>
- VLAN_PRIO_SHIFT;
- igc_del_vlan_prio_filter(adapter, prio);
+ if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_dest)) {
+ rule->filter.match_flags |= IGC_FILTER_FLAG_DST_MAC_ADDR;
+ ether_addr_copy(rule->filter.dst_addr,
+ fsp->h_u.ether_spec.h_dest);
}
-
- if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR)
- igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_SRC,
- rule->filter.src_addr);
-
- if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR)
- igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST,
- rule->filter.dst_addr);
-
- return 0;
}
-static int igc_ethtool_update_nfc_rule(struct igc_adapter *adapter,
- struct igc_nfc_rule *input,
- u16 sw_idx)
+/**
+ * igc_ethtool_check_nfc_rule() - Check if NFC rule is valid
+ * @adapter: Pointer to adapter
+ * @rule: Rule under evaluation
+ *
+ * Rules with both destination and source MAC addresses are considered invalid
+ * since the driver doesn't support them.
+ *
+ * Also, if there is already another rule with the same filter in a different
+ * location, @rule is considered invalid.
+ *
+ * Context: Expects adapter->nfc_rule_lock to be held by caller.
+ *
+ * Return: 0 in case of success, negative errno code otherwise.
+ */
+static int igc_ethtool_check_nfc_rule(struct igc_adapter *adapter,
+ struct igc_nfc_rule *rule)
{
- struct igc_nfc_rule *rule, *parent;
- int err = -EINVAL;
-
- parent = NULL;
- rule = NULL;
+ struct net_device *dev = adapter->netdev;
+ u8 flags = rule->filter.match_flags;
+ struct igc_nfc_rule *tmp;
- hlist_for_each_entry(rule, &adapter->nfc_rule_list, nfc_node) {
- /* hash found, or no matching entry */
- if (rule->sw_idx >= sw_idx)
- break;
- parent = rule;
+ if (!flags) {
+ netdev_dbg(dev, "Rule with no match\n");
+ return -EINVAL;
}
- /* if there is an old rule occupying our place remove it */
- if (rule && rule->sw_idx == sw_idx) {
- if (!input)
- err = igc_disable_nfc_rule(adapter, rule);
-
- hlist_del(&rule->nfc_node);
- kfree(rule);
- adapter->nfc_rule_count--;
+ if (flags & IGC_FILTER_FLAG_DST_MAC_ADDR &&
+ flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
+ netdev_dbg(dev, "Filters with both dst and src are not supported\n");
+ return -EOPNOTSUPP;
}
- /* If no input this was a delete, err should be 0 if a rule was
- * successfully found and removed from the list else -EINVAL
- */
- if (!input)
- return err;
-
- /* initialize node */
- INIT_HLIST_NODE(&input->nfc_node);
-
- /* add filter to the list */
- if (parent)
- hlist_add_behind(&input->nfc_node, &parent->nfc_node);
- else
- hlist_add_head(&input->nfc_node, &adapter->nfc_rule_list);
-
- /* update counts */
- adapter->nfc_rule_count++;
+ list_for_each_entry(tmp, &adapter->nfc_rule_list, list) {
+ if (!memcmp(&rule->filter, &tmp->filter,
+ sizeof(rule->filter)) &&
+ tmp->location != rule->location) {
+ netdev_dbg(dev, "Rule already exists\n");
+ return -EEXIST;
+ }
+ }
return 0;
}
@@ -1292,99 +1268,60 @@ static int igc_ethtool_add_nfc_rule(struct igc_adapter *adapter,
struct net_device *netdev = adapter->netdev;
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
- struct igc_nfc_rule *rule, *tmp;
- int err = 0;
+ struct igc_nfc_rule *rule, *old_rule;
+ int err;
- if (!(netdev->hw_features & NETIF_F_NTUPLE))
+ if (!(netdev->hw_features & NETIF_F_NTUPLE)) {
+ netdev_dbg(netdev, "N-tuple filters disabled\n");
return -EOPNOTSUPP;
+ }
- /* Don't allow programming if the action is a queue greater than
- * the number of online Rx queues.
- */
- if (fsp->ring_cookie == RX_CLS_FLOW_DISC ||
- fsp->ring_cookie >= adapter->num_rx_queues) {
- netdev_err(netdev,
- "ethtool -N: The specified action is invalid\n");
- return -EINVAL;
+ if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW) {
+ netdev_dbg(netdev, "Only ethernet flow type is supported\n");
+ return -EOPNOTSUPP;
}
- /* Don't allow indexes to exist outside of available space */
- if (fsp->location >= IGC_MAX_RXNFC_RULES) {
- netdev_err(netdev, "Location out of range\n");
+ if ((fsp->flow_type & FLOW_EXT) &&
+ fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK)) {
+ netdev_dbg(netdev, "VLAN mask not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (fsp->ring_cookie >= adapter->num_rx_queues) {
+ netdev_dbg(netdev, "Invalid action\n");
return -EINVAL;
}
- if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW)
+ if (fsp->location >= IGC_MAX_RXNFC_RULES) {
+ netdev_dbg(netdev, "Invalid location\n");
return -EINVAL;
+ }
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
- if (fsp->m_u.ether_spec.h_proto == ETHER_TYPE_FULL_MASK) {
- rule->filter.etype = ntohs(fsp->h_u.ether_spec.h_proto);
- rule->filter.match_flags = IGC_FILTER_FLAG_ETHER_TYPE;
- }
+ igc_ethtool_init_nfc_rule(rule, fsp);
- /* Both source and destination address filters only support the full
- * mask.
- */
- if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_source)) {
- rule->filter.match_flags |= IGC_FILTER_FLAG_SRC_MAC_ADDR;
- ether_addr_copy(rule->filter.src_addr,
- fsp->h_u.ether_spec.h_source);
- }
+ mutex_lock(&adapter->nfc_rule_lock);
- if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_dest)) {
- rule->filter.match_flags |= IGC_FILTER_FLAG_DST_MAC_ADDR;
- ether_addr_copy(rule->filter.dst_addr,
- fsp->h_u.ether_spec.h_dest);
- }
-
- if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR &&
- rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
- netdev_dbg(netdev, "Filters with both dst and src are not supported\n");
- err = -EOPNOTSUPP;
- goto err_out;
- }
-
- if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) {
- if (fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK)) {
- netdev_dbg(netdev, "VLAN mask not supported\n");
- err = -EOPNOTSUPP;
- goto err_out;
- }
- rule->filter.vlan_tci = ntohs(fsp->h_ext.vlan_tci);
- rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI;
- }
-
- rule->action = fsp->ring_cookie;
- rule->sw_idx = fsp->location;
-
- spin_lock(&adapter->nfc_rule_lock);
+ err = igc_ethtool_check_nfc_rule(adapter, rule);
+ if (err)
+ goto err;
- hlist_for_each_entry(tmp, &adapter->nfc_rule_list, nfc_node) {
- if (!memcmp(&rule->filter, &tmp->filter,
- sizeof(rule->filter))) {
- err = -EEXIST;
- netdev_err(netdev,
- "ethtool: this filter is already set\n");
- goto err_out_w_lock;
- }
- }
+ old_rule = igc_get_nfc_rule(adapter, fsp->location);
+ if (old_rule)
+ igc_del_nfc_rule(adapter, old_rule);
- err = igc_enable_nfc_rule(adapter, rule);
+ err = igc_add_nfc_rule(adapter, rule);
if (err)
- goto err_out_w_lock;
+ goto err;
- igc_ethtool_update_nfc_rule(adapter, rule, rule->sw_idx);
-
- spin_unlock(&adapter->nfc_rule_lock);
+ mutex_unlock(&adapter->nfc_rule_lock);
return 0;
-err_out_w_lock:
- spin_unlock(&adapter->nfc_rule_lock);
-err_out:
+err:
+ mutex_unlock(&adapter->nfc_rule_lock);
kfree(rule);
return err;
}
@@ -1394,13 +1331,20 @@ static int igc_ethtool_del_nfc_rule(struct igc_adapter *adapter,
{
struct ethtool_rx_flow_spec *fsp =
(struct ethtool_rx_flow_spec *)&cmd->fs;
- int err;
+ struct igc_nfc_rule *rule;
- spin_lock(&adapter->nfc_rule_lock);
- err = igc_ethtool_update_nfc_rule(adapter, NULL, fsp->location);
- spin_unlock(&adapter->nfc_rule_lock);
+ mutex_lock(&adapter->nfc_rule_lock);
- return err;
+ rule = igc_get_nfc_rule(adapter, fsp->location);
+ if (!rule) {
+ mutex_unlock(&adapter->nfc_rule_lock);
+ return -EINVAL;
+ }
+
+ igc_del_nfc_rule(adapter, rule);
+
+ mutex_unlock(&adapter->nfc_rule_lock);
+ return 0;
}
static int igc_ethtool_set_rxnfc(struct net_device *dev,
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index f48d6127a220..97d26991c87e 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -2174,18 +2174,6 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget)
return !!budget;
}
-static void igc_restore_nfc_rules(struct igc_adapter *adapter)
-{
- struct igc_nfc_rule *rule;
-
- spin_lock(&adapter->nfc_rule_lock);
-
- hlist_for_each_entry(rule, &adapter->nfc_rule_list, nfc_node)
- igc_enable_nfc_rule(adapter, rule);
-
- spin_unlock(&adapter->nfc_rule_lock);
-}
-
static int igc_find_mac_filter(struct igc_adapter *adapter,
enum igc_mac_filter_type type, const u8 *addr)
{
@@ -2242,16 +2230,13 @@ static int igc_get_avail_mac_filter_slot(struct igc_adapter *adapter)
*
* Return: 0 in case of success, negative errno code otherwise.
*/
-int igc_add_mac_filter(struct igc_adapter *adapter,
- enum igc_mac_filter_type type, const u8 *addr,
- int queue)
+static int igc_add_mac_filter(struct igc_adapter *adapter,
+ enum igc_mac_filter_type type, const u8 *addr,
+ int queue)
{
struct net_device *dev = adapter->netdev;
int index;
- if (!is_valid_ether_addr(addr))
- return -EINVAL;
-
index = igc_find_mac_filter(adapter, type, addr);
if (index >= 0)
goto update_filter;
@@ -2274,21 +2259,16 @@ update_filter:
* @adapter: Pointer to adapter where the filter should be deleted from
* @type: MAC address filter type (source or destination)
* @addr: MAC address
- *
- * Return: 0 in case of success, negative errno code otherwise.
*/
-int igc_del_mac_filter(struct igc_adapter *adapter,
- enum igc_mac_filter_type type, const u8 *addr)
+static void igc_del_mac_filter(struct igc_adapter *adapter,
+ enum igc_mac_filter_type type, const u8 *addr)
{
struct net_device *dev = adapter->netdev;
int index;
- if (!is_valid_ether_addr(addr))
- return -EINVAL;
-
index = igc_find_mac_filter(adapter, type, addr);
if (index < 0)
- return -ENOENT;
+ return;
if (index == 0) {
/* If this is the default filter, we don't actually delete it.
@@ -2306,8 +2286,6 @@ int igc_del_mac_filter(struct igc_adapter *adapter,
igc_clear_mac_filter_hw(adapter, index);
}
-
- return 0;
}
/**
@@ -2318,7 +2296,8 @@ int igc_del_mac_filter(struct igc_adapter *adapter,
*
* Return: 0 in case of success, negative errno code otherwise.
*/
-int igc_add_vlan_prio_filter(struct igc_adapter *adapter, int prio, int queue)
+static int igc_add_vlan_prio_filter(struct igc_adapter *adapter, int prio,
+ int queue)
{
struct net_device *dev = adapter->netdev;
struct igc_hw *hw = &adapter->hw;
@@ -2346,7 +2325,7 @@ int igc_add_vlan_prio_filter(struct igc_adapter *adapter, int prio, int queue)
* @adapter: Pointer to adapter where the filter should be deleted from
* @prio: VLAN priority value
*/
-void igc_del_vlan_prio_filter(struct igc_adapter *adapter, int prio)
+static void igc_del_vlan_prio_filter(struct igc_adapter *adapter, int prio)
{
struct igc_hw *hw = &adapter->hw;
u32 vlanpqf;
@@ -2387,7 +2366,8 @@ static int igc_get_avail_etype_filter_slot(struct igc_adapter *adapter)
*
* Return: 0 in case of success, negative errno code otherwise.
*/
-int igc_add_etype_filter(struct igc_adapter *adapter, u16 etype, int queue)
+static int igc_add_etype_filter(struct igc_adapter *adapter, u16 etype,
+ int queue)
{
struct igc_hw *hw = &adapter->hw;
int index;
@@ -2436,25 +2416,181 @@ static int igc_find_etype_filter(struct igc_adapter *adapter, u16 etype)
* igc_del_etype_filter() - Delete ethertype filter
* @adapter: Pointer to adapter where the filter should be deleted from
* @etype: Ethertype value
- *
- * Return: 0 in case of success, negative errno code otherwise.
*/
-int igc_del_etype_filter(struct igc_adapter *adapter, u16 etype)
+static void igc_del_etype_filter(struct igc_adapter *adapter, u16 etype)
{
struct igc_hw *hw = &adapter->hw;
int index;
index = igc_find_etype_filter(adapter, etype);
if (index < 0)
- return -ENOENT;
+ return;
wr32(IGC_ETQF(index), 0);
netdev_dbg(adapter->netdev, "Delete ethertype filter: etype %04x\n",
etype);
+}
+
+static int igc_enable_nfc_rule(struct igc_adapter *adapter,
+ const struct igc_nfc_rule *rule)
+{
+ int err;
+
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) {
+ err = igc_add_etype_filter(adapter, rule->filter.etype,
+ rule->action);
+ if (err)
+ return err;
+ }
+
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
+ err = igc_add_mac_filter(adapter, IGC_MAC_FILTER_TYPE_SRC,
+ rule->filter.src_addr, rule->action);
+ if (err)
+ return err;
+ }
+
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
+ err = igc_add_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST,
+ rule->filter.dst_addr, rule->action);
+ if (err)
+ return err;
+ }
+
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
+ int prio = (rule->filter.vlan_tci & VLAN_PRIO_MASK) >>
+ VLAN_PRIO_SHIFT;
+
+ err = igc_add_vlan_prio_filter(adapter, prio, rule->action);
+ if (err)
+ return err;
+ }
+
return 0;
}
+static void igc_disable_nfc_rule(struct igc_adapter *adapter,
+ const struct igc_nfc_rule *rule)
+{
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE)
+ igc_del_etype_filter(adapter, rule->filter.etype);
+
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
+ int prio = (rule->filter.vlan_tci & VLAN_PRIO_MASK) >>
+ VLAN_PRIO_SHIFT;
+
+ igc_del_vlan_prio_filter(adapter, prio);
+ }
+
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR)
+ igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_SRC,
+ rule->filter.src_addr);
+
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR)
+ igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST,
+ rule->filter.dst_addr);
+}
+
+/**
+ * igc_get_nfc_rule() - Get NFC rule
+ * @adapter: Pointer to adapter
+ * @location: Rule location
+ *
+ * Context: Expects adapter->nfc_rule_lock to be held by caller.
+ *
+ * Return: Pointer to NFC rule at @location. If not found, NULL.
+ */
+struct igc_nfc_rule *igc_get_nfc_rule(struct igc_adapter *adapter,
+ u32 location)
+{
+ struct igc_nfc_rule *rule;
+
+ list_for_each_entry(rule, &adapter->nfc_rule_list, list) {
+ if (rule->location == location)
+ return rule;
+ if (rule->location > location)
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * igc_del_nfc_rule() - Delete NFC rule
+ * @adapter: Pointer to adapter
+ * @rule: Pointer to rule to be deleted
+ *
+ * Disable NFC rule in hardware and delete it from adapter.
+ *
+ * Context: Expects adapter->nfc_rule_lock to be held by caller.
+ */
+void igc_del_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule)
+{
+ igc_disable_nfc_rule(adapter, rule);
+
+ list_del(&rule->list);
+ adapter->nfc_rule_count--;
+
+ kfree(rule);
+}
+
+static void igc_flush_nfc_rules(struct igc_adapter *adapter)
+{
+ struct igc_nfc_rule *rule, *tmp;
+
+ mutex_lock(&adapter->nfc_rule_lock);
+
+ list_for_each_entry_safe(rule, tmp, &adapter->nfc_rule_list, list)
+ igc_del_nfc_rule(adapter, rule);
+
+ mutex_unlock(&adapter->nfc_rule_lock);
+}
+
+/**
+ * igc_add_nfc_rule() - Add NFC rule
+ * @adapter: Pointer to adapter
+ * @rule: Pointer to rule to be added
+ *
+ * Enable NFC rule in hardware and add it to adapter.
+ *
+ * Context: Expects adapter->nfc_rule_lock to be held by caller.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int igc_add_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule)
+{
+ struct igc_nfc_rule *pred, *cur;
+ int err;
+
+ err = igc_enable_nfc_rule(adapter, rule);
+ if (err)
+ return err;
+
+ pred = NULL;
+ list_for_each_entry(cur, &adapter->nfc_rule_list, list) {
+ if (cur->location >= rule->location)
+ break;
+ pred = cur;
+ }
+
+ list_add(&rule->list, pred ? &pred->list : &adapter->nfc_rule_list);
+ adapter->nfc_rule_count++;
+ return 0;
+}
+
+static void igc_restore_nfc_rules(struct igc_adapter *adapter)
+{
+ struct igc_nfc_rule *rule;
+
+ mutex_lock(&adapter->nfc_rule_lock);
+
+ list_for_each_entry_reverse(rule, &adapter->nfc_rule_list, list)
+ igc_enable_nfc_rule(adapter, rule);
+
+ mutex_unlock(&adapter->nfc_rule_lock);
+}
+
static int igc_uc_sync(struct net_device *netdev, const unsigned char *addr)
{
struct igc_adapter *adapter = netdev_priv(netdev);
@@ -2466,7 +2602,8 @@ static int igc_uc_unsync(struct net_device *netdev, const unsigned char *addr)
{
struct igc_adapter *adapter = netdev_priv(netdev);
- return igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST, addr);
+ igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST, addr);
+ return 0;
}
/**
@@ -3424,7 +3561,10 @@ static int igc_sw_init(struct igc_adapter *adapter)
VLAN_HLEN;
adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
- spin_lock_init(&adapter->nfc_rule_lock);
+ mutex_init(&adapter->nfc_rule_lock);
+ INIT_LIST_HEAD(&adapter->nfc_rule_list);
+ adapter->nfc_rule_count = 0;
+
spin_lock_init(&adapter->stats64_lock);
/* Assume MSI-X interrupts, will be checked during IRQ allocation */
adapter->flags |= IGC_FLAG_HAS_MSIX;
@@ -3651,18 +3791,6 @@ void igc_update_stats(struct igc_adapter *adapter)
adapter->stats.mgpdc += rd32(IGC_MGTPDC);
}
-static void igc_nfc_rule_exit(struct igc_adapter *adapter)
-{
- struct igc_nfc_rule *rule;
-
- spin_lock(&adapter->nfc_rule_lock);
-
- hlist_for_each_entry(rule, &adapter->nfc_rule_list, nfc_node)
- igc_disable_nfc_rule(adapter, rule);
-
- spin_unlock(&adapter->nfc_rule_lock);
-}
-
/**
* igc_down - Close the interface
* @adapter: board private structure
@@ -3681,8 +3809,6 @@ void igc_down(struct igc_adapter *adapter)
wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN);
/* flush and sleep below */
- igc_nfc_rule_exit(adapter);
-
/* set trans_start so we don't get spurious watchdogs during reset */
netif_trans_update(netdev);
@@ -3831,20 +3957,8 @@ static int igc_set_features(struct net_device *netdev,
if (!(changed & (NETIF_F_RXALL | NETIF_F_NTUPLE)))
return 0;
- if (!(features & NETIF_F_NTUPLE)) {
- struct hlist_node *node2;
- struct igc_nfc_rule *rule;
-
- spin_lock(&adapter->nfc_rule_lock);
- hlist_for_each_entry_safe(rule, node2,
- &adapter->nfc_rule_list, nfc_node) {
- igc_disable_nfc_rule(adapter, rule);
- hlist_del(&rule->nfc_node);
- kfree(rule);
- }
- spin_unlock(&adapter->nfc_rule_lock);
- adapter->nfc_rule_count = 0;
- }
+ if (!(features & NETIF_F_NTUPLE))
+ igc_flush_nfc_rules(adapter);
netdev->features = features;
@@ -5111,6 +5225,8 @@ static void igc_remove(struct pci_dev *pdev)
pm_runtime_get_noresume(&pdev->dev);
+ igc_flush_nfc_rules(adapter);
+
igc_ptp_stop(adapter);
set_bit(__IGC_DOWN, &adapter->state);