Commit 168882d4 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-wangxun-more-ethtool'

Jiawen Wu says:

====================
Implement more ethtool_ops for Wangxun

Provide ethtool functions to operate pause param, ring param, coalesce
channel number and msglevel, for driver txgbe/ngbe.

v6 -> v7:
- Rebase on net-next.

v5 -> v6:
- Minor fixes address on Jakub Kicinski's comments.

v4 -> v5:
- Fix build error reported by kernel test robot.

v3 -> v4:
- Repartition the patches of phylink.
- Handle failure to allocate memory while changing ring parameters.
- Minor fixes about formatting.

v2 -> v3:
- Address comments:
  https://lore.kernel.org/all/ZW2loxTO6oKNYLew@shell.armlinux.org.uk/



v1 -> v2:
- Add phylink support for ngbe.
- Fix issue on interrupts when queue number is changed.
- Add more marco defines.
- Fix return codes.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5403d39b b746dc6b
Loading
Loading
Loading
Loading
+236 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include "wx_type.h"
#include "wx_ethtool.h"
#include "wx_hw.h"
#include "wx_lib.h"

struct wx_stats {
	char stat_string[ETH_GSTRING_LEN];
@@ -185,3 +186,238 @@ void wx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
	}
}
EXPORT_SYMBOL(wx_get_drvinfo);

int wx_nway_reset(struct net_device *netdev)
{
	struct wx *wx = netdev_priv(netdev);

	return phylink_ethtool_nway_reset(wx->phylink);
}
EXPORT_SYMBOL(wx_nway_reset);

int wx_get_link_ksettings(struct net_device *netdev,
			  struct ethtool_link_ksettings *cmd)
{
	struct wx *wx = netdev_priv(netdev);

	return phylink_ethtool_ksettings_get(wx->phylink, cmd);
}
EXPORT_SYMBOL(wx_get_link_ksettings);

int wx_set_link_ksettings(struct net_device *netdev,
			  const struct ethtool_link_ksettings *cmd)
{
	struct wx *wx = netdev_priv(netdev);

	return phylink_ethtool_ksettings_set(wx->phylink, cmd);
}
EXPORT_SYMBOL(wx_set_link_ksettings);

void wx_get_pauseparam(struct net_device *netdev,
		       struct ethtool_pauseparam *pause)
{
	struct wx *wx = netdev_priv(netdev);

	phylink_ethtool_get_pauseparam(wx->phylink, pause);
}
EXPORT_SYMBOL(wx_get_pauseparam);

int wx_set_pauseparam(struct net_device *netdev,
		      struct ethtool_pauseparam *pause)
{
	struct wx *wx = netdev_priv(netdev);

	return phylink_ethtool_set_pauseparam(wx->phylink, pause);
}
EXPORT_SYMBOL(wx_set_pauseparam);

void wx_get_ringparam(struct net_device *netdev,
		      struct ethtool_ringparam *ring,
		      struct kernel_ethtool_ringparam *kernel_ring,
		      struct netlink_ext_ack *extack)
{
	struct wx *wx = netdev_priv(netdev);

	ring->rx_max_pending = WX_MAX_RXD;
	ring->tx_max_pending = WX_MAX_TXD;
	ring->rx_mini_max_pending = 0;
	ring->rx_jumbo_max_pending = 0;
	ring->rx_pending = wx->rx_ring_count;
	ring->tx_pending = wx->tx_ring_count;
	ring->rx_mini_pending = 0;
	ring->rx_jumbo_pending = 0;
}
EXPORT_SYMBOL(wx_get_ringparam);

int wx_get_coalesce(struct net_device *netdev,
		    struct ethtool_coalesce *ec,
		    struct kernel_ethtool_coalesce *kernel_coal,
		    struct netlink_ext_ack *extack)
{
	struct wx *wx = netdev_priv(netdev);

	ec->tx_max_coalesced_frames_irq = wx->tx_work_limit;
	/* only valid if in constant ITR mode */
	if (wx->rx_itr_setting <= 1)
		ec->rx_coalesce_usecs = wx->rx_itr_setting;
	else
		ec->rx_coalesce_usecs = wx->rx_itr_setting >> 2;

	/* if in mixed tx/rx queues per vector mode, report only rx settings */
	if (wx->q_vector[0]->tx.count && wx->q_vector[0]->rx.count)
		return 0;

	/* only valid if in constant ITR mode */
	if (wx->tx_itr_setting <= 1)
		ec->tx_coalesce_usecs = wx->tx_itr_setting;
	else
		ec->tx_coalesce_usecs = wx->tx_itr_setting >> 2;

	return 0;
}
EXPORT_SYMBOL(wx_get_coalesce);

int wx_set_coalesce(struct net_device *netdev,
		    struct ethtool_coalesce *ec,
		    struct kernel_ethtool_coalesce *kernel_coal,
		    struct netlink_ext_ack *extack)
{
	struct wx *wx = netdev_priv(netdev);
	u16 tx_itr_param, rx_itr_param;
	struct wx_q_vector *q_vector;
	u16 max_eitr;
	int i;

	if (wx->q_vector[0]->tx.count && wx->q_vector[0]->rx.count) {
		/* reject Tx specific changes in case of mixed RxTx vectors */
		if (ec->tx_coalesce_usecs)
			return -EOPNOTSUPP;
	}

	if (ec->tx_max_coalesced_frames_irq)
		wx->tx_work_limit = ec->tx_max_coalesced_frames_irq;

	if (wx->mac.type == wx_mac_sp)
		max_eitr = WX_SP_MAX_EITR;
	else
		max_eitr = WX_EM_MAX_EITR;

	if ((ec->rx_coalesce_usecs > (max_eitr >> 2)) ||
	    (ec->tx_coalesce_usecs > (max_eitr >> 2)))
		return -EINVAL;

	if (ec->rx_coalesce_usecs > 1)
		wx->rx_itr_setting = ec->rx_coalesce_usecs << 2;
	else
		wx->rx_itr_setting = ec->rx_coalesce_usecs;

	if (wx->rx_itr_setting == 1)
		rx_itr_param = WX_20K_ITR;
	else
		rx_itr_param = wx->rx_itr_setting;

	if (ec->tx_coalesce_usecs > 1)
		wx->tx_itr_setting = ec->tx_coalesce_usecs << 2;
	else
		wx->tx_itr_setting = ec->tx_coalesce_usecs;

	if (wx->tx_itr_setting == 1) {
		if (wx->mac.type == wx_mac_sp)
			tx_itr_param = WX_12K_ITR;
		else
			tx_itr_param = WX_20K_ITR;
	} else {
		tx_itr_param = wx->tx_itr_setting;
	}

	/* mixed Rx/Tx */
	if (wx->q_vector[0]->tx.count && wx->q_vector[0]->rx.count)
		wx->tx_itr_setting = wx->rx_itr_setting;

	for (i = 0; i < wx->num_q_vectors; i++) {
		q_vector = wx->q_vector[i];
		if (q_vector->tx.count && !q_vector->rx.count)
			/* tx only */
			q_vector->itr = tx_itr_param;
		else
			/* rx only or mixed */
			q_vector->itr = rx_itr_param;
		wx_write_eitr(q_vector);
	}

	return 0;
}
EXPORT_SYMBOL(wx_set_coalesce);

static unsigned int wx_max_channels(struct wx *wx)
{
	unsigned int max_combined;

	if (!wx->msix_q_entries) {
		/* We only support one q_vector without MSI-X */
		max_combined = 1;
	} else {
		/* support up to max allowed queues with RSS */
		if (wx->mac.type == wx_mac_sp)
			max_combined = 63;
		else
			max_combined = 8;
	}

	return max_combined;
}

void wx_get_channels(struct net_device *dev,
		     struct ethtool_channels *ch)
{
	struct wx *wx = netdev_priv(dev);

	/* report maximum channels */
	ch->max_combined = wx_max_channels(wx);

	/* report info for other vector */
	if (wx->msix_q_entries) {
		ch->max_other = 1;
		ch->other_count = 1;
	}

	/* record RSS queues */
	ch->combined_count = wx->ring_feature[RING_F_RSS].indices;
}
EXPORT_SYMBOL(wx_get_channels);

int wx_set_channels(struct net_device *dev,
		    struct ethtool_channels *ch)
{
	unsigned int count = ch->combined_count;
	struct wx *wx = netdev_priv(dev);

	/* verify other_count has not changed */
	if (ch->other_count != 1)
		return -EINVAL;

	/* verify the number of channels does not exceed hardware limits */
	if (count > wx_max_channels(wx))
		return -EINVAL;

	wx->ring_feature[RING_F_RSS].limit = count;

	return 0;
}
EXPORT_SYMBOL(wx_set_channels);

u32 wx_get_msglevel(struct net_device *netdev)
{
	struct wx *wx = netdev_priv(netdev);

	return wx->msg_enable;
}
EXPORT_SYMBOL(wx_get_msglevel);

void wx_set_msglevel(struct net_device *netdev, u32 data)
{
	struct wx *wx = netdev_priv(netdev);

	wx->msg_enable = data;
}
EXPORT_SYMBOL(wx_set_msglevel);
+27 −0
Original line number Diff line number Diff line
@@ -13,4 +13,31 @@ void wx_get_mac_stats(struct net_device *netdev,
void wx_get_pause_stats(struct net_device *netdev,
			struct ethtool_pause_stats *stats);
void wx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info);
int wx_nway_reset(struct net_device *netdev);
int wx_get_link_ksettings(struct net_device *netdev,
			  struct ethtool_link_ksettings *cmd);
int wx_set_link_ksettings(struct net_device *netdev,
			  const struct ethtool_link_ksettings *cmd);
void wx_get_pauseparam(struct net_device *netdev,
		       struct ethtool_pauseparam *pause);
int wx_set_pauseparam(struct net_device *netdev,
		      struct ethtool_pauseparam *pause);
void wx_get_ringparam(struct net_device *netdev,
		      struct ethtool_ringparam *ring,
		      struct kernel_ethtool_ringparam *kernel_ring,
		      struct netlink_ext_ack *extack);
int wx_get_coalesce(struct net_device *netdev,
		    struct ethtool_coalesce *ec,
		    struct kernel_ethtool_coalesce *kernel_coal,
		    struct netlink_ext_ack *extack);
int wx_set_coalesce(struct net_device *netdev,
		    struct ethtool_coalesce *ec,
		    struct kernel_ethtool_coalesce *kernel_coal,
		    struct netlink_ext_ack *extack);
void wx_get_channels(struct net_device *dev,
		     struct ethtool_channels *ch);
int wx_set_channels(struct net_device *dev,
		    struct ethtool_channels *ch);
u32 wx_get_msglevel(struct net_device *netdev);
void wx_set_msglevel(struct net_device *netdev, u32 data);
#endif /* _WX_ETHTOOL_H_ */
+273 −2
Original line number Diff line number Diff line
@@ -149,9 +149,9 @@ void wx_irq_disable(struct wx *wx)
		int vector;

		for (vector = 0; vector < wx->num_q_vectors; vector++)
			synchronize_irq(wx->msix_entries[vector].vector);
			synchronize_irq(wx->msix_q_entries[vector].vector);

		synchronize_irq(wx->msix_entries[vector].vector);
		synchronize_irq(wx->msix_entry->vector);
	} else {
		synchronize_irq(pdev->irq);
	}
@@ -1158,6 +1158,81 @@ static void wx_set_rxpba(struct wx *wx)
	wr32(wx, WX_TDM_PB_THRE(0), txpbthresh);
}

#define WX_ETH_FRAMING 20

/**
 * wx_hpbthresh - calculate high water mark for flow control
 *
 * @wx: board private structure to calculate for
 **/
static int wx_hpbthresh(struct wx *wx)
{
	struct net_device *dev = wx->netdev;
	int link, tc, kb, marker;
	u32 dv_id, rx_pba;

	/* Calculate max LAN frame size */
	link = dev->mtu + ETH_HLEN + ETH_FCS_LEN + WX_ETH_FRAMING;
	tc = link;

	/* Calculate delay value for device */
	dv_id = WX_DV(link, tc);

	/* Delay value is calculated in bit times convert to KB */
	kb = WX_BT2KB(dv_id);
	rx_pba = rd32(wx, WX_RDB_PB_SZ(0)) >> WX_RDB_PB_SZ_SHIFT;

	marker = rx_pba - kb;

	/* It is possible that the packet buffer is not large enough
	 * to provide required headroom. In this case throw an error
	 * to user and a do the best we can.
	 */
	if (marker < 0) {
		dev_warn(&wx->pdev->dev,
			 "Packet Buffer can not provide enough headroom to support flow control. Decrease MTU or number of traffic classes\n");
		marker = tc + 1;
	}

	return marker;
}

/**
 * wx_lpbthresh - calculate low water mark for flow control
 *
 * @wx: board private structure to calculate for
 **/
static int wx_lpbthresh(struct wx *wx)
{
	struct net_device *dev = wx->netdev;
	u32 dv_id;
	int tc;

	/* Calculate max LAN frame size */
	tc = dev->mtu + ETH_HLEN + ETH_FCS_LEN;

	/* Calculate delay value for device */
	dv_id = WX_LOW_DV(tc);

	/* Delay value is calculated in bit times convert to KB */
	return WX_BT2KB(dv_id);
}

/**
 * wx_pbthresh_setup - calculate and setup high low water marks
 *
 * @wx: board private structure to calculate for
 **/
static void wx_pbthresh_setup(struct wx *wx)
{
	wx->fc.high_water = wx_hpbthresh(wx);
	wx->fc.low_water = wx_lpbthresh(wx);

	/* Low water marks must not be larger than high water marks */
	if (wx->fc.low_water > wx->fc.high_water)
		wx->fc.low_water = 0;
}

static void wx_configure_port(struct wx *wx)
{
	u32 value, i;
@@ -1522,6 +1597,72 @@ static void wx_restore_vlan(struct wx *wx)
		wx_vlan_rx_add_vid(wx->netdev, htons(ETH_P_8021Q), vid);
}

static void wx_store_reta(struct wx *wx)
{
	u8 *indir_tbl = wx->rss_indir_tbl;
	u32 reta = 0;
	u32 i;

	/* Fill out the redirection table as follows:
	 *  - 8 bit wide entries containing 4 bit RSS index
	 */
	for (i = 0; i < WX_MAX_RETA_ENTRIES; i++) {
		reta |= indir_tbl[i] << (i & 0x3) * 8;
		if ((i & 3) == 3) {
			wr32(wx, WX_RDB_RSSTBL(i >> 2), reta);
			reta = 0;
		}
	}
}

static void wx_setup_reta(struct wx *wx)
{
	u16 rss_i = wx->ring_feature[RING_F_RSS].indices;
	u32 random_key_size = WX_RSS_KEY_SIZE / 4;
	u32 i, j;

	/* Fill out hash function seeds */
	for (i = 0; i < random_key_size; i++)
		wr32(wx, WX_RDB_RSSRK(i), wx->rss_key[i]);

	/* Fill out redirection table */
	memset(wx->rss_indir_tbl, 0, sizeof(wx->rss_indir_tbl));

	for (i = 0, j = 0; i < WX_MAX_RETA_ENTRIES; i++, j++) {
		if (j == rss_i)
			j = 0;

		wx->rss_indir_tbl[i] = j;
	}

	wx_store_reta(wx);
}

static void wx_setup_mrqc(struct wx *wx)
{
	u32 rss_field = 0;

	/* Disable indicating checksum in descriptor, enables RSS hash */
	wr32m(wx, WX_PSR_CTL, WX_PSR_CTL_PCSD, WX_PSR_CTL_PCSD);

	/* Perform hash on these packet types */
	rss_field = WX_RDB_RA_CTL_RSS_IPV4 |
		    WX_RDB_RA_CTL_RSS_IPV4_TCP |
		    WX_RDB_RA_CTL_RSS_IPV4_UDP |
		    WX_RDB_RA_CTL_RSS_IPV6 |
		    WX_RDB_RA_CTL_RSS_IPV6_TCP |
		    WX_RDB_RA_CTL_RSS_IPV6_UDP;

	netdev_rss_key_fill(wx->rss_key, sizeof(wx->rss_key));

	wx_setup_reta(wx);

	if (wx->rss_enabled)
		rss_field |= WX_RDB_RA_CTL_RSS_EN;

	wr32(wx, WX_RDB_RA_CTL, rss_field);
}

/**
 * wx_configure_rx - Configure Receive Unit after Reset
 * @wx: pointer to private structure
@@ -1554,6 +1695,8 @@ void wx_configure_rx(struct wx *wx)
		wr32(wx, WX_PSR_CTL, psrctl);
	}

	wx_setup_mrqc(wx);

	/* set_rx_buffer_len must be called before ring initialization */
	wx_set_rx_buffer_len(wx);

@@ -1584,6 +1727,7 @@ static void wx_configure_isb(struct wx *wx)
void wx_configure(struct wx *wx)
{
	wx_set_rxpba(wx);
	wx_pbthresh_setup(wx);
	wx_configure_port(wx);

	wx_set_rx_mode(wx->netdev);
@@ -1750,6 +1894,28 @@ int wx_get_pcie_msix_counts(struct wx *wx, u16 *msix_count, u16 max_msix_count)
}
EXPORT_SYMBOL(wx_get_pcie_msix_counts);

/**
 * wx_init_rss_key - Initialize wx RSS key
 * @wx: device handle
 *
 * Allocates and initializes the RSS key if it is not allocated.
 **/
static int wx_init_rss_key(struct wx *wx)
{
	u32 *rss_key;

	if (!wx->rss_key) {
		rss_key = kzalloc(WX_RSS_KEY_SIZE, GFP_KERNEL);
		if (unlikely(!rss_key))
			return -ENOMEM;

		netdev_rss_key_fill(rss_key, WX_RSS_KEY_SIZE);
		wx->rss_key = rss_key;
	}

	return 0;
}

int wx_sw_init(struct wx *wx)
{
	struct pci_dev *pdev = wx->pdev;
@@ -1777,14 +1943,23 @@ int wx_sw_init(struct wx *wx)
		wx->subsystem_device_id = swab16((u16)ssid);
	}

	err = wx_init_rss_key(wx);
	if (err < 0) {
		wx_err(wx, "rss key allocation failed\n");
		return err;
	}

	wx->mac_table = kcalloc(wx->mac.num_rar_entries,
				sizeof(struct wx_mac_addr),
				GFP_KERNEL);
	if (!wx->mac_table) {
		wx_err(wx, "mac_table allocation failed\n");
		kfree(wx->rss_key);
		return -ENOMEM;
	}

	wx->msix_in_use = false;

	return 0;
}
EXPORT_SYMBOL(wx_sw_init);
@@ -2003,6 +2178,102 @@ int wx_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
}
EXPORT_SYMBOL(wx_vlan_rx_kill_vid);

static void wx_enable_rx_drop(struct wx *wx, struct wx_ring *ring)
{
	u16 reg_idx = ring->reg_idx;
	u32 srrctl;

	srrctl = rd32(wx, WX_PX_RR_CFG(reg_idx));
	srrctl |= WX_PX_RR_CFG_DROP_EN;

	wr32(wx, WX_PX_RR_CFG(reg_idx), srrctl);
}

static void wx_disable_rx_drop(struct wx *wx, struct wx_ring *ring)
{
	u16 reg_idx = ring->reg_idx;
	u32 srrctl;

	srrctl = rd32(wx, WX_PX_RR_CFG(reg_idx));
	srrctl &= ~WX_PX_RR_CFG_DROP_EN;

	wr32(wx, WX_PX_RR_CFG(reg_idx), srrctl);
}

int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause)
{
	u16 pause_time = WX_DEFAULT_FCPAUSE;
	u32 mflcn_reg, fccfg_reg, reg;
	u32 fcrtl, fcrth;
	int i;

	/* Low water mark of zero causes XOFF floods */
	if (tx_pause && wx->fc.high_water) {
		if (!wx->fc.low_water || wx->fc.low_water >= wx->fc.high_water) {
			wx_err(wx, "Invalid water mark configuration\n");
			return -EINVAL;
		}
	}

	/* Disable any previous flow control settings */
	mflcn_reg = rd32(wx, WX_MAC_RX_FLOW_CTRL);
	mflcn_reg &= ~WX_MAC_RX_FLOW_CTRL_RFE;

	fccfg_reg = rd32(wx, WX_RDB_RFCC);
	fccfg_reg &= ~WX_RDB_RFCC_RFCE_802_3X;

	if (rx_pause)
		mflcn_reg |= WX_MAC_RX_FLOW_CTRL_RFE;
	if (tx_pause)
		fccfg_reg |= WX_RDB_RFCC_RFCE_802_3X;

	/* Set 802.3x based flow control settings. */
	wr32(wx, WX_MAC_RX_FLOW_CTRL, mflcn_reg);
	wr32(wx, WX_RDB_RFCC, fccfg_reg);

	/* Set up and enable Rx high/low water mark thresholds, enable XON. */
	if (tx_pause && wx->fc.high_water) {
		fcrtl = (wx->fc.low_water << 10) | WX_RDB_RFCL_XONE;
		wr32(wx, WX_RDB_RFCL, fcrtl);
		fcrth = (wx->fc.high_water << 10) | WX_RDB_RFCH_XOFFE;
	} else {
		wr32(wx, WX_RDB_RFCL, 0);
		/* In order to prevent Tx hangs when the internal Tx
		 * switch is enabled we must set the high water mark
		 * to the Rx packet buffer size - 24KB.  This allows
		 * the Tx switch to function even under heavy Rx
		 * workloads.
		 */
		fcrth = rd32(wx, WX_RDB_PB_SZ(0)) - 24576;
	}

	wr32(wx, WX_RDB_RFCH, fcrth);

	/* Configure pause time */
	reg = pause_time * 0x00010001;
	wr32(wx, WX_RDB_RFCV, reg);

	/* Configure flow control refresh threshold value */
	wr32(wx, WX_RDB_RFCRT, pause_time / 2);

	/*  We should set the drop enable bit if:
	 *  Number of Rx queues > 1 and flow control is disabled
	 *
	 *  This allows us to avoid head of line blocking for security
	 *  and performance reasons.
	 */
	if (wx->num_rx_queues > 1 && !tx_pause) {
		for (i = 0; i < wx->num_rx_queues; i++)
			wx_enable_rx_drop(wx, wx->rx_ring[i]);
	} else {
		for (i = 0; i < wx->num_rx_queues; i++)
			wx_disable_rx_drop(wx, wx->rx_ring[i]);
	}

	return 0;
}
EXPORT_SYMBOL(wx_fc_enable);

/**
 * wx_update_stats - Update the board statistics counters.
 * @wx: board private structure
+1 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ int wx_get_pcie_msix_counts(struct wx *wx, u16 *msix_count, u16 max_msix_count);
int wx_sw_init(struct wx *wx);
int wx_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid);
int wx_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid);
int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause);
void wx_update_stats(struct wx *wx);
void wx_clear_hw_cntrs(struct wx *wx);

+126 −28
Original line number Diff line number Diff line
@@ -1568,8 +1568,14 @@ EXPORT_SYMBOL(wx_napi_disable_all);
 **/
static void wx_set_rss_queues(struct wx *wx)
{
	wx->num_rx_queues = wx->mac.max_rx_queues;
	wx->num_tx_queues = wx->mac.max_tx_queues;
	struct wx_ring_feature *f;

	/* set mask for 16 queue limit of RSS */
	f = &wx->ring_feature[RING_F_RSS];
	f->indices = f->limit;

	wx->num_rx_queues = f->limit;
	wx->num_tx_queues = f->limit;
}

static void wx_set_num_queues(struct wx *wx)
@@ -1595,35 +1601,51 @@ static int wx_acquire_msix_vectors(struct wx *wx)
	struct irq_affinity affd = {0, };
	int nvecs, i;

	nvecs = min_t(int, num_online_cpus(), wx->mac.max_msix_vectors);
	/* We start by asking for one vector per queue pair */
	nvecs = max(wx->num_rx_queues, wx->num_tx_queues);
	nvecs = min_t(int, nvecs, num_online_cpus());
	nvecs = min_t(int, nvecs, wx->mac.max_msix_vectors);

	wx->msix_entries = kcalloc(nvecs,
				   sizeof(struct msix_entry),
	wx->msix_q_entries = kcalloc(nvecs, sizeof(struct msix_entry),
				     GFP_KERNEL);
	if (!wx->msix_entries)
	if (!wx->msix_q_entries)
		return -ENOMEM;

	/* One for non-queue interrupts */
	nvecs += 1;

	if (!wx->msix_in_use) {
		wx->msix_entry = kcalloc(1, sizeof(struct msix_entry),
					 GFP_KERNEL);
		if (!wx->msix_entry) {
			kfree(wx->msix_q_entries);
			wx->msix_q_entries = NULL;
			return -ENOMEM;
		}
	}

	nvecs = pci_alloc_irq_vectors_affinity(wx->pdev, nvecs,
					       nvecs,
					       PCI_IRQ_MSIX | PCI_IRQ_AFFINITY,
					       &affd);
	if (nvecs < 0) {
		wx_err(wx, "Failed to allocate MSI-X interrupts. Err: %d\n", nvecs);
		kfree(wx->msix_entries);
		wx->msix_entries = NULL;
		kfree(wx->msix_q_entries);
		wx->msix_q_entries = NULL;
		kfree(wx->msix_entry);
		wx->msix_entry = NULL;
		return nvecs;
	}

	wx->msix_entry->entry = 0;
	wx->msix_entry->vector = pci_irq_vector(wx->pdev, 0);
	nvecs -= 1;
	for (i = 0; i < nvecs; i++) {
		wx->msix_entries[i].entry = i;
		wx->msix_entries[i].vector = pci_irq_vector(wx->pdev, i);
		wx->msix_q_entries[i].entry = i;
		wx->msix_q_entries[i].vector = pci_irq_vector(wx->pdev, i + 1);
	}

	/* one for msix_other */
	nvecs -= 1;
	wx->num_q_vectors = nvecs;
	wx->num_rx_queues = nvecs;
	wx->num_tx_queues = nvecs;

	return 0;
}
@@ -1645,9 +1667,11 @@ static int wx_set_interrupt_capability(struct wx *wx)
	if (ret == 0 || (ret == -ENOMEM))
		return ret;

	wx->num_rx_queues = 1;
	wx->num_tx_queues = 1;
	wx->num_q_vectors = 1;
	/* Disable RSS */
	dev_warn(&wx->pdev->dev, "Disabling RSS support\n");
	wx->ring_feature[RING_F_RSS].limit = 1;

	wx_set_num_queues(wx);

	/* minmum one for queue, one for misc*/
	nvecs = 1;
@@ -1905,8 +1929,12 @@ void wx_reset_interrupt_capability(struct wx *wx)
		return;

	if (pdev->msix_enabled) {
		kfree(wx->msix_entries);
		wx->msix_entries = NULL;
		kfree(wx->msix_q_entries);
		wx->msix_q_entries = NULL;
		if (!wx->msix_in_use) {
			kfree(wx->msix_entry);
			wx->msix_entry = NULL;
		}
	}
	pci_free_irq_vectors(wx->pdev);
}
@@ -1978,7 +2006,7 @@ void wx_free_irq(struct wx *wx)

	for (vector = 0; vector < wx->num_q_vectors; vector++) {
		struct wx_q_vector *q_vector = wx->q_vector[vector];
		struct msix_entry *entry = &wx->msix_entries[vector];
		struct msix_entry *entry = &wx->msix_q_entries[vector];

		/* free only the irqs that were actually requested */
		if (!q_vector->rx.ring && !q_vector->tx.ring)
@@ -1988,7 +2016,7 @@ void wx_free_irq(struct wx *wx)
	}

	if (wx->mac.type == wx_mac_em)
		free_irq(wx->msix_entries[vector].vector, wx);
		free_irq(wx->msix_entry->vector, wx);
}
EXPORT_SYMBOL(wx_free_irq);

@@ -2065,6 +2093,7 @@ static void wx_set_ivar(struct wx *wx, s8 direction,
		wr32(wx, WX_PX_MISC_IVAR, ivar);
	} else {
		/* tx or rx causes */
		msix_vector += 1; /* offset for queue vectors */
		msix_vector |= WX_PX_IVAR_ALLOC_VAL;
		index = ((16 * (queue & 1)) + (8 * direction));
		ivar = rd32(wx, WX_PX_IVAR(queue >> 1));
@@ -2082,7 +2111,7 @@ static void wx_set_ivar(struct wx *wx, s8 direction,
 * when it needs to update EITR registers at runtime.  Hardware
 * specific quirks/differences are taken care of here.
 */
static void wx_write_eitr(struct wx_q_vector *q_vector)
void wx_write_eitr(struct wx_q_vector *q_vector)
{
	struct wx *wx = q_vector->wx;
	int v_idx = q_vector->v_idx;
@@ -2095,7 +2124,7 @@ static void wx_write_eitr(struct wx_q_vector *q_vector)

	itr_reg |= WX_PX_ITR_CNT_WDIS;

	wr32(wx, WX_PX_ITR(v_idx), itr_reg);
	wr32(wx, WX_PX_ITR(v_idx + 1), itr_reg);
}

/**
@@ -2141,9 +2170,9 @@ void wx_configure_vectors(struct wx *wx)
		wx_write_eitr(q_vector);
	}

	wx_set_ivar(wx, -1, 0, v_idx);
	wx_set_ivar(wx, -1, 0, 0);
	if (pdev->msix_enabled)
		wr32(wx, WX_PX_ITR(v_idx), 1950);
		wr32(wx, WX_PX_ITR(0), 1950);
}
EXPORT_SYMBOL(wx_configure_vectors);

@@ -2656,11 +2685,14 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
	netdev_features_t changed = netdev->features ^ features;
	struct wx *wx = netdev_priv(netdev);

	if (changed & NETIF_F_RXHASH)
	if (features & NETIF_F_RXHASH) {
		wr32m(wx, WX_RDB_RA_CTL, WX_RDB_RA_CTL_RSS_EN,
		      WX_RDB_RA_CTL_RSS_EN);
	else
		wx->rss_enabled = true;
	} else {
		wr32m(wx, WX_RDB_RA_CTL, WX_RDB_RA_CTL_RSS_EN, 0);
		wx->rss_enabled = false;
	}

	if (changed &
	    (NETIF_F_HW_VLAN_CTAG_RX |
@@ -2671,4 +2703,70 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
}
EXPORT_SYMBOL(wx_set_features);

void wx_set_ring(struct wx *wx, u32 new_tx_count,
		 u32 new_rx_count, struct wx_ring *temp_ring)
{
	int i, err = 0;

	/* Setup new Tx resources and free the old Tx resources in that order.
	 * We can then assign the new resources to the rings via a memcpy.
	 * The advantage to this approach is that we are guaranteed to still
	 * have resources even in the case of an allocation failure.
	 */
	if (new_tx_count != wx->tx_ring_count) {
		for (i = 0; i < wx->num_tx_queues; i++) {
			memcpy(&temp_ring[i], wx->tx_ring[i],
			       sizeof(struct wx_ring));

			temp_ring[i].count = new_tx_count;
			err = wx_setup_tx_resources(&temp_ring[i]);
			if (err) {
				wx_err(wx, "setup new tx resources failed, keep using the old config\n");
				while (i) {
					i--;
					wx_free_tx_resources(&temp_ring[i]);
				}
				return;
			}
		}

		for (i = 0; i < wx->num_tx_queues; i++) {
			wx_free_tx_resources(wx->tx_ring[i]);

			memcpy(wx->tx_ring[i], &temp_ring[i],
			       sizeof(struct wx_ring));
		}

		wx->tx_ring_count = new_tx_count;
	}

	/* Repeat the process for the Rx rings if needed */
	if (new_rx_count != wx->rx_ring_count) {
		for (i = 0; i < wx->num_rx_queues; i++) {
			memcpy(&temp_ring[i], wx->rx_ring[i],
			       sizeof(struct wx_ring));

			temp_ring[i].count = new_rx_count;
			err = wx_setup_rx_resources(&temp_ring[i]);
			if (err) {
				wx_err(wx, "setup new rx resources failed, keep using the old config\n");
				while (i) {
					i--;
					wx_free_rx_resources(&temp_ring[i]);
				}
				return;
			}
		}

		for (i = 0; i < wx->num_rx_queues; i++) {
			wx_free_rx_resources(wx->rx_ring[i]);
			memcpy(wx->rx_ring[i], &temp_ring[i],
			       sizeof(struct wx_ring));
		}

		wx->rx_ring_count = new_rx_count;
	}
}
EXPORT_SYMBOL(wx_set_ring);

MODULE_LICENSE("GPL");
Loading