Commit 67fe64d7 authored by Brett Creeley's avatar Brett Creeley Committed by Jeff Kirsher
Browse files

ice: Implement getting and setting ethtool coalesce



This patch includes the following ethtool operations:

1. get_coalesce
2. set_coalesce
3. get_per_q_coalesce
4. set_per_q_coalesce

Each ITR value (current_itr/target_itr) are stored on a per
ice_ring_container basis. This is because each valid ice_ring_container
can have 1 or more rings that are tied to the same q_vector ITR index.

Signed-off-by: default avatarBrett Creeley <brett.creeley@intel.com>
Signed-off-by: default avatarAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: default avatarAndrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 63f545ed
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -110,6 +110,9 @@ extern const char ice_drv_ver[];
#define ice_for_each_alloc_rxq(vsi, i) \
	for ((i) = 0; (i) < (vsi)->alloc_rxq; (i)++)

#define ice_for_each_q_vector(vsi, i) \
	for ((i) = 0; (i) < (vsi)->num_q_vectors; (i)++)

struct ice_tc_info {
	u16 qoffset;
	u16 qcount_tx;
+256 −0
Original line number Diff line number Diff line
@@ -1823,6 +1823,258 @@ static int ice_set_rxfh(struct net_device *netdev, const u32 *indir,
	return 0;
}

enum ice_container_type {
	ICE_RX_CONTAINER,
	ICE_TX_CONTAINER,
};

/**
 * ice_get_rc_coalesce - get ITR values for specific ring container
 * @ec: ethtool structure to fill with driver's coalesce settings
 * @c_type: container type, RX or TX
 * @rc: ring container that the ITR values will come from
 *
 * Query the device for ice_ring_container specific ITR values. This is
 * done per ice_ring_container because each q_vector can have 1 or more rings
 * and all of said ring(s) will have the same ITR values.
 *
 * Returns 0 on success, negative otherwise.
 */
static int
ice_get_rc_coalesce(struct ethtool_coalesce *ec, enum ice_container_type c_type,
		    struct ice_ring_container *rc)
{
	struct ice_pf *pf = rc->ring->vsi->back;

	switch (c_type) {
	case ICE_RX_CONTAINER:
		ec->use_adaptive_rx_coalesce = ITR_IS_DYNAMIC(rc->itr_setting);
		ec->rx_coalesce_usecs = rc->itr_setting & ~ICE_ITR_DYNAMIC;
		break;
	case ICE_TX_CONTAINER:
		ec->use_adaptive_tx_coalesce = ITR_IS_DYNAMIC(rc->itr_setting);
		ec->tx_coalesce_usecs = rc->itr_setting & ~ICE_ITR_DYNAMIC;
		break;
	default:
		dev_dbg(&pf->pdev->dev, "Invalid c_type %d\n", c_type);
		return -EINVAL;
	}

	return 0;
}

/**
 * __ice_get_coalesce - get ITR/INTRL values for the device
 * @netdev: pointer to the netdev associated with this query
 * @ec: ethtool structure to fill with driver's coalesce settings
 * @q_num: queue number to get the coalesce settings for
 */
static int
__ice_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec,
		   int q_num)
{
	struct ice_netdev_priv *np = netdev_priv(netdev);
	int tx = -EINVAL, rx = -EINVAL;
	struct ice_vsi *vsi = np->vsi;

	if (q_num < 0) {
		rx = ice_get_rc_coalesce(ec, ICE_RX_CONTAINER,
					 &vsi->rx_rings[0]->q_vector->rx);
		tx = ice_get_rc_coalesce(ec, ICE_TX_CONTAINER,
					 &vsi->tx_rings[0]->q_vector->tx);

		goto update_coalesced_frames;
	}

	if (q_num < vsi->num_rxq && q_num < vsi->num_txq) {
		rx = ice_get_rc_coalesce(ec, ICE_RX_CONTAINER,
					 &vsi->rx_rings[q_num]->q_vector->rx);
		tx = ice_get_rc_coalesce(ec, ICE_TX_CONTAINER,
					 &vsi->tx_rings[q_num]->q_vector->tx);
	} else if (q_num < vsi->num_rxq) {
		rx = ice_get_rc_coalesce(ec, ICE_RX_CONTAINER,
					 &vsi->rx_rings[q_num]->q_vector->rx);
	} else if (q_num < vsi->num_txq) {
		tx = ice_get_rc_coalesce(ec, ICE_TX_CONTAINER,
					 &vsi->tx_rings[q_num]->q_vector->tx);
	} else {
		/* q_num is invalid for both Rx and Tx queues */
		return -EINVAL;
	}

update_coalesced_frames:
	/* either q_num is invalid for both Rx and Tx queues or setting coalesce
	 * failed completely
	 */
	if (tx && rx)
		return -EINVAL;

	if (q_num < vsi->num_txq)
		ec->tx_max_coalesced_frames_irq = vsi->work_lmt;

	if (q_num < vsi->num_rxq)
		ec->rx_max_coalesced_frames_irq = vsi->work_lmt;

	return 0;
}

static int
ice_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec)
{
	return __ice_get_coalesce(netdev, ec, -1);
}

static int ice_get_per_q_coalesce(struct net_device *netdev, u32 q_num,
				  struct ethtool_coalesce *ec)
{
	return __ice_get_coalesce(netdev, ec, q_num);
}

/**
 * ice_set_rc_coalesce - set ITR values for specific ring container
 * @c_type: container type, RX or TX
 * @ec: ethtool structure from user to update ITR settings
 * @rc: ring container that the ITR values will come from
 * @vsi: VSI associated to the ring container
 *
 * Set specific ITR values. This is done per ice_ring_container because each
 * q_vector can have 1 or more rings and all of said ring(s) will have the same
 * ITR values.
 *
 * Returns 0 on success, negative otherwise.
 */
static int
ice_set_rc_coalesce(enum ice_container_type c_type, struct ethtool_coalesce *ec,
		    struct ice_ring_container *rc, struct ice_vsi *vsi)
{
	struct ice_pf *pf = vsi->back;
	u16 itr_setting;

	if (!rc->ring)
		return -EINVAL;

	itr_setting = rc->itr_setting & ~ICE_ITR_DYNAMIC;

	switch (c_type) {
	case ICE_RX_CONTAINER:
		if (ec->rx_coalesce_usecs != itr_setting &&
		    ec->use_adaptive_rx_coalesce) {
			netdev_info(vsi->netdev,
				    "Rx interrupt throttling cannot be changed if adaptive-rx is enabled\n");
			return -EINVAL;
		}

		if (ec->rx_coalesce_usecs > ICE_ITR_MAX) {
			netdev_info(vsi->netdev,
				    "Invalid value, rx-usecs range is 0-%d\n",
				   ICE_ITR_MAX);
			return -EINVAL;
		}

		if (ec->use_adaptive_rx_coalesce) {
			rc->itr_setting |= ICE_ITR_DYNAMIC;
		} else {
			rc->itr_setting = ITR_REG_ALIGN(ec->rx_coalesce_usecs);
			rc->target_itr = ITR_TO_REG(rc->itr_setting);
		}
		break;
	case ICE_TX_CONTAINER:
		if (ec->tx_coalesce_usecs != itr_setting &&
		    ec->use_adaptive_tx_coalesce) {
			netdev_info(vsi->netdev,
				    "Tx interrupt throttling cannot be changed if adaptive-tx is enabled\n");
			return -EINVAL;
		}

		if (ec->tx_coalesce_usecs > ICE_ITR_MAX) {
			netdev_info(vsi->netdev,
				    "Invalid value, tx-usecs range is 0-%d\n",
				   ICE_ITR_MAX);
			return -EINVAL;
		}

		if (ec->use_adaptive_tx_coalesce) {
			rc->itr_setting |= ICE_ITR_DYNAMIC;
		} else {
			rc->itr_setting = ITR_REG_ALIGN(ec->tx_coalesce_usecs);
			rc->target_itr = ITR_TO_REG(rc->itr_setting);
		}
		break;
	default:
		dev_dbg(&pf->pdev->dev, "Invalid container type %d\n", c_type);
		return -EINVAL;
	}

	return 0;
}

static int
__ice_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec,
		   int q_num)
{
	struct ice_netdev_priv *np = netdev_priv(netdev);
	int rx = -EINVAL, tx = -EINVAL;
	struct ice_vsi *vsi = np->vsi;

	if (q_num < 0) {
		int i;

		ice_for_each_q_vector(vsi, i) {
			struct ice_q_vector *q_vector = vsi->q_vectors[i];

			if (ice_set_rc_coalesce(ICE_RX_CONTAINER, ec,
						&q_vector->rx, vsi) ||
			    ice_set_rc_coalesce(ICE_TX_CONTAINER, ec,
						&q_vector->tx, vsi))
				return -EINVAL;
		}

		goto set_work_lmt;
	}

	if (q_num < vsi->num_rxq && q_num < vsi->num_txq) {
		rx = ice_set_rc_coalesce(ICE_RX_CONTAINER, ec,
					 &vsi->rx_rings[q_num]->q_vector->rx,
					 vsi);
		tx = ice_set_rc_coalesce(ICE_TX_CONTAINER, ec,
					 &vsi->tx_rings[q_num]->q_vector->tx,
					 vsi);
	} else if (q_num < vsi->num_rxq) {
		rx = ice_set_rc_coalesce(ICE_RX_CONTAINER, ec,
					 &vsi->rx_rings[q_num]->q_vector->rx,
					 vsi);
	} else if (q_num < vsi->num_txq) {
		tx  = ice_set_rc_coalesce(ICE_TX_CONTAINER, ec,
					  &vsi->tx_rings[q_num]->q_vector->tx,
					  vsi);
	}

	/* either q_num is invalid for both Rx and Tx queues or setting coalesce
	 * failed completely
	 */
	if (rx && tx)
		return -EINVAL;

set_work_lmt:
	if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq)
		vsi->work_lmt = max(ec->tx_max_coalesced_frames_irq,
				    ec->rx_max_coalesced_frames_irq);

	return 0;
}

static int
ice_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec)
{
	return __ice_set_coalesce(netdev, ec, -1);
}

static int ice_set_per_q_coalesce(struct net_device *netdev, u32 q_num,
				  struct ethtool_coalesce *ec)
{
	return __ice_set_coalesce(netdev, ec, q_num);
}

static const struct ethtool_ops ice_ethtool_ops = {
	.get_link_ksettings	= ice_get_link_ksettings,
	.set_link_ksettings	= ice_set_link_ksettings,
@@ -1834,6 +2086,8 @@ static const struct ethtool_ops ice_ethtool_ops = {
	.get_link		= ethtool_op_get_link,
	.get_eeprom_len		= ice_get_eeprom_len,
	.get_eeprom		= ice_get_eeprom,
	.get_coalesce		= ice_get_coalesce,
	.set_coalesce		= ice_set_coalesce,
	.get_strings		= ice_get_strings,
	.set_phys_id		= ice_set_phys_id,
	.get_ethtool_stats      = ice_get_ethtool_stats,
@@ -1850,6 +2104,8 @@ static const struct ethtool_ops ice_ethtool_ops = {
	.get_rxfh_indir_size	= ice_get_rxfh_indir_size,
	.get_rxfh		= ice_get_rxfh,
	.set_rxfh		= ice_set_rxfh,
	.get_per_queue_coalesce = ice_get_per_q_coalesce,
	.set_per_queue_coalesce = ice_set_per_q_coalesce,
};

/**
+2 −0
Original line number Diff line number Diff line
@@ -118,9 +118,11 @@ enum ice_rx_dtype {
#define ICE_TX_ITR	ICE_IDX_ITR1
#define ICE_ITR_8K	124
#define ICE_ITR_20K	50
#define ICE_ITR_MAX	8160
#define ICE_DFLT_TX_ITR	(ICE_ITR_20K | ICE_ITR_DYNAMIC)
#define ICE_DFLT_RX_ITR	(ICE_ITR_20K | ICE_ITR_DYNAMIC)
#define ICE_ITR_DYNAMIC	0x8000  /* used as flag for itr_setting */
#define ITR_IS_DYNAMIC(setting) (!!((setting) & ICE_ITR_DYNAMIC))
#define ITR_TO_REG(setting)	((setting) & ~ICE_ITR_DYNAMIC)
#define ICE_ITR_GRAN_S		1	/* Assume ITR granularity is 2us */
#define ICE_ITR_MASK		0x1FFE	/* ITR register value alignment mask */