diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc.h | 68 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_base.h | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_defines.h | 17 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ethtool.c | 839 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_main.c | 456 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_regs.h | 16 | 
6 files changed, 1383 insertions, 17 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 80faccc34cda..0f5534ce27b0 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -29,9 +29,15 @@ unsigned int igc_get_max_rss_queues(struct igc_adapter *adapter);  void igc_set_flag_queue_pairs(struct igc_adapter *adapter,  			      const u32 max_rss_queues);  int igc_reinit_queues(struct igc_adapter *adapter); +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_steering_filter(struct igc_adapter *adapter, +				const u8 *addr, u8 queue, u8 flags); +int igc_del_mac_steering_filter(struct igc_adapter *adapter, +				const u8 *addr, u8 queue, u8 flags); +void igc_update_stats(struct igc_adapter *adapter);  extern char igc_driver_name[];  extern char igc_driver_version[]; @@ -51,6 +57,13 @@ extern char igc_driver_version[];  #define IGC_FLAG_VLAN_PROMISC		BIT(15)  #define IGC_FLAG_RX_LEGACY		BIT(16) +#define IGC_FLAG_RSS_FIELD_IPV4_UDP	BIT(6) +#define IGC_FLAG_RSS_FIELD_IPV6_UDP	BIT(7) + +#define IGC_MRQC_ENABLE_RSS_MQ		0x00000002 +#define IGC_MRQC_RSS_FIELD_IPV4_UDP	0x00400000 +#define IGC_MRQC_RSS_FIELD_IPV6_UDP	0x00800000 +  #define IGC_START_ITR			648 /* ~6000 ints/sec */  #define IGC_4K_ITR			980  #define IGC_20K_ITR			196 @@ -284,15 +297,50 @@ struct igc_q_vector {  	struct igc_ring ring[0] ____cacheline_internodealigned_in_smp;  }; +#define MAX_ETYPE_FILTER		(4 - 1) + +enum igc_filter_match_flags { +	IGC_FILTER_FLAG_ETHER_TYPE =	0x1, +	IGC_FILTER_FLAG_VLAN_TCI   =	0x2, +	IGC_FILTER_FLAG_SRC_MAC_ADDR =	0x4, +	IGC_FILTER_FLAG_DST_MAC_ADDR =	0x8, +}; + +/* RX network flow classification data structure */ +struct igc_nfc_input { +	/* Byte layout in order, all values with MSB first: +	 * match_flags - 1 byte +	 * etype - 2 bytes +	 * vlan_tci - 2 bytes +	 */ +	u8 match_flags; +	__be16 etype; +	__be16 vlan_tci; +	u8 src_addr[ETH_ALEN]; +	u8 dst_addr[ETH_ALEN]; +}; + +struct igc_nfc_filter { +	struct hlist_node nfc_node; +	struct igc_nfc_input filter; +	unsigned long cookie; +	u16 etype_reg_index; +	u16 sw_idx; +	u16 action; +}; +  struct igc_mac_addr {  	u8 addr[ETH_ALEN];  	u8 queue;  	u8 state; /* bitmask */  }; -#define IGC_MAC_STATE_DEFAULT	0x1 -#define IGC_MAC_STATE_MODIFIED	0x2 -#define IGC_MAC_STATE_IN_USE	0x4 +#define IGC_MAC_STATE_DEFAULT		0x1 +#define IGC_MAC_STATE_IN_USE		0x2 +#define IGC_MAC_STATE_SRC_ADDR		0x4 +#define IGC_MAC_STATE_QUEUE_STEERING	0x8 + +#define IGC_MAX_RXNFC_FILTERS		16  /* Board specific private data structure */  struct igc_adapter { @@ -356,12 +404,22 @@ struct igc_adapter {  	u16 tx_ring_count;  	u16 rx_ring_count; +	u32 tx_hwtstamp_timeouts; +	u32 tx_hwtstamp_skipped; +	u32 rx_hwtstamp_cleared;  	u32 *shadow_vfta;  	u32 rss_queues; +	u32 rss_indir_tbl_init; + +	/* RX network flow classification support */ +	struct hlist_head nfc_filter_list; +	struct hlist_head cls_flower_list; +	unsigned int nfc_filter_count;  	/* lock for RX network flow classification filter */  	spinlock_t nfc_lock; +	bool etype_bitmap[MAX_ETYPE_FILTER];  	struct igc_mac_addr *mac_table; @@ -447,6 +505,10 @@ static inline s32 igc_read_phy_reg(struct igc_hw *hw, u32 offset, u16 *data)  /* forward declaration */  void igc_reinit_locked(struct igc_adapter *); +int igc_add_filter(struct igc_adapter *adapter, +		   struct igc_nfc_filter *input); +int igc_erase_filter(struct igc_adapter *adapter, +		     struct igc_nfc_filter *input);  #define igc_rx_pg_size(_ring) (PAGE_SIZE << igc_rx_pg_order(_ring)) diff --git a/drivers/net/ethernet/intel/igc/igc_base.h b/drivers/net/ethernet/intel/igc/igc_base.h index 76d4991d7284..58d1109d7f3f 100644 --- a/drivers/net/ethernet/intel/igc/igc_base.h +++ b/drivers/net/ethernet/intel/igc/igc_base.h @@ -1,8 +1,8 @@  /* SPDX-License-Identifier: GPL-2.0 */  /* Copyright (c)  2018 Intel Corporation */ -#ifndef _IGC_BASE_H -#define _IGC_BASE_H +#ifndef _IGC_BASE_H_ +#define _IGC_BASE_H_  /* forward declaration */  void igc_rx_fifo_flush_base(struct igc_hw *hw); diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index 7d1bdcd1225a..a9a30268de59 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -310,6 +310,12 @@  	IGC_RXDEXT_STATERR_CXE |	\  	IGC_RXDEXT_STATERR_RXE) +#define IGC_MRQC_RSS_FIELD_IPV4_TCP	0x00010000 +#define IGC_MRQC_RSS_FIELD_IPV4		0x00020000 +#define IGC_MRQC_RSS_FIELD_IPV6_TCP_EX	0x00040000 +#define IGC_MRQC_RSS_FIELD_IPV6		0x00100000 +#define IGC_MRQC_RSS_FIELD_IPV6_TCP	0x00200000 +  /* Header split receive */  #define IGC_RFCTL_IPV6_EX_DIS	0x00010000  #define IGC_RFCTL_LEF		0x00040000 @@ -325,6 +331,10 @@  #define I225_RXPBSIZE_DEFAULT	0x000000A2 /* RXPBSIZE default */  #define I225_TXPBSIZE_DEFAULT	0x04000014 /* TXPBSIZE default */ +/* Receive Checksum Control */ +#define IGC_RXCSUM_CRCOFL	0x00000800   /* CRC32 offload enable */ +#define IGC_RXCSUM_PCSD		0x00002000   /* packet checksum disabled */ +  /* GPY211 - I225 defines */  #define GPY_MMD_MASK		0xFFFF0000  #define GPY_MMD_SHIFT		16 @@ -390,4 +400,11 @@  #define IGC_N0_QUEUE -1 +#define IGC_MAX_MAC_HDR_LEN	127 +#define IGC_MAX_NETWORK_HDR_LEN	511 + +#define IGC_VLAPQF_QUEUE_SEL(_n, q_idx) ((q_idx) << ((_n) * 4)) +#define IGC_VLAPQF_P_VALID(_n)	(0x1 << (3 + (_n) * 4)) +#define IGC_VLAPQF_QUEUE_MASK	0x03 +  #endif /* _IGC_DEFINES_H_ */ diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index eff37a6c0afa..ac98f1d96892 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -2,10 +2,120 @@  /* Copyright (c)  2018 Intel Corporation */  /* ethtool support for igc */ +#include <linux/if_vlan.h>  #include <linux/pm_runtime.h>  #include "igc.h" +/* forward declaration */ +struct igc_stats { +	char stat_string[ETH_GSTRING_LEN]; +	int sizeof_stat; +	int stat_offset; +}; + +#define IGC_STAT(_name, _stat) { \ +	.stat_string = _name, \ +	.sizeof_stat = FIELD_SIZEOF(struct igc_adapter, _stat), \ +	.stat_offset = offsetof(struct igc_adapter, _stat) \ +} + +static const struct igc_stats igc_gstrings_stats[] = { +	IGC_STAT("rx_packets", stats.gprc), +	IGC_STAT("tx_packets", stats.gptc), +	IGC_STAT("rx_bytes", stats.gorc), +	IGC_STAT("tx_bytes", stats.gotc), +	IGC_STAT("rx_broadcast", stats.bprc), +	IGC_STAT("tx_broadcast", stats.bptc), +	IGC_STAT("rx_multicast", stats.mprc), +	IGC_STAT("tx_multicast", stats.mptc), +	IGC_STAT("multicast", stats.mprc), +	IGC_STAT("collisions", stats.colc), +	IGC_STAT("rx_crc_errors", stats.crcerrs), +	IGC_STAT("rx_no_buffer_count", stats.rnbc), +	IGC_STAT("rx_missed_errors", stats.mpc), +	IGC_STAT("tx_aborted_errors", stats.ecol), +	IGC_STAT("tx_carrier_errors", stats.tncrs), +	IGC_STAT("tx_window_errors", stats.latecol), +	IGC_STAT("tx_abort_late_coll", stats.latecol), +	IGC_STAT("tx_deferred_ok", stats.dc), +	IGC_STAT("tx_single_coll_ok", stats.scc), +	IGC_STAT("tx_multi_coll_ok", stats.mcc), +	IGC_STAT("tx_timeout_count", tx_timeout_count), +	IGC_STAT("rx_long_length_errors", stats.roc), +	IGC_STAT("rx_short_length_errors", stats.ruc), +	IGC_STAT("rx_align_errors", stats.algnerrc), +	IGC_STAT("tx_tcp_seg_good", stats.tsctc), +	IGC_STAT("tx_tcp_seg_failed", stats.tsctfc), +	IGC_STAT("rx_flow_control_xon", stats.xonrxc), +	IGC_STAT("rx_flow_control_xoff", stats.xoffrxc), +	IGC_STAT("tx_flow_control_xon", stats.xontxc), +	IGC_STAT("tx_flow_control_xoff", stats.xofftxc), +	IGC_STAT("rx_long_byte_count", stats.gorc), +	IGC_STAT("tx_dma_out_of_sync", stats.doosync), +	IGC_STAT("tx_smbus", stats.mgptc), +	IGC_STAT("rx_smbus", stats.mgprc), +	IGC_STAT("dropped_smbus", stats.mgpdc), +	IGC_STAT("os2bmc_rx_by_bmc", stats.o2bgptc), +	IGC_STAT("os2bmc_tx_by_bmc", stats.b2ospc), +	IGC_STAT("os2bmc_tx_by_host", stats.o2bspc), +	IGC_STAT("os2bmc_rx_by_host", stats.b2ogprc), +	IGC_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts), +	IGC_STAT("tx_hwtstamp_skipped", tx_hwtstamp_skipped), +	IGC_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared), +}; + +#define IGC_NETDEV_STAT(_net_stat) { \ +	.stat_string = __stringify(_net_stat), \ +	.sizeof_stat = FIELD_SIZEOF(struct rtnl_link_stats64, _net_stat), \ +	.stat_offset = offsetof(struct rtnl_link_stats64, _net_stat) \ +} + +static const struct igc_stats igc_gstrings_net_stats[] = { +	IGC_NETDEV_STAT(rx_errors), +	IGC_NETDEV_STAT(tx_errors), +	IGC_NETDEV_STAT(tx_dropped), +	IGC_NETDEV_STAT(rx_length_errors), +	IGC_NETDEV_STAT(rx_over_errors), +	IGC_NETDEV_STAT(rx_frame_errors), +	IGC_NETDEV_STAT(rx_fifo_errors), +	IGC_NETDEV_STAT(tx_fifo_errors), +	IGC_NETDEV_STAT(tx_heartbeat_errors) +}; + +enum igc_diagnostics_results { +	TEST_REG = 0, +	TEST_EEP, +	TEST_IRQ, +	TEST_LOOP, +	TEST_LINK +}; + +static const char igc_gstrings_test[][ETH_GSTRING_LEN] = { +	[TEST_REG]  = "Register test  (offline)", +	[TEST_EEP]  = "Eeprom test    (offline)", +	[TEST_IRQ]  = "Interrupt test (offline)", +	[TEST_LOOP] = "Loopback test  (offline)", +	[TEST_LINK] = "Link test   (on/offline)" +}; + +#define IGC_TEST_LEN (sizeof(igc_gstrings_test) / ETH_GSTRING_LEN) + +#define IGC_GLOBAL_STATS_LEN	\ +	(sizeof(igc_gstrings_stats) / sizeof(struct igc_stats)) +#define IGC_NETDEV_STATS_LEN	\ +	(sizeof(igc_gstrings_net_stats) / sizeof(struct igc_stats)) +#define IGC_RX_QUEUE_STATS_LEN \ +	(sizeof(struct igc_rx_queue_stats) / sizeof(u64)) +#define IGC_TX_QUEUE_STATS_LEN 3 /* packets, bytes, restart_queue */ +#define IGC_QUEUE_STATS_LEN \ +	((((struct igc_adapter *)netdev_priv(netdev))->num_rx_queues * \ +	  IGC_RX_QUEUE_STATS_LEN) + \ +	 (((struct igc_adapter *)netdev_priv(netdev))->num_tx_queues * \ +	  IGC_TX_QUEUE_STATS_LEN)) +#define IGC_STATS_LEN \ +	(IGC_GLOBAL_STATS_LEN + IGC_NETDEV_STATS_LEN + IGC_QUEUE_STATS_LEN) +  static const char igc_priv_flags_strings[][ETH_GSTRING_LEN] = {  #define IGC_PRIV_FLAGS_LEGACY_RX	BIT(0)  	"legacy-rx", @@ -545,6 +655,127 @@ static int igc_set_pauseparam(struct net_device *netdev,  	return retval;  } +static void igc_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ +	struct igc_adapter *adapter = netdev_priv(netdev); +	u8 *p = data; +	int i; + +	switch (stringset) { +	case ETH_SS_TEST: +		memcpy(data, *igc_gstrings_test, +		       IGC_TEST_LEN * ETH_GSTRING_LEN); +		break; +	case ETH_SS_STATS: +		for (i = 0; i < IGC_GLOBAL_STATS_LEN; i++) { +			memcpy(p, igc_gstrings_stats[i].stat_string, +			       ETH_GSTRING_LEN); +			p += ETH_GSTRING_LEN; +		} +		for (i = 0; i < IGC_NETDEV_STATS_LEN; i++) { +			memcpy(p, igc_gstrings_net_stats[i].stat_string, +			       ETH_GSTRING_LEN); +			p += ETH_GSTRING_LEN; +		} +		for (i = 0; i < adapter->num_tx_queues; i++) { +			sprintf(p, "tx_queue_%u_packets", i); +			p += ETH_GSTRING_LEN; +			sprintf(p, "tx_queue_%u_bytes", i); +			p += ETH_GSTRING_LEN; +			sprintf(p, "tx_queue_%u_restart", i); +			p += ETH_GSTRING_LEN; +		} +		for (i = 0; i < adapter->num_rx_queues; i++) { +			sprintf(p, "rx_queue_%u_packets", i); +			p += ETH_GSTRING_LEN; +			sprintf(p, "rx_queue_%u_bytes", i); +			p += ETH_GSTRING_LEN; +			sprintf(p, "rx_queue_%u_drops", i); +			p += ETH_GSTRING_LEN; +			sprintf(p, "rx_queue_%u_csum_err", i); +			p += ETH_GSTRING_LEN; +			sprintf(p, "rx_queue_%u_alloc_failed", i); +			p += ETH_GSTRING_LEN; +		} +		/* BUG_ON(p - data != IGC_STATS_LEN * ETH_GSTRING_LEN); */ +		break; +	case ETH_SS_PRIV_FLAGS: +		memcpy(data, igc_priv_flags_strings, +		       IGC_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN); +		break; +	} +} + +static int igc_get_sset_count(struct net_device *netdev, int sset) +{ +	switch (sset) { +	case ETH_SS_STATS: +		return IGC_STATS_LEN; +	case ETH_SS_TEST: +		return IGC_TEST_LEN; +	case ETH_SS_PRIV_FLAGS: +		return IGC_PRIV_FLAGS_STR_LEN; +	default: +		return -ENOTSUPP; +	} +} + +static void igc_get_ethtool_stats(struct net_device *netdev, +				  struct ethtool_stats *stats, u64 *data) +{ +	struct igc_adapter *adapter = netdev_priv(netdev); +	struct rtnl_link_stats64 *net_stats = &adapter->stats64; +	unsigned int start; +	struct igc_ring *ring; +	int i, j; +	char *p; + +	spin_lock(&adapter->stats64_lock); +	igc_update_stats(adapter); + +	for (i = 0; i < IGC_GLOBAL_STATS_LEN; i++) { +		p = (char *)adapter + igc_gstrings_stats[i].stat_offset; +		data[i] = (igc_gstrings_stats[i].sizeof_stat == +			sizeof(u64)) ? *(u64 *)p : *(u32 *)p; +	} +	for (j = 0; j < IGC_NETDEV_STATS_LEN; j++, i++) { +		p = (char *)net_stats + igc_gstrings_net_stats[j].stat_offset; +		data[i] = (igc_gstrings_net_stats[j].sizeof_stat == +			sizeof(u64)) ? *(u64 *)p : *(u32 *)p; +	} +	for (j = 0; j < adapter->num_tx_queues; j++) { +		u64	restart2; + +		ring = adapter->tx_ring[j]; +		do { +			start = u64_stats_fetch_begin_irq(&ring->tx_syncp); +			data[i]   = ring->tx_stats.packets; +			data[i + 1] = ring->tx_stats.bytes; +			data[i + 2] = ring->tx_stats.restart_queue; +		} while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); +		do { +			start = u64_stats_fetch_begin_irq(&ring->tx_syncp2); +			restart2  = ring->tx_stats.restart_queue2; +		} while (u64_stats_fetch_retry_irq(&ring->tx_syncp2, start)); +		data[i + 2] += restart2; + +		i += IGC_TX_QUEUE_STATS_LEN; +	} +	for (j = 0; j < adapter->num_rx_queues; j++) { +		ring = adapter->rx_ring[j]; +		do { +			start = u64_stats_fetch_begin_irq(&ring->rx_syncp); +			data[i]   = ring->rx_stats.packets; +			data[i + 1] = ring->rx_stats.bytes; +			data[i + 2] = ring->rx_stats.drops; +			data[i + 3] = ring->rx_stats.csum_err; +			data[i + 4] = ring->rx_stats.alloc_failed; +		} while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); +		i += IGC_RX_QUEUE_STATS_LEN; +	} +	spin_unlock(&adapter->stats64_lock); +} +  static int igc_get_coalesce(struct net_device *netdev,  			    struct ethtool_coalesce *ec)  { @@ -643,6 +874,605 @@ static int igc_set_coalesce(struct net_device *netdev,  	return 0;  } +#define ETHER_TYPE_FULL_MASK ((__force __be16)~0) +static int igc_get_ethtool_nfc_entry(struct igc_adapter *adapter, +				     struct ethtool_rxnfc *cmd) +{ +	struct ethtool_rx_flow_spec *fsp = &cmd->fs; +	struct igc_nfc_filter *rule = NULL; + +	/* report total rule count */ +	cmd->data = IGC_MAX_RXNFC_FILTERS; + +	hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) { +		if (fsp->location <= rule->sw_idx) +			break; +	} + +	if (!rule || fsp->location != rule->sw_idx) +		return -EINVAL; + +	if (rule->filter.match_flags) { +		fsp->flow_type = ETHER_FLOW; +		fsp->ring_cookie = rule->action; +		if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) { +			fsp->h_u.ether_spec.h_proto = rule->filter.etype; +			fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK; +		} +		if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) { +			fsp->flow_type |= FLOW_EXT; +			fsp->h_ext.vlan_tci = rule->filter.vlan_tci; +			fsp->m_ext.vlan_tci = htons(VLAN_PRIO_MASK); +		} +		if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) { +			ether_addr_copy(fsp->h_u.ether_spec.h_dest, +					rule->filter.dst_addr); +			/* As we only support matching by the full +			 * mask, return the mask to userspace +			 */ +			eth_broadcast_addr(fsp->m_u.ether_spec.h_dest); +		} +		if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) { +			ether_addr_copy(fsp->h_u.ether_spec.h_source, +					rule->filter.src_addr); +			/* As we only support matching by the full +			 * mask, return the mask to userspace +			 */ +			eth_broadcast_addr(fsp->m_u.ether_spec.h_source); +		} + +		return 0; +	} +	return -EINVAL; +} + +static int igc_get_ethtool_nfc_all(struct igc_adapter *adapter, +				   struct ethtool_rxnfc *cmd, +				   u32 *rule_locs) +{ +	struct igc_nfc_filter *rule; +	int cnt = 0; + +	/* report total rule count */ +	cmd->data = IGC_MAX_RXNFC_FILTERS; + +	hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) { +		if (cnt == cmd->rule_cnt) +			return -EMSGSIZE; +		rule_locs[cnt] = rule->sw_idx; +		cnt++; +	} + +	cmd->rule_cnt = cnt; + +	return 0; +} + +static int igc_get_rss_hash_opts(struct igc_adapter *adapter, +				 struct ethtool_rxnfc *cmd) +{ +	cmd->data = 0; + +	/* Report default options for RSS on igc */ +	switch (cmd->flow_type) { +	case TCP_V4_FLOW: +		cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; +		/* Fall through */ +	case UDP_V4_FLOW: +		if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV4_UDP) +			cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; +		/* Fall through */ +	case SCTP_V4_FLOW: +		/* Fall through */ +	case AH_ESP_V4_FLOW: +		/* Fall through */ +	case AH_V4_FLOW: +		/* Fall through */ +	case ESP_V4_FLOW: +		/* Fall through */ +	case IPV4_FLOW: +		cmd->data |= RXH_IP_SRC | RXH_IP_DST; +		break; +	case TCP_V6_FLOW: +		cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; +		/* Fall through */ +	case UDP_V6_FLOW: +		if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV6_UDP) +			cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; +		/* Fall through */ +	case SCTP_V6_FLOW: +		/* Fall through */ +	case AH_ESP_V6_FLOW: +		/* Fall through */ +	case AH_V6_FLOW: +		/* Fall through */ +	case ESP_V6_FLOW: +		/* Fall through */ +	case IPV6_FLOW: +		cmd->data |= RXH_IP_SRC | RXH_IP_DST; +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +static int igc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, +			 u32 *rule_locs) +{ +	struct igc_adapter *adapter = netdev_priv(dev); +	int ret = -EOPNOTSUPP; + +	switch (cmd->cmd) { +	case ETHTOOL_GRXRINGS: +		cmd->data = adapter->num_rx_queues; +		ret = 0; +		break; +	case ETHTOOL_GRXCLSRLCNT: +		cmd->rule_cnt = adapter->nfc_filter_count; +		ret = 0; +		break; +	case ETHTOOL_GRXCLSRULE: +		ret = igc_get_ethtool_nfc_entry(adapter, cmd); +		break; +	case ETHTOOL_GRXCLSRLALL: +		ret = igc_get_ethtool_nfc_all(adapter, cmd, rule_locs); +		break; +	case ETHTOOL_GRXFH: +		ret = igc_get_rss_hash_opts(adapter, cmd); +		break; +	default: +		break; +	} + +	return ret; +} + +#define UDP_RSS_FLAGS (IGC_FLAG_RSS_FIELD_IPV4_UDP | \ +		       IGC_FLAG_RSS_FIELD_IPV6_UDP) +static int igc_set_rss_hash_opt(struct igc_adapter *adapter, +				struct ethtool_rxnfc *nfc) +{ +	u32 flags = adapter->flags; + +	/* RSS does not support anything other than hashing +	 * to queues on src and dst IPs and ports +	 */ +	if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | +			  RXH_L4_B_0_1 | RXH_L4_B_2_3)) +		return -EINVAL; + +	switch (nfc->flow_type) { +	case TCP_V4_FLOW: +	case TCP_V6_FLOW: +		if (!(nfc->data & RXH_IP_SRC) || +		    !(nfc->data & RXH_IP_DST) || +		    !(nfc->data & RXH_L4_B_0_1) || +		    !(nfc->data & RXH_L4_B_2_3)) +			return -EINVAL; +		break; +	case UDP_V4_FLOW: +		if (!(nfc->data & RXH_IP_SRC) || +		    !(nfc->data & RXH_IP_DST)) +			return -EINVAL; +		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { +		case 0: +			flags &= ~IGC_FLAG_RSS_FIELD_IPV4_UDP; +			break; +		case (RXH_L4_B_0_1 | RXH_L4_B_2_3): +			flags |= IGC_FLAG_RSS_FIELD_IPV4_UDP; +			break; +		default: +			return -EINVAL; +		} +		break; +	case UDP_V6_FLOW: +		if (!(nfc->data & RXH_IP_SRC) || +		    !(nfc->data & RXH_IP_DST)) +			return -EINVAL; +		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { +		case 0: +			flags &= ~IGC_FLAG_RSS_FIELD_IPV6_UDP; +			break; +		case (RXH_L4_B_0_1 | RXH_L4_B_2_3): +			flags |= IGC_FLAG_RSS_FIELD_IPV6_UDP; +			break; +		default: +			return -EINVAL; +		} +		break; +	case AH_ESP_V4_FLOW: +	case AH_V4_FLOW: +	case ESP_V4_FLOW: +	case SCTP_V4_FLOW: +	case AH_ESP_V6_FLOW: +	case AH_V6_FLOW: +	case ESP_V6_FLOW: +	case SCTP_V6_FLOW: +		if (!(nfc->data & RXH_IP_SRC) || +		    !(nfc->data & RXH_IP_DST) || +		    (nfc->data & RXH_L4_B_0_1) || +		    (nfc->data & RXH_L4_B_2_3)) +			return -EINVAL; +		break; +	default: +		return -EINVAL; +	} + +	/* if we changed something we need to update flags */ +	if (flags != adapter->flags) { +		struct igc_hw *hw = &adapter->hw; +		u32 mrqc = rd32(IGC_MRQC); + +		if ((flags & UDP_RSS_FLAGS) && +		    !(adapter->flags & UDP_RSS_FLAGS)) +			dev_err(&adapter->pdev->dev, +				"enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); + +		adapter->flags = flags; + +		/* Perform hash on these packet types */ +		mrqc |= IGC_MRQC_RSS_FIELD_IPV4 | +			IGC_MRQC_RSS_FIELD_IPV4_TCP | +			IGC_MRQC_RSS_FIELD_IPV6 | +			IGC_MRQC_RSS_FIELD_IPV6_TCP; + +		mrqc &= ~(IGC_MRQC_RSS_FIELD_IPV4_UDP | +			  IGC_MRQC_RSS_FIELD_IPV6_UDP); + +		if (flags & IGC_FLAG_RSS_FIELD_IPV4_UDP) +			mrqc |= IGC_MRQC_RSS_FIELD_IPV4_UDP; + +		if (flags & IGC_FLAG_RSS_FIELD_IPV6_UDP) +			mrqc |= IGC_MRQC_RSS_FIELD_IPV6_UDP; + +		wr32(IGC_MRQC, mrqc); +	} + +	return 0; +} + +static int igc_rxnfc_write_etype_filter(struct igc_adapter *adapter, +					struct igc_nfc_filter *input) +{ +	struct igc_hw *hw = &adapter->hw; +	u8 i; +	u32 etqf; +	u16 etype; + +	/* find an empty etype filter register */ +	for (i = 0; i < MAX_ETYPE_FILTER; ++i) { +		if (!adapter->etype_bitmap[i]) +			break; +	} +	if (i == MAX_ETYPE_FILTER) { +		dev_err(&adapter->pdev->dev, "ethtool -N: etype filters are all used.\n"); +		return -EINVAL; +	} + +	adapter->etype_bitmap[i] = true; + +	etqf = rd32(IGC_ETQF(i)); +	etype = ntohs(input->filter.etype & ETHER_TYPE_FULL_MASK); + +	etqf |= IGC_ETQF_FILTER_ENABLE; +	etqf &= ~IGC_ETQF_ETYPE_MASK; +	etqf |= (etype & IGC_ETQF_ETYPE_MASK); + +	etqf &= ~IGC_ETQF_QUEUE_MASK; +	etqf |= ((input->action << IGC_ETQF_QUEUE_SHIFT) +		& IGC_ETQF_QUEUE_MASK); +	etqf |= IGC_ETQF_QUEUE_ENABLE; + +	wr32(IGC_ETQF(i), etqf); + +	input->etype_reg_index = i; + +	return 0; +} + +static int igc_rxnfc_write_vlan_prio_filter(struct igc_adapter *adapter, +					    struct igc_nfc_filter *input) +{ +	struct igc_hw *hw = &adapter->hw; +	u8 vlan_priority; +	u16 queue_index; +	u32 vlapqf; + +	vlapqf = rd32(IGC_VLAPQF); +	vlan_priority = (ntohs(input->filter.vlan_tci) & VLAN_PRIO_MASK) +				>> VLAN_PRIO_SHIFT; +	queue_index = (vlapqf >> (vlan_priority * 4)) & IGC_VLAPQF_QUEUE_MASK; + +	/* check whether this vlan prio is already set */ +	if (vlapqf & IGC_VLAPQF_P_VALID(vlan_priority) && +	    queue_index != input->action) { +		dev_err(&adapter->pdev->dev, "ethtool rxnfc set vlan prio filter failed.\n"); +		return -EEXIST; +	} + +	vlapqf |= IGC_VLAPQF_P_VALID(vlan_priority); +	vlapqf |= IGC_VLAPQF_QUEUE_SEL(vlan_priority, input->action); + +	wr32(IGC_VLAPQF, vlapqf); + +	return 0; +} + +int igc_add_filter(struct igc_adapter *adapter, struct igc_nfc_filter *input) +{ +	struct igc_hw *hw = &adapter->hw; +	int err = -EINVAL; + +	if (hw->mac.type == igc_i225 && +	    !(input->filter.match_flags & ~IGC_FILTER_FLAG_SRC_MAC_ADDR)) { +		dev_err(&adapter->pdev->dev, +			"i225 doesn't support flow classification rules specifying only source addresses.\n"); +		return -EOPNOTSUPP; +	} + +	if (input->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) { +		err = igc_rxnfc_write_etype_filter(adapter, input); +		if (err) +			return err; +	} + +	if (input->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) { +		err = igc_add_mac_steering_filter(adapter, +						  input->filter.dst_addr, +						  input->action, 0); +		err = min_t(int, err, 0); +		if (err) +			return err; +	} + +	if (input->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) { +		err = igc_add_mac_steering_filter(adapter, +						  input->filter.src_addr, +						  input->action, +						  IGC_MAC_STATE_SRC_ADDR); +		err = min_t(int, err, 0); +		if (err) +			return err; +	} + +	if (input->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) +		err = igc_rxnfc_write_vlan_prio_filter(adapter, input); + +	return err; +} + +static void igc_clear_etype_filter_regs(struct igc_adapter *adapter, +					u16 reg_index) +{ +	struct igc_hw *hw = &adapter->hw; +	u32 etqf = rd32(IGC_ETQF(reg_index)); + +	etqf &= ~IGC_ETQF_QUEUE_ENABLE; +	etqf &= ~IGC_ETQF_QUEUE_MASK; +	etqf &= ~IGC_ETQF_FILTER_ENABLE; + +	wr32(IGC_ETQF(reg_index), etqf); + +	adapter->etype_bitmap[reg_index] = false; +} + +static void igc_clear_vlan_prio_filter(struct igc_adapter *adapter, +				       u16 vlan_tci) +{ +	struct igc_hw *hw = &adapter->hw; +	u8 vlan_priority; +	u32 vlapqf; + +	vlan_priority = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; + +	vlapqf = rd32(IGC_VLAPQF); +	vlapqf &= ~IGC_VLAPQF_P_VALID(vlan_priority); +	vlapqf &= ~IGC_VLAPQF_QUEUE_SEL(vlan_priority, +						IGC_VLAPQF_QUEUE_MASK); + +	wr32(IGC_VLAPQF, vlapqf); +} + +int igc_erase_filter(struct igc_adapter *adapter, struct igc_nfc_filter *input) +{ +	if (input->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) +		igc_clear_etype_filter_regs(adapter, +					    input->etype_reg_index); + +	if (input->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) +		igc_clear_vlan_prio_filter(adapter, +					   ntohs(input->filter.vlan_tci)); + +	if (input->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) +		igc_del_mac_steering_filter(adapter, input->filter.src_addr, +					    input->action, +					    IGC_MAC_STATE_SRC_ADDR); + +	if (input->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) +		igc_del_mac_steering_filter(adapter, input->filter.dst_addr, +					    input->action, 0); + +	return 0; +} + +static int igc_update_ethtool_nfc_entry(struct igc_adapter *adapter, +					struct igc_nfc_filter *input, +					u16 sw_idx) +{ +	struct igc_nfc_filter *rule, *parent; +	int err = -EINVAL; + +	parent = NULL; +	rule = NULL; + +	hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) { +		/* hash found, or no matching entry */ +		if (rule->sw_idx >= sw_idx) +			break; +		parent = rule; +	} + +	/* if there is an old rule occupying our place remove it */ +	if (rule && rule->sw_idx == sw_idx) { +		if (!input) +			err = igc_erase_filter(adapter, rule); + +		hlist_del(&rule->nfc_node); +		kfree(rule); +		adapter->nfc_filter_count--; +	} + +	/* 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_filter_list); + +	/* update counts */ +	adapter->nfc_filter_count++; + +	return 0; +} + +static int igc_add_ethtool_nfc_entry(struct igc_adapter *adapter, +				     struct ethtool_rxnfc *cmd) +{ +	struct net_device *netdev = adapter->netdev; +	struct ethtool_rx_flow_spec *fsp = +		(struct ethtool_rx_flow_spec *)&cmd->fs; +	struct igc_nfc_filter *input, *rule; +	int err = 0; + +	if (!(netdev->hw_features & NETIF_F_NTUPLE)) +		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) { +		dev_err(&adapter->pdev->dev, "ethtool -N: The specified action is invalid\n"); +		return -EINVAL; +	} + +	/* Don't allow indexes to exist outside of available space */ +	if (fsp->location >= IGC_MAX_RXNFC_FILTERS) { +		dev_err(&adapter->pdev->dev, "Location out of range\n"); +		return -EINVAL; +	} + +	if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW) +		return -EINVAL; + +	input = kzalloc(sizeof(*input), GFP_KERNEL); +	if (!input) +		return -ENOMEM; + +	if (fsp->m_u.ether_spec.h_proto == ETHER_TYPE_FULL_MASK) { +		input->filter.etype = fsp->h_u.ether_spec.h_proto; +		input->filter.match_flags = IGC_FILTER_FLAG_ETHER_TYPE; +	} + +	/* Only support matching addresses by the full mask */ +	if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_source)) { +		input->filter.match_flags |= IGC_FILTER_FLAG_SRC_MAC_ADDR; +		ether_addr_copy(input->filter.src_addr, +				fsp->h_u.ether_spec.h_source); +	} + +	/* Only support matching addresses by the full mask */ +	if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_dest)) { +		input->filter.match_flags |= IGC_FILTER_FLAG_DST_MAC_ADDR; +		ether_addr_copy(input->filter.dst_addr, +				fsp->h_u.ether_spec.h_dest); +	} + +	if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) { +		if (fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK)) { +			err = -EINVAL; +			goto err_out; +		} +		input->filter.vlan_tci = fsp->h_ext.vlan_tci; +		input->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI; +	} + +	input->action = fsp->ring_cookie; +	input->sw_idx = fsp->location; + +	spin_lock(&adapter->nfc_lock); + +	hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) { +		if (!memcmp(&input->filter, &rule->filter, +			    sizeof(input->filter))) { +			err = -EEXIST; +			dev_err(&adapter->pdev->dev, +				"ethtool: this filter is already set\n"); +			goto err_out_w_lock; +		} +	} + +	err = igc_add_filter(adapter, input); +	if (err) +		goto err_out_w_lock; + +	igc_update_ethtool_nfc_entry(adapter, input, input->sw_idx); + +	spin_unlock(&adapter->nfc_lock); +	return 0; + +err_out_w_lock: +	spin_unlock(&adapter->nfc_lock); +err_out: +	kfree(input); +	return err; +} + +static int igc_del_ethtool_nfc_entry(struct igc_adapter *adapter, +				     struct ethtool_rxnfc *cmd) +{ +	struct ethtool_rx_flow_spec *fsp = +		(struct ethtool_rx_flow_spec *)&cmd->fs; +	int err; + +	spin_lock(&adapter->nfc_lock); +	err = igc_update_ethtool_nfc_entry(adapter, NULL, fsp->location); +	spin_unlock(&adapter->nfc_lock); + +	return err; +} + +static int igc_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ +	struct igc_adapter *adapter = netdev_priv(dev); +	int ret = -EOPNOTSUPP; + +	switch (cmd->cmd) { +	case ETHTOOL_SRXFH: +		ret = igc_set_rss_hash_opt(adapter, cmd); +		break; +	case ETHTOOL_SRXCLSRLINS: +		ret = igc_add_ethtool_nfc_entry(adapter, cmd); +		break; +	case ETHTOOL_SRXCLSRLDEL: +		ret = igc_del_ethtool_nfc_entry(adapter, cmd); +	default: +		break; +	} + +	return ret; +} +  void igc_write_rss_indir_tbl(struct igc_adapter *adapter)  {  	struct igc_hw *hw = &adapter->hw; @@ -885,17 +1715,13 @@ static int igc_get_link_ksettings(struct net_device *netdev,  			if (hw->mac.type == igc_i225 &&  			    (status & IGC_STATUS_SPEED_2500)) {  				speed = SPEED_2500; -				hw_dbg("2500 Mbs, ");  			} else {  				speed = SPEED_1000; -				hw_dbg("1000 Mbs, ");  			}  		} else if (status & IGC_STATUS_SPEED_100) {  			speed = SPEED_100; -			hw_dbg("100 Mbs, ");  		} else {  			speed = SPEED_10; -			hw_dbg("10 Mbs, ");  		}  		if ((status & IGC_STATUS_FD) ||  		    hw->phy.media_type != igc_media_type_copper) @@ -1011,8 +1837,13 @@ static const struct ethtool_ops igc_ethtool_ops = {  	.set_ringparam		= igc_set_ringparam,  	.get_pauseparam		= igc_get_pauseparam,  	.set_pauseparam		= igc_set_pauseparam, +	.get_strings		= igc_get_strings, +	.get_sset_count		= igc_get_sset_count, +	.get_ethtool_stats	= igc_get_ethtool_stats,  	.get_coalesce		= igc_get_coalesce,  	.set_coalesce		= igc_set_coalesce, +	.get_rxnfc		= igc_get_rxnfc, +	.set_rxnfc		= igc_set_rxnfc,  	.get_rxfh_indir_size	= igc_get_rxfh_indir_size,  	.get_rxfh		= igc_get_rxfh,  	.set_rxfh		= igc_set_rxfh, diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 87a11879bf2d..34fa0e60a780 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -620,6 +620,55 @@ static void igc_configure_tx(struct igc_adapter *adapter)   */  static void igc_setup_mrqc(struct igc_adapter *adapter)  { +	struct igc_hw *hw = &adapter->hw; +	u32 j, num_rx_queues; +	u32 mrqc, rxcsum; +	u32 rss_key[10]; + +	netdev_rss_key_fill(rss_key, sizeof(rss_key)); +	for (j = 0; j < 10; j++) +		wr32(IGC_RSSRK(j), rss_key[j]); + +	num_rx_queues = adapter->rss_queues; + +	if (adapter->rss_indir_tbl_init != num_rx_queues) { +		for (j = 0; j < IGC_RETA_SIZE; j++) +			adapter->rss_indir_tbl[j] = +			(j * num_rx_queues) / IGC_RETA_SIZE; +		adapter->rss_indir_tbl_init = num_rx_queues; +	} +	igc_write_rss_indir_tbl(adapter); + +	/* Disable raw packet checksumming so that RSS hash is placed in +	 * descriptor on writeback.  No need to enable TCP/UDP/IP checksum +	 * offloads as they are enabled by default +	 */ +	rxcsum = rd32(IGC_RXCSUM); +	rxcsum |= IGC_RXCSUM_PCSD; + +	/* Enable Receive Checksum Offload for SCTP */ +	rxcsum |= IGC_RXCSUM_CRCOFL; + +	/* Don't need to set TUOFL or IPOFL, they default to 1 */ +	wr32(IGC_RXCSUM, rxcsum); + +	/* Generate RSS hash based on packet types, TCP/UDP +	 * port numbers and/or IPv4/v6 src and dst addresses +	 */ +	mrqc = IGC_MRQC_RSS_FIELD_IPV4 | +	       IGC_MRQC_RSS_FIELD_IPV4_TCP | +	       IGC_MRQC_RSS_FIELD_IPV6 | +	       IGC_MRQC_RSS_FIELD_IPV6_TCP | +	       IGC_MRQC_RSS_FIELD_IPV6_TCP_EX; + +	if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV4_UDP) +		mrqc |= IGC_MRQC_RSS_FIELD_IPV4_UDP; +	if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV6_UDP) +		mrqc |= IGC_MRQC_RSS_FIELD_IPV6_UDP; + +	mrqc |= IGC_MRQC_ENABLE_RSS_MQ; + +	wr32(IGC_MRQC, mrqc);  }  /** @@ -890,13 +939,8 @@ static int igc_tx_map(struct igc_ring *tx_ring,  	/* Make sure there is space in the ring for the next send. */  	igc_maybe_stop_tx(tx_ring, DESC_NEEDED); -	if (netif_xmit_stopped(txring_txq(tx_ring)) || !skb->xmit_more) { +	if (netif_xmit_stopped(txring_txq(tx_ring)) || !netdev_xmit_more()) {  		writel(i, tx_ring->tail); - -		/* we need this if more than one processor can write to our tail -		 * at a time, it synchronizes IO on IA64/Altix systems -		 */ -		mmiowb();  	}  	return 0; @@ -1150,7 +1194,7 @@ static struct sk_buff *igc_construct_skb(struct igc_ring *rx_ring,  	/* Determine available headroom for copy */  	headlen = size;  	if (headlen > IGC_RX_HDR_LEN) -		headlen = eth_get_headlen(va, IGC_RX_HDR_LEN); +		headlen = eth_get_headlen(skb->dev, va, IGC_RX_HDR_LEN);  	/* align pull length to size of long to optimize memcpy performance */  	memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long))); @@ -1738,12 +1782,200 @@ void igc_up(struct igc_adapter *adapter)   * igc_update_stats - Update the board statistics counters   * @adapter: board private structure   */ -static void igc_update_stats(struct igc_adapter *adapter) +void igc_update_stats(struct igc_adapter *adapter)  { +	struct rtnl_link_stats64 *net_stats = &adapter->stats64; +	struct pci_dev *pdev = adapter->pdev; +	struct igc_hw *hw = &adapter->hw; +	u64 _bytes, _packets; +	u64 bytes, packets; +	unsigned int start; +	u32 mpc; +	int i; + +	/* Prevent stats update while adapter is being reset, or if the pci +	 * connection is down. +	 */ +	if (adapter->link_speed == 0) +		return; +	if (pci_channel_offline(pdev)) +		return; + +	packets = 0; +	bytes = 0; + +	rcu_read_lock(); +	for (i = 0; i < adapter->num_rx_queues; i++) { +		struct igc_ring *ring = adapter->rx_ring[i]; +		u32 rqdpc = rd32(IGC_RQDPC(i)); + +		if (hw->mac.type >= igc_i225) +			wr32(IGC_RQDPC(i), 0); + +		if (rqdpc) { +			ring->rx_stats.drops += rqdpc; +			net_stats->rx_fifo_errors += rqdpc; +		} + +		do { +			start = u64_stats_fetch_begin_irq(&ring->rx_syncp); +			_bytes = ring->rx_stats.bytes; +			_packets = ring->rx_stats.packets; +		} while (u64_stats_fetch_retry_irq(&ring->rx_syncp, start)); +		bytes += _bytes; +		packets += _packets; +	} + +	net_stats->rx_bytes = bytes; +	net_stats->rx_packets = packets; + +	packets = 0; +	bytes = 0; +	for (i = 0; i < adapter->num_tx_queues; i++) { +		struct igc_ring *ring = adapter->tx_ring[i]; + +		do { +			start = u64_stats_fetch_begin_irq(&ring->tx_syncp); +			_bytes = ring->tx_stats.bytes; +			_packets = ring->tx_stats.packets; +		} while (u64_stats_fetch_retry_irq(&ring->tx_syncp, start)); +		bytes += _bytes; +		packets += _packets; +	} +	net_stats->tx_bytes = bytes; +	net_stats->tx_packets = packets; +	rcu_read_unlock(); + +	/* read stats registers */ +	adapter->stats.crcerrs += rd32(IGC_CRCERRS); +	adapter->stats.gprc += rd32(IGC_GPRC); +	adapter->stats.gorc += rd32(IGC_GORCL); +	rd32(IGC_GORCH); /* clear GORCL */ +	adapter->stats.bprc += rd32(IGC_BPRC); +	adapter->stats.mprc += rd32(IGC_MPRC); +	adapter->stats.roc += rd32(IGC_ROC); + +	adapter->stats.prc64 += rd32(IGC_PRC64); +	adapter->stats.prc127 += rd32(IGC_PRC127); +	adapter->stats.prc255 += rd32(IGC_PRC255); +	adapter->stats.prc511 += rd32(IGC_PRC511); +	adapter->stats.prc1023 += rd32(IGC_PRC1023); +	adapter->stats.prc1522 += rd32(IGC_PRC1522); +	adapter->stats.symerrs += rd32(IGC_SYMERRS); +	adapter->stats.sec += rd32(IGC_SEC); + +	mpc = rd32(IGC_MPC); +	adapter->stats.mpc += mpc; +	net_stats->rx_fifo_errors += mpc; +	adapter->stats.scc += rd32(IGC_SCC); +	adapter->stats.ecol += rd32(IGC_ECOL); +	adapter->stats.mcc += rd32(IGC_MCC); +	adapter->stats.latecol += rd32(IGC_LATECOL); +	adapter->stats.dc += rd32(IGC_DC); +	adapter->stats.rlec += rd32(IGC_RLEC); +	adapter->stats.xonrxc += rd32(IGC_XONRXC); +	adapter->stats.xontxc += rd32(IGC_XONTXC); +	adapter->stats.xoffrxc += rd32(IGC_XOFFRXC); +	adapter->stats.xofftxc += rd32(IGC_XOFFTXC); +	adapter->stats.fcruc += rd32(IGC_FCRUC); +	adapter->stats.gptc += rd32(IGC_GPTC); +	adapter->stats.gotc += rd32(IGC_GOTCL); +	rd32(IGC_GOTCH); /* clear GOTCL */ +	adapter->stats.rnbc += rd32(IGC_RNBC); +	adapter->stats.ruc += rd32(IGC_RUC); +	adapter->stats.rfc += rd32(IGC_RFC); +	adapter->stats.rjc += rd32(IGC_RJC); +	adapter->stats.tor += rd32(IGC_TORH); +	adapter->stats.tot += rd32(IGC_TOTH); +	adapter->stats.tpr += rd32(IGC_TPR); + +	adapter->stats.ptc64 += rd32(IGC_PTC64); +	adapter->stats.ptc127 += rd32(IGC_PTC127); +	adapter->stats.ptc255 += rd32(IGC_PTC255); +	adapter->stats.ptc511 += rd32(IGC_PTC511); +	adapter->stats.ptc1023 += rd32(IGC_PTC1023); +	adapter->stats.ptc1522 += rd32(IGC_PTC1522); + +	adapter->stats.mptc += rd32(IGC_MPTC); +	adapter->stats.bptc += rd32(IGC_BPTC); + +	adapter->stats.tpt += rd32(IGC_TPT); +	adapter->stats.colc += rd32(IGC_COLC); + +	adapter->stats.algnerrc += rd32(IGC_ALGNERRC); + +	adapter->stats.tsctc += rd32(IGC_TSCTC); +	adapter->stats.tsctfc += rd32(IGC_TSCTFC); + +	adapter->stats.iac += rd32(IGC_IAC); +	adapter->stats.icrxoc += rd32(IGC_ICRXOC); +	adapter->stats.icrxptc += rd32(IGC_ICRXPTC); +	adapter->stats.icrxatc += rd32(IGC_ICRXATC); +	adapter->stats.ictxptc += rd32(IGC_ICTXPTC); +	adapter->stats.ictxatc += rd32(IGC_ICTXATC); +	adapter->stats.ictxqec += rd32(IGC_ICTXQEC); +	adapter->stats.ictxqmtc += rd32(IGC_ICTXQMTC); +	adapter->stats.icrxdmtc += rd32(IGC_ICRXDMTC); + +	/* Fill out the OS statistics structure */ +	net_stats->multicast = adapter->stats.mprc; +	net_stats->collisions = adapter->stats.colc; + +	/* Rx Errors */ + +	/* RLEC on some newer hardware can be incorrect so build +	 * our own version based on RUC and ROC +	 */ +	net_stats->rx_errors = adapter->stats.rxerrc + +		adapter->stats.crcerrs + adapter->stats.algnerrc + +		adapter->stats.ruc + adapter->stats.roc + +		adapter->stats.cexterr; +	net_stats->rx_length_errors = adapter->stats.ruc + +				      adapter->stats.roc; +	net_stats->rx_crc_errors = adapter->stats.crcerrs; +	net_stats->rx_frame_errors = adapter->stats.algnerrc; +	net_stats->rx_missed_errors = adapter->stats.mpc; + +	/* Tx Errors */ +	net_stats->tx_errors = adapter->stats.ecol + +			       adapter->stats.latecol; +	net_stats->tx_aborted_errors = adapter->stats.ecol; +	net_stats->tx_window_errors = adapter->stats.latecol; +	net_stats->tx_carrier_errors = adapter->stats.tncrs; + +	/* Tx Dropped needs to be maintained elsewhere */ + +	/* Management Stats */ +	adapter->stats.mgptc += rd32(IGC_MGTPTC); +	adapter->stats.mgprc += rd32(IGC_MGTPRC); +	adapter->stats.mgpdc += rd32(IGC_MGTPDC);  }  static void igc_nfc_filter_exit(struct igc_adapter *adapter)  { +	struct igc_nfc_filter *rule; + +	spin_lock(&adapter->nfc_lock); + +	hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) +		igc_erase_filter(adapter, rule); + +	hlist_for_each_entry(rule, &adapter->cls_flower_list, nfc_node) +		igc_erase_filter(adapter, rule); + +	spin_unlock(&adapter->nfc_lock); +} + +static void igc_nfc_filter_restore(struct igc_adapter *adapter) +{ +	struct igc_nfc_filter *rule; + +	spin_lock(&adapter->nfc_lock); + +	hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) +		igc_add_filter(adapter, rule); + +	spin_unlock(&adapter->nfc_lock);  }  /** @@ -1890,6 +2122,86 @@ static struct net_device_stats *igc_get_stats(struct net_device *netdev)  	return &netdev->stats;  } +static netdev_features_t igc_fix_features(struct net_device *netdev, +					  netdev_features_t features) +{ +	/* Since there is no support for separate Rx/Tx vlan accel +	 * enable/disable make sure Tx flag is always in same state as Rx. +	 */ +	if (features & NETIF_F_HW_VLAN_CTAG_RX) +		features |= NETIF_F_HW_VLAN_CTAG_TX; +	else +		features &= ~NETIF_F_HW_VLAN_CTAG_TX; + +	return features; +} + +static int igc_set_features(struct net_device *netdev, +			    netdev_features_t features) +{ +	netdev_features_t changed = netdev->features ^ features; +	struct igc_adapter *adapter = netdev_priv(netdev); + +	/* Add VLAN support */ +	if (!(changed & (NETIF_F_RXALL | NETIF_F_NTUPLE))) +		return 0; + +	if (!(features & NETIF_F_NTUPLE)) { +		struct hlist_node *node2; +		struct igc_nfc_filter *rule; + +		spin_lock(&adapter->nfc_lock); +		hlist_for_each_entry_safe(rule, node2, +					  &adapter->nfc_filter_list, nfc_node) { +			igc_erase_filter(adapter, rule); +			hlist_del(&rule->nfc_node); +			kfree(rule); +		} +		spin_unlock(&adapter->nfc_lock); +		adapter->nfc_filter_count = 0; +	} + +	netdev->features = features; + +	if (netif_running(netdev)) +		igc_reinit_locked(adapter); +	else +		igc_reset(adapter); + +	return 1; +} + +static netdev_features_t +igc_features_check(struct sk_buff *skb, struct net_device *dev, +		   netdev_features_t features) +{ +	unsigned int network_hdr_len, mac_hdr_len; + +	/* Make certain the headers can be described by a context descriptor */ +	mac_hdr_len = skb_network_header(skb) - skb->data; +	if (unlikely(mac_hdr_len > IGC_MAX_MAC_HDR_LEN)) +		return features & ~(NETIF_F_HW_CSUM | +				    NETIF_F_SCTP_CRC | +				    NETIF_F_HW_VLAN_CTAG_TX | +				    NETIF_F_TSO | +				    NETIF_F_TSO6); + +	network_hdr_len = skb_checksum_start(skb) - skb_network_header(skb); +	if (unlikely(network_hdr_len >  IGC_MAX_NETWORK_HDR_LEN)) +		return features & ~(NETIF_F_HW_CSUM | +				    NETIF_F_SCTP_CRC | +				    NETIF_F_TSO | +				    NETIF_F_TSO6); + +	/* We can only support IPv4 TSO in tunnels if we can mangle the +	 * inner IP ID field, so strip TSO if MANGLEID is not supported. +	 */ +	if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) +		features &= ~NETIF_F_TSO; + +	return features; +} +  /**   * igc_configure - configure the hardware for RX and TX   * @adapter: private board structure @@ -1906,6 +2218,7 @@ static void igc_configure(struct igc_adapter *adapter)  	igc_setup_mrqc(adapter);  	igc_setup_rctl(adapter); +	igc_nfc_filter_restore(adapter);  	igc_configure_tx(adapter);  	igc_configure_rx(adapter); @@ -1967,6 +2280,127 @@ static void igc_set_default_mac_filter(struct igc_adapter *adapter)  	igc_rar_set_index(adapter, 0);  } +/* If the filter to be added and an already existing filter express + * the same address and address type, it should be possible to only + * override the other configurations, for example the queue to steer + * traffic. + */ +static bool igc_mac_entry_can_be_used(const struct igc_mac_addr *entry, +				      const u8 *addr, const u8 flags) +{ +	if (!(entry->state & IGC_MAC_STATE_IN_USE)) +		return true; + +	if ((entry->state & IGC_MAC_STATE_SRC_ADDR) != +	    (flags & IGC_MAC_STATE_SRC_ADDR)) +		return false; + +	if (!ether_addr_equal(addr, entry->addr)) +		return false; + +	return true; +} + +/* Add a MAC filter for 'addr' directing matching traffic to 'queue', + * 'flags' is used to indicate what kind of match is made, match is by + * default for the destination address, if matching by source address + * is desired the flag IGC_MAC_STATE_SRC_ADDR can be used. + */ +static int igc_add_mac_filter_flags(struct igc_adapter *adapter, +				    const u8 *addr, const u8 queue, +				    const u8 flags) +{ +	struct igc_hw *hw = &adapter->hw; +	int rar_entries = hw->mac.rar_entry_count; +	int i; + +	if (is_zero_ether_addr(addr)) +		return -EINVAL; + +	/* Search for the first empty entry in the MAC table. +	 * Do not touch entries at the end of the table reserved for the VF MAC +	 * addresses. +	 */ +	for (i = 0; i < rar_entries; i++) { +		if (!igc_mac_entry_can_be_used(&adapter->mac_table[i], +					       addr, flags)) +			continue; + +		ether_addr_copy(adapter->mac_table[i].addr, addr); +		adapter->mac_table[i].queue = queue; +		adapter->mac_table[i].state |= IGC_MAC_STATE_IN_USE | flags; + +		igc_rar_set_index(adapter, i); +		return i; +	} + +	return -ENOSPC; +} + +int igc_add_mac_steering_filter(struct igc_adapter *adapter, +				const u8 *addr, u8 queue, u8 flags) +{ +	return igc_add_mac_filter_flags(adapter, addr, queue, +					IGC_MAC_STATE_QUEUE_STEERING | flags); +} + +/* Remove a MAC filter for 'addr' directing matching traffic to + * 'queue', 'flags' is used to indicate what kind of match need to be + * removed, match is by default for the destination address, if + * matching by source address is to be removed the flag + * IGC_MAC_STATE_SRC_ADDR can be used. + */ +static int igc_del_mac_filter_flags(struct igc_adapter *adapter, +				    const u8 *addr, const u8 queue, +				    const u8 flags) +{ +	struct igc_hw *hw = &adapter->hw; +	int rar_entries = hw->mac.rar_entry_count; +	int i; + +	if (is_zero_ether_addr(addr)) +		return -EINVAL; + +	/* Search for matching entry in the MAC table based on given address +	 * and queue. Do not touch entries at the end of the table reserved +	 * for the VF MAC addresses. +	 */ +	for (i = 0; i < rar_entries; i++) { +		if (!(adapter->mac_table[i].state & IGC_MAC_STATE_IN_USE)) +			continue; +		if ((adapter->mac_table[i].state & flags) != flags) +			continue; +		if (adapter->mac_table[i].queue != queue) +			continue; +		if (!ether_addr_equal(adapter->mac_table[i].addr, addr)) +			continue; + +		/* When a filter for the default address is "deleted", +		 * we return it to its initial configuration +		 */ +		if (adapter->mac_table[i].state & IGC_MAC_STATE_DEFAULT) { +			adapter->mac_table[i].state = +				IGC_MAC_STATE_DEFAULT | IGC_MAC_STATE_IN_USE; +		} else { +			adapter->mac_table[i].state = 0; +			adapter->mac_table[i].queue = 0; +			memset(adapter->mac_table[i].addr, 0, ETH_ALEN); +		} + +		igc_rar_set_index(adapter, i); +		return 0; +	} + +	return -ENOENT; +} + +int igc_del_mac_steering_filter(struct igc_adapter *adapter, +				const u8 *addr, u8 queue, u8 flags) +{ +	return igc_del_mac_filter_flags(adapter, addr, queue, +					IGC_MAC_STATE_QUEUE_STEERING | flags); +} +  /**   * igc_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set   * @netdev: network interface device structure @@ -3434,6 +3868,9 @@ static const struct net_device_ops igc_netdev_ops = {  	.ndo_set_mac_address	= igc_set_mac,  	.ndo_change_mtu		= igc_change_mtu,  	.ndo_get_stats		= igc_get_stats, +	.ndo_fix_features	= igc_fix_features, +	.ndo_set_features	= igc_set_features, +	.ndo_features_check	= igc_features_check,  };  /* PCIe configuration access */ @@ -3663,6 +4100,9 @@ static int igc_probe(struct pci_dev *pdev,  	if (err)  		goto err_sw_init; +	/* copy netdev features into list of user selectable features */ +	netdev->hw_features |= NETIF_F_NTUPLE; +  	/* MTU range: 68 - 9216 */  	netdev->min_mtu = ETH_MIN_MTU;  	netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE; diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h b/drivers/net/ethernet/intel/igc/igc_regs.h index 5afe7a8d3faf..50d7c04dccf5 100644 --- a/drivers/net/ethernet/intel/igc/igc_regs.h +++ b/drivers/net/ethernet/intel/igc/igc_regs.h @@ -80,8 +80,23 @@  /* MSI-X Table Register Descriptions */  #define IGC_PBACL		0x05B68  /* MSIx PBA Clear - R/W 1 to clear */ +/* RSS registers */ +#define IGC_MRQC		0x05818 /* Multiple Receive Control - RW */ + +/* Filtering Registers */ +#define IGC_ETQF(_n)		(0x05CB0 + (4 * (_n))) /* EType Queue Fltr */ + +/* ETQF register bit definitions */ +#define IGC_ETQF_FILTER_ENABLE	BIT(26) +#define IGC_ETQF_QUEUE_ENABLE	BIT(31) +#define IGC_ETQF_QUEUE_SHIFT	16 +#define IGC_ETQF_QUEUE_MASK	0x00070000 +#define IGC_ETQF_ETYPE_MASK	0x0000FFFF +  /* Redirection Table - RW Array */  #define IGC_RETA(_i)		(0x05C00 + ((_i) * 4)) +/* RSS Random Key - RW Array */ +#define IGC_RSSRK(_i)		(0x05C80 + ((_i) * 4))  /* Receive Register Descriptions */  #define IGC_RCTL		0x00100  /* Rx Control - RW */ @@ -101,6 +116,7 @@  #define IGC_UTA			0x0A000  /* Unicast Table Array - RW */  #define IGC_RAL(_n)		(0x05400 + ((_n) * 0x08))  #define IGC_RAH(_n)		(0x05404 + ((_n) * 0x08)) +#define IGC_VLAPQF		0x055B0  /* VLAN Priority Queue Filter VLAPQF */  /* Transmit Register Descriptions */  #define IGC_TCTL		0x00400  /* Tx Control - RW */  |