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

queue

Tony Nguyen says:

====================
1GbE Intel Wired LAN Driver Updates 2021-08-27

ravindhan Gunasekaran says:

This adds support for Credit-based shaper qdisc offload from
Traffic Control system. It enables traffic prioritization and
bandwidth reservation via the Credit-Based Shaper which is
implemented in hardware by i225 controller.

Patch 1/3 adds a default cycle-time for TSN mode to be configured.

Patch 2/3 helps to separate TSN mode programming on the fly and
during reset sequence. It also simplifies handling features flags
for various TSN modes supported by i225 in the driver.

Patch 3/3 adds support for IEEE802.1Qav(CBS) standard
implemented in i225 HW. Two sets of CBS HW shapers are present
in i225 and driver enables them in the two high priority queues.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c7722511 1ab011b0
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -98,6 +98,13 @@ struct igc_ring {
	u32 start_time;
	u32 end_time;

	/* CBS parameters */
	bool cbs_enable;                /* indicates if CBS is enabled */
	s32 idleslope;                  /* idleSlope in kbps */
	s32 sendslope;                  /* sendSlope in kbps */
	s32 hicredit;                   /* hiCredit in bytes */
	s32 locredit;                   /* loCredit in bytes */

	/* everything past this point are written often */
	u16 next_to_clean;
	u16 next_to_use;
@@ -290,6 +297,10 @@ extern char igc_driver_name[];
#define IGC_FLAG_VLAN_PROMISC		BIT(15)
#define IGC_FLAG_RX_LEGACY		BIT(16)
#define IGC_FLAG_TSN_QBV_ENABLED	BIT(17)
#define IGC_FLAG_TSN_QAV_ENABLED	BIT(18)

#define IGC_FLAG_TSN_ANY_ENABLED \
	(IGC_FLAG_TSN_QBV_ENABLED | IGC_FLAG_TSN_QAV_ENABLED)

#define IGC_FLAG_RSS_FIELD_IPV4_UDP	BIT(6)
#define IGC_FLAG_RSS_FIELD_IPV6_UDP	BIT(7)
+8 −0
Original line number Diff line number Diff line
@@ -518,6 +518,14 @@
#define IGC_TXQCTL_QUEUE_MODE_LAUNCHT	0x00000001
#define IGC_TXQCTL_STRICT_CYCLE		0x00000002
#define IGC_TXQCTL_STRICT_END		0x00000004
#define IGC_TXQCTL_QAV_SEL_MASK		0x000000C0
#define IGC_TXQCTL_QAV_SEL_CBS0		0x00000080
#define IGC_TXQCTL_QAV_SEL_CBS1		0x000000C0

#define IGC_TQAVCC_IDLESLOPE_MASK	0xFFFF
#define IGC_TQAVCC_KEEP_CREDITS		BIT(30)

#define IGC_MAX_SR_QUEUES		2

/* Receive Checksum Control */
#define IGC_RXCSUM_CRCOFL	0x00000800   /* CRC32 offload enable */
+93 −17
Original line number Diff line number Diff line
@@ -120,7 +120,7 @@ void igc_reset(struct igc_adapter *adapter)
	igc_ptp_reset(adapter);

	/* Re-enable TSN offloading, where applicable. */
	igc_tsn_offload_apply(adapter);
	igc_tsn_reset(adapter);

	igc_get_phy_info(hw);
}
@@ -5749,7 +5749,6 @@ static int igc_save_launchtime_params(struct igc_adapter *adapter, int queue,
				      bool enable)
{
	struct igc_ring *ring;
	int i;

	if (queue < 0 || queue >= adapter->num_tx_queues)
		return -EINVAL;
@@ -5757,17 +5756,6 @@ static int igc_save_launchtime_params(struct igc_adapter *adapter, int queue,
	ring = adapter->tx_ring[queue];
	ring->launchtime_enable = enable;

	if (adapter->base_time)
		return 0;

	adapter->cycle_time = NSEC_PER_SEC;

	for (i = 0; i < adapter->num_tx_queues; i++) {
		ring = adapter->tx_ring[i];
		ring->start_time = 0;
		ring->end_time = NSEC_PER_SEC;
	}

	return 0;
}

@@ -5840,16 +5828,31 @@ static int igc_tsn_enable_launchtime(struct igc_adapter *adapter,
	return igc_tsn_offload_apply(adapter);
}

static int igc_tsn_clear_schedule(struct igc_adapter *adapter)
{
	int i;

	adapter->base_time = 0;
	adapter->cycle_time = NSEC_PER_SEC;

	for (i = 0; i < adapter->num_tx_queues; i++) {
		struct igc_ring *ring = adapter->tx_ring[i];

		ring->start_time = 0;
		ring->end_time = NSEC_PER_SEC;
	}

	return 0;
}

static int igc_save_qbv_schedule(struct igc_adapter *adapter,
				 struct tc_taprio_qopt_offload *qopt)
{
	u32 start_time = 0, end_time = 0;
	size_t n;

	if (!qopt->enable) {
		adapter->base_time = 0;
		return 0;
	}
	if (!qopt->enable)
		return igc_tsn_clear_schedule(adapter);

	if (adapter->base_time)
		return -EALREADY;
@@ -5901,6 +5904,74 @@ static int igc_tsn_enable_qbv_scheduling(struct igc_adapter *adapter,
	return igc_tsn_offload_apply(adapter);
}

static int igc_save_cbs_params(struct igc_adapter *adapter, int queue,
			       bool enable, int idleslope, int sendslope,
			       int hicredit, int locredit)
{
	bool cbs_status[IGC_MAX_SR_QUEUES] = { false };
	struct net_device *netdev = adapter->netdev;
	struct igc_ring *ring;
	int i;

	/* i225 has two sets of credit-based shaper logic.
	 * Supporting it only on the top two priority queues
	 */
	if (queue < 0 || queue > 1)
		return -EINVAL;

	ring = adapter->tx_ring[queue];

	for (i = 0; i < IGC_MAX_SR_QUEUES; i++)
		if (adapter->tx_ring[i])
			cbs_status[i] = adapter->tx_ring[i]->cbs_enable;

	/* CBS should be enabled on the highest priority queue first in order
	 * for the CBS algorithm to operate as intended.
	 */
	if (enable) {
		if (queue == 1 && !cbs_status[0]) {
			netdev_err(netdev,
				   "Enabling CBS on queue1 before queue0\n");
			return -EINVAL;
		}
	} else {
		if (queue == 0 && cbs_status[1]) {
			netdev_err(netdev,
				   "Disabling CBS on queue0 before queue1\n");
			return -EINVAL;
		}
	}

	ring->cbs_enable = enable;
	ring->idleslope = idleslope;
	ring->sendslope = sendslope;
	ring->hicredit = hicredit;
	ring->locredit = locredit;

	return 0;
}

static int igc_tsn_enable_cbs(struct igc_adapter *adapter,
			      struct tc_cbs_qopt_offload *qopt)
{
	struct igc_hw *hw = &adapter->hw;
	int err;

	if (hw->mac.type != igc_i225)
		return -EOPNOTSUPP;

	if (qopt->queue < 0 || qopt->queue > 1)
		return -EINVAL;

	err = igc_save_cbs_params(adapter, qopt->queue, qopt->enable,
				  qopt->idleslope, qopt->sendslope,
				  qopt->hicredit, qopt->locredit);
	if (err)
		return err;

	return igc_tsn_offload_apply(adapter);
}

static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
			void *type_data)
{
@@ -5913,6 +5984,9 @@ static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
	case TC_SETUP_QDISC_ETF:
		return igc_tsn_enable_launchtime(adapter, type_data);

	case TC_SETUP_QDISC_CBS:
		return igc_tsn_enable_cbs(adapter, type_data);

	default:
		return -EOPNOTSUPP;
	}
@@ -6339,6 +6413,8 @@ static int igc_probe(struct pci_dev *pdev,

	igc_ptp_init(adapter);

	igc_tsn_clear_schedule(adapter);

	/* reset the hardware with the new settings */
	igc_reset(adapter);

+3 −0
Original line number Diff line number Diff line
@@ -236,6 +236,9 @@
#define IGC_ENDQT(_n)		(0x3334 + 0x4 * (_n))
#define IGC_DTXMXPKTSZ		0x355C

#define IGC_TQAVCC(_n)		(0x3004 + ((_n) * 0x40))
#define IGC_TQAVHC(_n)		(0x300C + ((_n) * 0x40))

/* System Time Registers */
#define IGC_SYSTIML	0x0B600  /* System time register Low - RO */
#define IGC_SYSTIMH	0x0B604  /* System time register High - RO */
+142 −32
Original line number Diff line number Diff line
@@ -18,8 +18,38 @@ static bool is_any_launchtime(struct igc_adapter *adapter)
	return false;
}

static bool is_cbs_enabled(struct igc_adapter *adapter)
{
	int i;

	for (i = 0; i < adapter->num_tx_queues; i++) {
		struct igc_ring *ring = adapter->tx_ring[i];

		if (ring->cbs_enable)
			return true;
	}

	return false;
}

static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)
{
	unsigned int new_flags = adapter->flags & ~IGC_FLAG_TSN_ANY_ENABLED;

	if (adapter->base_time)
		new_flags |= IGC_FLAG_TSN_QBV_ENABLED;

	if (is_any_launchtime(adapter))
		new_flags |= IGC_FLAG_TSN_QBV_ENABLED;

	if (is_cbs_enabled(adapter))
		new_flags |= IGC_FLAG_TSN_QAV_ENABLED;

	return new_flags;
}

/* Returns the TSN specific registers to their default values after
 * TSN offloading is disabled.
 * the adapter is reset.
 */
static int igc_tsn_disable_offload(struct igc_adapter *adapter)
{
@@ -27,11 +57,6 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)
	u32 tqavctrl;
	int i;

	if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED))
		return 0;

	adapter->cycle_time = 0;

	wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
	wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT);

@@ -41,12 +66,6 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter)
	wr32(IGC_TQAVCTRL, tqavctrl);

	for (i = 0; i < adapter->num_tx_queues; i++) {
		struct igc_ring *ring = adapter->tx_ring[i];

		ring->start_time = 0;
		ring->end_time = 0;
		ring->launchtime_enable = false;

		wr32(IGC_TXQCTL(i), 0);
		wr32(IGC_STQT(i), 0);
		wr32(IGC_ENDQT(i), NSEC_PER_SEC);
@@ -68,9 +87,6 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
	ktime_t base_time, systim;
	int i;

	if (adapter->flags & IGC_FLAG_TSN_QBV_ENABLED)
		return 0;

	cycle = adapter->cycle_time;
	base_time = adapter->base_time;

@@ -88,6 +104,8 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
	for (i = 0; i < adapter->num_tx_queues; i++) {
		struct igc_ring *ring = adapter->tx_ring[i];
		u32 txqctl = 0;
		u16 cbs_value;
		u32 tqavcc;

		wr32(IGC_STQT(i), ring->start_time);
		wr32(IGC_ENDQT(i), ring->end_time);
@@ -105,6 +123,90 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
		if (ring->launchtime_enable)
			txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT;

		/* Skip configuring CBS for Q2 and Q3 */
		if (i > 1)
			goto skip_cbs;

		if (ring->cbs_enable) {
			if (i == 0)
				txqctl |= IGC_TXQCTL_QAV_SEL_CBS0;
			else
				txqctl |= IGC_TXQCTL_QAV_SEL_CBS1;

			/* According to i225 datasheet section 7.5.2.7, we
			 * should set the 'idleSlope' field from TQAVCC
			 * register following the equation:
			 *
			 * value = link-speed   0x7736 * BW * 0.2
			 *         ---------- *  -----------------         (E1)
			 *          100Mbps            2.5
			 *
			 * Note that 'link-speed' is in Mbps.
			 *
			 * 'BW' is the percentage bandwidth out of full
			 * link speed which can be found with the
			 * following equation. Note that idleSlope here
			 * is the parameter from this function
			 * which is in kbps.
			 *
			 *     BW =     idleSlope
			 *          -----------------                      (E2)
			 *          link-speed * 1000
			 *
			 * That said, we can come up with a generic
			 * equation to calculate the value we should set
			 * it TQAVCC register by replacing 'BW' in E1 by E2.
			 * The resulting equation is:
			 *
			 * value = link-speed * 0x7736 * idleSlope * 0.2
			 *         -------------------------------------   (E3)
			 *             100 * 2.5 * link-speed * 1000
			 *
			 * 'link-speed' is present in both sides of the
			 * fraction so it is canceled out. The final
			 * equation is the following:
			 *
			 *     value = idleSlope * 61036
			 *             -----------------                   (E4)
			 *                  2500000
			 *
			 * NOTE: For i225, given the above, we can see
			 *       that idleslope is represented in
			 *       40.959433 kbps units by the value at
			 *       the TQAVCC register (2.5Gbps / 61036),
			 *       which reduces the granularity for
			 *       idleslope increments.
			 *
			 * In i225 controller, the sendSlope and loCredit
			 * parameters from CBS are not configurable
			 * by software so we don't do any
			 * 'controller configuration' in respect to
			 * these parameters.
			 */
			cbs_value = DIV_ROUND_UP_ULL(ring->idleslope
						     * 61036ULL, 2500000);

			tqavcc = rd32(IGC_TQAVCC(i));
			tqavcc &= ~IGC_TQAVCC_IDLESLOPE_MASK;
			tqavcc |= cbs_value | IGC_TQAVCC_KEEP_CREDITS;
			wr32(IGC_TQAVCC(i), tqavcc);

			wr32(IGC_TQAVHC(i),
			     0x80000000 + ring->hicredit * 0x7735);
		} else {
			/* Disable any CBS for the queue */
			txqctl &= ~(IGC_TXQCTL_QAV_SEL_MASK);

			/* Set idleSlope to zero. */
			tqavcc = rd32(IGC_TQAVCC(i));
			tqavcc &= ~(IGC_TQAVCC_IDLESLOPE_MASK |
				    IGC_TQAVCC_KEEP_CREDITS);
			wr32(IGC_TQAVCC(i), tqavcc);

			/* Set hiCredit to zero. */
			wr32(IGC_TQAVHC(i), 0);
		}
skip_cbs:
		wr32(IGC_TXQCTL(i), txqctl);
	}

@@ -125,33 +227,41 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
	wr32(IGC_BASET_H, baset_h);
	wr32(IGC_BASET_L, baset_l);

	adapter->flags |= IGC_FLAG_TSN_QBV_ENABLED;

	return 0;
}

int igc_tsn_offload_apply(struct igc_adapter *adapter)
int igc_tsn_reset(struct igc_adapter *adapter)
{
	bool is_any_enabled = adapter->base_time || is_any_launchtime(adapter);
	unsigned int new_flags;
	int err = 0;

	if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) && !is_any_enabled)
		return 0;
	new_flags = igc_tsn_new_flags(adapter);

	if (!is_any_enabled) {
		int err = igc_tsn_disable_offload(adapter);
	if (!(new_flags & IGC_FLAG_TSN_ANY_ENABLED))
		return igc_tsn_disable_offload(adapter);

	err = igc_tsn_enable_offload(adapter);
	if (err < 0)
		return err;

		/* The BASET registers aren't cleared when writing
		 * into them, force a reset if the interface is
		 * running.
		 */
		if (netif_running(adapter->netdev))
			schedule_work(&adapter->reset_task);
	adapter->flags = new_flags;

	return err;
}

int igc_tsn_offload_apply(struct igc_adapter *adapter)
{
	int err;

	if (netif_running(adapter->netdev)) {
		schedule_work(&adapter->reset_task);
		return 0;
	}

	return igc_tsn_enable_offload(adapter);
	err = igc_tsn_enable_offload(adapter);
	if (err < 0)
		return err;

	adapter->flags = igc_tsn_new_flags(adapter);
	return 0;
}
Loading