Commit d02f734c authored by Maciej Fijalkowski's avatar Maciej Fijalkowski Committed by Jeff Kirsher
Browse files

ice: add support for enabling/disabling single queues



Refactor the queue handling functions that are going through queue
arrays in a way that the logic done for a single queue is pulled out and
it will be called for each ring when traversing ring array. This implies
that when disabling Tx rings we won't fill up q_ids, q_teids and
q_handles arrays.  Drop also 'offset' parameter; the value from vsi's
txq_map is stored in ring->reg_idx and that drops the need for mentioned
parameter. Introduce the ice_vsi_cfg_txq, ice_vsi_stop_tx_ring and
ice_vsi_ctrl_rx_ring that are the functions with pulled out logic.

There's several Tx queue meta data (q_id, q_handle, q_teid and other)
that need to be set up during Tx queue disablement, so let's as well add
a helper structure that wraps it up and a function that will be filling
it up.

Signed-off-by: default avatarMaciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
Tested-by: default avatarAndrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent a1199d67
Loading
Loading
Loading
Loading
+198 −144
Original line number Diff line number Diff line
@@ -191,25 +191,24 @@ static int ice_pf_rxq_wait(struct ice_pf *pf, int pf_q, bool ena)
}

/**
 * ice_vsi_ctrl_rx_rings - Start or stop a VSI's Rx rings
 * ice_vsi_ctrl_rx_ring - Start or stop a VSI's Rx ring
 * @vsi: the VSI being configured
 * @ena: start or stop the Rx rings
 * @rxq_idx: Rx queue index
 */
static int ice_vsi_ctrl_rx_rings(struct ice_vsi *vsi, bool ena)
static int ice_vsi_ctrl_rx_ring(struct ice_vsi *vsi, bool ena, u16 rxq_idx)
{
	int pf_q = vsi->rxq_map[rxq_idx];
	struct ice_pf *pf = vsi->back;
	struct ice_hw *hw = &pf->hw;
	int i, ret = 0;

	for (i = 0; i < vsi->num_rxq; i++) {
		int pf_q = vsi->rxq_map[i];
	int ret = 0;
	u32 rx_reg;

	rx_reg = rd32(hw, QRX_CTRL(pf_q));

	/* Skip if the queue is already in the requested state */
	if (ena == !!(rx_reg & QRX_CTRL_QENA_STAT_M))
			continue;
		return 0;

	/* turn on/off the queue */
	if (ena)
@@ -220,12 +219,27 @@ static int ice_vsi_ctrl_rx_rings(struct ice_vsi *vsi, bool ena)

	/* wait for the change to finish */
	ret = ice_pf_rxq_wait(pf, pf_q, ena);
		if (ret) {
	if (ret)
		dev_err(&pf->pdev->dev,
			"VSI idx %d Rx ring %d %sable timeout\n",
			vsi->idx, pf_q, (ena ? "en" : "dis"));
			break;

	return ret;
}

/**
 * ice_vsi_ctrl_rx_rings - Start or stop a VSI's Rx rings
 * @vsi: the VSI being configured
 * @ena: start or stop the Rx rings
 */
static int ice_vsi_ctrl_rx_rings(struct ice_vsi *vsi, bool ena)
{
	int i, ret = 0;

	for (i = 0; i < vsi->num_rxq; i++) {
		ret = ice_vsi_ctrl_rx_ring(vsi, ena, i);
		if (ret)
			break;
	}

	return ret;
@@ -1649,43 +1663,26 @@ setup_rings:
}

/**
 * ice_vsi_cfg_txqs - Configure the VSI for Tx
 * @vsi: the VSI being configured
 * @rings: Tx ring array to be configured
 * @offset: offset within vsi->txq_map
 *
 * Return 0 on success and a negative value on error
 * Configure the Tx VSI for operation.
 * ice_vsi_cfg_txq - Configure single Tx queue
 * @vsi: the VSI that queue belongs to
 * @ring: Tx ring to be configured
 * @tc_q_idx: queue index within given TC
 * @qg_buf: queue group buffer
 * @tc: TC that Tx ring belongs to
 */
static int
ice_vsi_cfg_txqs(struct ice_vsi *vsi, struct ice_ring **rings, int offset)
ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, u16 tc_q_idx,
		struct ice_aqc_add_tx_qgrp *qg_buf, u8 tc)
{
	struct ice_aqc_add_tx_qgrp *qg_buf;
	struct ice_tlan_ctx tlan_ctx = { 0 };
	struct ice_aqc_add_txqs_perq *txq;
	struct ice_pf *pf = vsi->back;
	u8 num_q_grps, q_idx = 0;
	u8 buf_len = sizeof(*qg_buf);
	enum ice_status status;
	u16 buf_len, i, pf_q;
	int err = 0, tc;

	buf_len = sizeof(*qg_buf);
	qg_buf = devm_kzalloc(&pf->pdev->dev, buf_len, GFP_KERNEL);
	if (!qg_buf)
		return -ENOMEM;

	qg_buf->num_txqs = 1;
	num_q_grps = 1;

	/* set up and configure the Tx queues for each enabled TC */
	ice_for_each_traffic_class(tc) {
		if (!(vsi->tc_cfg.ena_tc & BIT(tc)))
			break;

		for (i = 0; i < vsi->tc_cfg.tc_info[tc].qcount_tx; i++) {
			struct ice_tlan_ctx tlan_ctx = { 0 };
	u16 pf_q;

			pf_q = vsi->txq_map[q_idx + offset];
			ice_setup_tx_ctx(rings[q_idx], &tlan_ctx, pf_q);
	pf_q = ring->reg_idx;
	ice_setup_tx_ctx(ring, &tlan_ctx, pf_q);
	/* copy context contents into the qg_buf */
	qg_buf->txqs[0].txq_id = cpu_to_le16(pf_q);
	ice_set_ctx((u8 *)&tlan_ctx, qg_buf->txqs[0].txq_ctx,
@@ -1694,17 +1691,20 @@ ice_vsi_cfg_txqs(struct ice_vsi *vsi, struct ice_ring **rings, int offset)
	/* init queue specific tail reg. It is referred as
	 * transmit comm scheduler queue doorbell.
	 */
			rings[q_idx]->tail =
				pf->hw.hw_addr + QTX_COMM_DBELL(pf_q);
			status = ice_ena_vsi_txq(vsi->port_info, vsi->idx, tc,
						 i, num_q_grps, qg_buf,
						 buf_len, NULL);
	ring->tail = pf->hw.hw_addr + QTX_COMM_DBELL(pf_q);

	/* Add unique software queue handle of the Tx queue per
	 * TC into the VSI Tx ring
	 */
	ring->q_handle = tc_q_idx;

	status = ice_ena_vsi_txq(vsi->port_info, vsi->idx, tc, ring->q_handle,
				 1, qg_buf, buf_len, NULL);
	if (status) {
		dev_err(&pf->pdev->dev,
			"Failed to set LAN Tx queue context, error: %d\n",
			status);
				err = -ENODEV;
				goto err_cfg_txqs;
		return -ENODEV;
	}

	/* Add Tx Queue TEID into the VSI Tx ring from the
@@ -1713,8 +1713,45 @@ ice_vsi_cfg_txqs(struct ice_vsi *vsi, struct ice_ring **rings, int offset)
	 */
	txq = &qg_buf->txqs[0];
	if (pf_q == le16_to_cpu(txq->txq_id))
				rings[q_idx]->txq_teid =
					le32_to_cpu(txq->q_teid);
		ring->txq_teid = le32_to_cpu(txq->q_teid);

	return 0;
}

/**
 * ice_vsi_cfg_txqs - Configure the VSI for Tx
 * @vsi: the VSI being configured
 * @rings: Tx ring array to be configured
 * @offset: offset within vsi->txq_map
 *
 * Return 0 on success and a negative value on error
 * Configure the Tx VSI for operation.
 */
static int
ice_vsi_cfg_txqs(struct ice_vsi *vsi, struct ice_ring **rings, int offset)
{
	struct ice_aqc_add_tx_qgrp *qg_buf;
	struct ice_pf *pf = vsi->back;
	u16 q_idx = 0, i;
	int err = 0;
	u8 tc;

	qg_buf = devm_kzalloc(&pf->pdev->dev, sizeof(*qg_buf), GFP_KERNEL);
	if (!qg_buf)
		return -ENOMEM;

	qg_buf->num_txqs = 1;

	/* set up and configure the Tx queues for each enabled TC */
	ice_for_each_traffic_class(tc) {
		if (!(vsi->tc_cfg.ena_tc & BIT(tc)))
			break;

		for (i = 0; i < vsi->tc_cfg.tc_info[tc].qcount_tx; i++) {
			err = ice_vsi_cfg_txq(vsi, rings[q_idx], i + offset,
					      qg_buf, tc);
			if (err)
				goto err_cfg_txqs;

			q_idx++;
		}
@@ -2061,67 +2098,28 @@ void ice_trigger_sw_intr(struct ice_hw *hw, struct ice_q_vector *q_vector)
}

/**
 * ice_vsi_stop_tx_rings - Disable Tx rings
 * ice_vsi_stop_tx_ring - Disable single Tx ring
 * @vsi: the VSI being configured
 * @rst_src: reset source
 * @rel_vmvf_num: Relative ID of VF/VM
 * @rings: Tx ring array to be stopped
 * @offset: offset within vsi->txq_map
 * @ring: Tx ring to be stopped
 * @txq_meta: Meta data of Tx ring to be stopped
 */
static int
ice_vsi_stop_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
		      u16 rel_vmvf_num, struct ice_ring **rings, int offset)
ice_vsi_stop_tx_ring(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
		     u16 rel_vmvf_num, struct ice_ring *ring,
		     struct ice_txq_meta *txq_meta)
{
	struct ice_pf *pf = vsi->back;
	struct ice_q_vector *q_vector;
	struct ice_hw *hw = &pf->hw;
	int tc, q_idx = 0, err = 0;
	u16 *q_ids, *q_handles, i;
	enum ice_status status;
	u32 *q_teids, val;

	if (vsi->num_txq > ICE_LAN_TXQ_MAX_QDIS)
		return -EINVAL;

	q_teids = devm_kcalloc(&pf->pdev->dev, vsi->num_txq, sizeof(*q_teids),
			       GFP_KERNEL);
	if (!q_teids)
		return -ENOMEM;

	q_ids = devm_kcalloc(&pf->pdev->dev, vsi->num_txq, sizeof(*q_ids),
			     GFP_KERNEL);
	if (!q_ids) {
		err = -ENOMEM;
		goto err_alloc_q_ids;
	}

	q_handles = devm_kcalloc(&pf->pdev->dev, vsi->num_txq,
				 sizeof(*q_handles), GFP_KERNEL);
	if (!q_handles) {
		err = -ENOMEM;
		goto err_alloc_q_handles;
	}

	/* set up the Tx queue list to be disabled for each enabled TC */
	ice_for_each_traffic_class(tc) {
		if (!(vsi->tc_cfg.ena_tc & BIT(tc)))
			break;

		for (i = 0; i < vsi->tc_cfg.tc_info[tc].qcount_tx; i++) {
			struct ice_q_vector *q_vector;

			if (!rings || !rings[q_idx]) {
				err = -EINVAL;
				goto err_out;
			}

			q_ids[i] = vsi->txq_map[q_idx + offset];
			q_teids[i] = rings[q_idx]->txq_teid;
			q_handles[i] = i;
	u32 val;

	/* clear cause_ena bit for disabled queues */
			val = rd32(hw, QINT_TQCTL(rings[i]->reg_idx));
	val = rd32(hw, QINT_TQCTL(ring->reg_idx));
	val &= ~QINT_TQCTL_CAUSE_ENA_M;
			wr32(hw, QINT_TQCTL(rings[i]->reg_idx), val);
	wr32(hw, QINT_TQCTL(ring->reg_idx), val);

	/* software is expected to wait for 100 ns */
	ndelay(100);
@@ -2129,45 +2127,102 @@ ice_vsi_stop_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
	/* trigger a software interrupt for the vector
	 * associated to the queue to schedule NAPI handler
	 */
			q_vector = rings[i]->q_vector;
	q_vector = ring->q_vector;
	if (q_vector)
		ice_trigger_sw_intr(hw, q_vector);

			q_idx++;
		}
		status = ice_dis_vsi_txq(vsi->port_info, vsi->idx, tc,
					 vsi->num_txq, q_handles, q_ids,
					 q_teids, rst_src, rel_vmvf_num, NULL);
	status = ice_dis_vsi_txq(vsi->port_info, txq_meta->vsi_idx,
				 txq_meta->tc, 1, &txq_meta->q_handle,
				 &txq_meta->q_id, &txq_meta->q_teid, rst_src,
				 rel_vmvf_num, NULL);

		/* if the disable queue command was exercised during an active
		 * reset flow, ICE_ERR_RESET_ONGOING is returned. This is not
		 * an error as the reset operation disables queues at the
		 * hardware level anyway.
	/* if the disable queue command was exercised during an
	 * active reset flow, ICE_ERR_RESET_ONGOING is returned.
	 * This is not an error as the reset operation disables
	 * queues at the hardware level anyway.
	 */
	if (status == ICE_ERR_RESET_ONGOING) {
			dev_dbg(&pf->pdev->dev,
		dev_dbg(&vsi->back->pdev->dev,
			"Reset in progress. LAN Tx queues already disabled\n");
	} else if (status == ICE_ERR_DOES_NOT_EXIST) {
			dev_dbg(&pf->pdev->dev,
				"LAN Tx queues does not exist, nothing to disabled\n");
		dev_dbg(&vsi->back->pdev->dev,
			"LAN Tx queues do not exist, nothing to disable\n");
	} else if (status) {
			dev_err(&pf->pdev->dev,
				"Failed to disable LAN Tx queues, error: %d\n",
				status);
			err = -ENODEV;
		dev_err(&vsi->back->pdev->dev,
			"Failed to disable LAN Tx queues, error: %d\n", status);
		return -ENODEV;
	}

	return 0;
}

err_out:
	devm_kfree(&pf->pdev->dev, q_handles);
/**
 * ice_fill_txq_meta - Prepare the Tx queue's meta data
 * @vsi: VSI that ring belongs to
 * @ring: ring that txq_meta will be based on
 * @txq_meta: a helper struct that wraps Tx queue's information
 *
 * Set up a helper struct that will contain all the necessary fields that
 * are needed for stopping Tx queue
 */
static void
ice_fill_txq_meta(struct ice_vsi *vsi, struct ice_ring *ring,
		  struct ice_txq_meta *txq_meta)
{
	u8 tc = 0;

err_alloc_q_handles:
	devm_kfree(&pf->pdev->dev, q_ids);
#ifdef CONFIG_DCB
	tc = ring->dcb_tc;
#endif /* CONFIG_DCB */
	txq_meta->q_id = ring->reg_idx;
	txq_meta->q_teid = ring->txq_teid;
	txq_meta->q_handle = ring->q_handle;
	txq_meta->vsi_idx = vsi->idx;
	txq_meta->tc = tc;
}

err_alloc_q_ids:
	devm_kfree(&pf->pdev->dev, q_teids);
/**
 * ice_vsi_stop_tx_rings - Disable Tx rings
 * @vsi: the VSI being configured
 * @rst_src: reset source
 * @rel_vmvf_num: Relative ID of VF/VM
 * @rings: Tx ring array to be stopped
 */
static int
ice_vsi_stop_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
		      u16 rel_vmvf_num, struct ice_ring **rings)
{
	u16 i, q_idx = 0;
	int status;
	u8 tc;

	return err;
	if (vsi->num_txq > ICE_LAN_TXQ_MAX_QDIS)
		return -EINVAL;

	/* set up the Tx queue list to be disabled for each enabled TC */
	ice_for_each_traffic_class(tc) {
		if (!(vsi->tc_cfg.ena_tc & BIT(tc)))
			break;

		for (i = 0; i < vsi->tc_cfg.tc_info[tc].qcount_tx; i++) {
			struct ice_txq_meta txq_meta = { };

			if (!rings || !rings[q_idx])
				return -EINVAL;

			ice_fill_txq_meta(vsi, rings[q_idx], &txq_meta);
			status = ice_vsi_stop_tx_ring(vsi, rst_src,
						      rel_vmvf_num,
						      rings[q_idx], &txq_meta);

			if (status)
				return status;

			q_idx++;
		}
	}

	return 0;
}

/**
@@ -2180,8 +2235,7 @@ int
ice_vsi_stop_lan_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
			  u16 rel_vmvf_num)
{
	return ice_vsi_stop_tx_rings(vsi, rst_src, rel_vmvf_num, vsi->tx_rings,
				     0);
	return ice_vsi_stop_tx_rings(vsi, rst_src, rel_vmvf_num, vsi->tx_rings);
}

/**
+16 −2
Original line number Diff line number Diff line
@@ -6,7 +6,21 @@

#include "ice.h"

int ice_add_mac_to_list(struct ice_vsi *vsi, struct list_head *add_list,
struct ice_txq_meta {
	/* Tx-scheduler element identifier */
	u32 q_teid;
	/* Entry in VSI's txq_map bitmap */
	u16 q_id;
	/* Relative index of Tx queue within TC */
	u16 q_handle;
	/* VSI index that Tx queue belongs to */
	u16 vsi_idx;
	/* TC number that Tx queue belongs to */
	u8 tc;
};

int
ice_add_mac_to_list(struct ice_vsi *vsi, struct list_head *add_list,
		    const u8 *macaddr);

void ice_free_fltr_list(struct device *dev, struct list_head *h);