Skip to content

Commit ec50a9d

Browse files
vcgomesJeff Kirsher
authored andcommitted
igc: Add support for taprio offloading
Adds support for translating taprio schedules into i225 cycles. This will allow schedules to run in the hardware, making the schedules enforcement more precise and saving CPU time. Right now, the only simple schedules are allowed, complex schedules are rejected. "simple" in this context are schedules that each HW queue is opened and closed only once in each cycle. Changing schedules is still not supported as well. Signed-off-by: Vinicius Costa Gomes <[email protected]> Reviewed-by: Andre Guedes <[email protected]> Tested-by: Aaron Brown <[email protected]> Signed-off-by: Jeff Kirsher <[email protected]>
1 parent 34428df commit ec50a9d

File tree

7 files changed

+294
-1
lines changed

7 files changed

+294
-1
lines changed

drivers/net/ethernet/intel/igc/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
obj-$(CONFIG_IGC) += igc.o
99

1010
igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \
11-
igc_ethtool.o igc_ptp.o igc_dump.o
11+
igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o

drivers/net/ethernet/intel/igc/igc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ extern char igc_driver_version[];
7070
#define IGC_FLAG_HAS_MSIX BIT(13)
7171
#define IGC_FLAG_VLAN_PROMISC BIT(15)
7272
#define IGC_FLAG_RX_LEGACY BIT(16)
73+
#define IGC_FLAG_TSN_QBV_ENABLED BIT(17)
7374

7475
#define IGC_FLAG_RSS_FIELD_IPV4_UDP BIT(6)
7576
#define IGC_FLAG_RSS_FIELD_IPV6_UDP BIT(7)
@@ -287,6 +288,9 @@ struct igc_ring {
287288
u8 reg_idx; /* physical index of the ring */
288289
bool launchtime_enable; /* true if LaunchTime is enabled */
289290

291+
u32 start_time;
292+
u32 end_time;
293+
290294
/* everything past this point are written often */
291295
u16 next_to_clean;
292296
u16 next_to_use;
@@ -421,6 +425,9 @@ struct igc_adapter {
421425
u32 max_frame_size;
422426
u32 min_frame_size;
423427

428+
ktime_t base_time;
429+
ktime_t cycle_time;
430+
424431
/* OS defined structs */
425432
struct pci_dev *pdev;
426433
/* lock for statistics */

drivers/net/ethernet/intel/igc/igc_defines.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,11 @@
377377
#define I225_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */
378378
#define IGC_RXPBS_CFG_TS_EN 0x80000000 /* Timestamp in Rx buffer */
379379

380+
#define IGC_TXPBSIZE_TSN 0x04145145 /* 5k bytes buffer for each queue */
381+
382+
#define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA packet size */
383+
#define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */
384+
380385
/* Time Sync Interrupt Causes */
381386
#define IGC_TSICR_SYS_WRAP BIT(0) /* SYSTIM Wrap around. */
382387
#define IGC_TSICR_TXTS BIT(1) /* Transmit Timestamp. */
@@ -431,6 +436,13 @@
431436
#define IGC_TSYNCTXCTL_START_SYNC 0x80000000 /* initiate sync */
432437
#define IGC_TSYNCTXCTL_TXSYNSIG 0x00000020 /* Sample TX tstamp in PHY sop */
433438

439+
/* Transmit Scheduling */
440+
#define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001
441+
#define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008
442+
443+
#define IGC_TXQCTL_STRICT_CYCLE 0x00000002
444+
#define IGC_TXQCTL_STRICT_END 0x00000004
445+
434446
/* Receive Checksum Control */
435447
#define IGC_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable */
436448
#define IGC_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */

drivers/net/ethernet/intel/igc/igc_main.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
#include <linux/udp.h>
1010
#include <linux/ip.h>
1111
#include <linux/pm_runtime.h>
12+
#include <net/pkt_sched.h>
1213

1314
#include <net/ipv6.h>
1415

1516
#include "igc.h"
1617
#include "igc_hw.h"
18+
#include "igc_tsn.h"
1719

1820
#define DRV_VERSION "0.0.1-k"
1921
#define DRV_SUMMARY "Intel(R) 2.5G Ethernet Linux Driver"
@@ -106,6 +108,9 @@ void igc_reset(struct igc_adapter *adapter)
106108
/* Re-enable PTP, where applicable. */
107109
igc_ptp_reset(adapter);
108110

111+
/* Re-enable TSN offloading, where applicable. */
112+
igc_tsn_offload_apply(adapter);
113+
109114
igc_get_phy_info(hw);
110115
}
111116

@@ -4491,6 +4496,113 @@ static int igc_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
44914496
}
44924497
}
44934498

4499+
static bool validate_schedule(const struct tc_taprio_qopt_offload *qopt)
4500+
{
4501+
int queue_uses[IGC_MAX_TX_QUEUES] = { };
4502+
size_t n;
4503+
4504+
if (qopt->cycle_time_extension)
4505+
return false;
4506+
4507+
for (n = 0; n < qopt->num_entries; n++) {
4508+
const struct tc_taprio_sched_entry *e;
4509+
int i;
4510+
4511+
e = &qopt->entries[n];
4512+
4513+
/* i225 only supports "global" frame preemption
4514+
* settings.
4515+
*/
4516+
if (e->command != TC_TAPRIO_CMD_SET_GATES)
4517+
return false;
4518+
4519+
for (i = 0; i < IGC_MAX_TX_QUEUES; i++) {
4520+
if (e->gate_mask & BIT(i))
4521+
queue_uses[i]++;
4522+
4523+
if (queue_uses[i] > 1)
4524+
return false;
4525+
}
4526+
}
4527+
4528+
return true;
4529+
}
4530+
4531+
static int igc_save_qbv_schedule(struct igc_adapter *adapter,
4532+
struct tc_taprio_qopt_offload *qopt)
4533+
{
4534+
u32 start_time = 0, end_time = 0;
4535+
size_t n;
4536+
4537+
if (!qopt->enable) {
4538+
adapter->base_time = 0;
4539+
return 0;
4540+
}
4541+
4542+
if (adapter->base_time)
4543+
return -EALREADY;
4544+
4545+
if (!validate_schedule(qopt))
4546+
return -EINVAL;
4547+
4548+
adapter->cycle_time = qopt->cycle_time;
4549+
adapter->base_time = qopt->base_time;
4550+
4551+
/* FIXME: be a little smarter about cases when the gate for a
4552+
* queue stays open for more than one entry.
4553+
*/
4554+
for (n = 0; n < qopt->num_entries; n++) {
4555+
struct tc_taprio_sched_entry *e = &qopt->entries[n];
4556+
int i;
4557+
4558+
end_time += e->interval;
4559+
4560+
for (i = 0; i < IGC_MAX_TX_QUEUES; i++) {
4561+
struct igc_ring *ring = adapter->tx_ring[i];
4562+
4563+
if (!(e->gate_mask & BIT(i)))
4564+
continue;
4565+
4566+
ring->start_time = start_time;
4567+
ring->end_time = end_time;
4568+
}
4569+
4570+
start_time += e->interval;
4571+
}
4572+
4573+
return 0;
4574+
}
4575+
4576+
static int igc_tsn_enable_qbv_scheduling(struct igc_adapter *adapter,
4577+
struct tc_taprio_qopt_offload *qopt)
4578+
{
4579+
struct igc_hw *hw = &adapter->hw;
4580+
int err;
4581+
4582+
if (hw->mac.type != igc_i225)
4583+
return -EOPNOTSUPP;
4584+
4585+
err = igc_save_qbv_schedule(adapter, qopt);
4586+
if (err)
4587+
return err;
4588+
4589+
return igc_tsn_offload_apply(adapter);
4590+
}
4591+
4592+
static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
4593+
void *type_data)
4594+
{
4595+
struct igc_adapter *adapter = netdev_priv(dev);
4596+
4597+
switch (type) {
4598+
case TC_SETUP_QDISC_TAPRIO:
4599+
return igc_tsn_enable_qbv_scheduling(adapter, type_data);
4600+
4601+
default:
4602+
return -EOPNOTSUPP;
4603+
}
4604+
}
4605+
44944606
static const struct net_device_ops igc_netdev_ops = {
44954607
.ndo_open = igc_open,
44964608
.ndo_stop = igc_close,
@@ -4503,6 +4615,7 @@ static const struct net_device_ops igc_netdev_ops = {
45034615
.ndo_set_features = igc_set_features,
45044616
.ndo_features_check = igc_features_check,
45054617
.ndo_do_ioctl = igc_ioctl,
4618+
.ndo_setup_tc = igc_setup_tc,
45064619
};
45074620

45084621
/* PCIe configuration access */

drivers/net/ethernet/intel/igc/igc_regs.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,18 @@
231231

232232
#define IGC_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */
233233

234+
/* Transmit Scheduling Registers */
235+
#define IGC_TQAVCTRL 0x3570
236+
#define IGC_TXQCTL(_n) (0x3344 + 0x4 * (_n))
237+
#define IGC_BASET_L 0x3314
238+
#define IGC_BASET_H 0x3318
239+
#define IGC_QBVCYCLET 0x331C
240+
#define IGC_QBVCYCLET_S 0x3320
241+
242+
#define IGC_STQT(_n) (0x3324 + 0x4 * (_n))
243+
#define IGC_ENDQT(_n) (0x3334 + 0x4 * (_n))
244+
#define IGC_DTXMXPKTSZ 0x355C
245+
234246
/* System Time Registers */
235247
#define IGC_SYSTIML 0x0B600 /* System time register Low - RO */
236248
#define IGC_SYSTIMH 0x0B604 /* System time register High - RO */
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2019 Intel Corporation */
3+
4+
#include "igc.h"
5+
#include "igc_tsn.h"
6+
7+
/* Returns the TSN specific registers to their default values after
8+
* TSN offloading is disabled.
9+
*/
10+
static int igc_tsn_disable_offload(struct igc_adapter *adapter)
11+
{
12+
struct igc_hw *hw = &adapter->hw;
13+
u32 tqavctrl;
14+
int i;
15+
16+
if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED))
17+
return 0;
18+
19+
adapter->cycle_time = 0;
20+
21+
wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
22+
wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT);
23+
24+
tqavctrl = rd32(IGC_TQAVCTRL);
25+
tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN |
26+
IGC_TQAVCTRL_ENHANCED_QAV);
27+
wr32(IGC_TQAVCTRL, tqavctrl);
28+
29+
for (i = 0; i < adapter->num_tx_queues; i++) {
30+
struct igc_ring *ring = adapter->tx_ring[i];
31+
32+
ring->start_time = 0;
33+
ring->end_time = 0;
34+
ring->launchtime_enable = false;
35+
36+
wr32(IGC_TXQCTL(i), 0);
37+
wr32(IGC_STQT(i), 0);
38+
wr32(IGC_ENDQT(i), NSEC_PER_SEC);
39+
}
40+
41+
wr32(IGC_QBVCYCLET_S, NSEC_PER_SEC);
42+
wr32(IGC_QBVCYCLET, NSEC_PER_SEC);
43+
44+
adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED;
45+
46+
return 0;
47+
}
48+
49+
static int igc_tsn_enable_offload(struct igc_adapter *adapter)
50+
{
51+
struct igc_hw *hw = &adapter->hw;
52+
u32 tqavctrl, baset_l, baset_h;
53+
u32 sec, nsec, cycle;
54+
ktime_t base_time, systim;
55+
int i;
56+
57+
if (adapter->flags & IGC_FLAG_TSN_QBV_ENABLED)
58+
return 0;
59+
60+
cycle = adapter->cycle_time;
61+
base_time = adapter->base_time;
62+
63+
wr32(IGC_TSAUXC, 0);
64+
wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN);
65+
wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN);
66+
67+
tqavctrl = rd32(IGC_TQAVCTRL);
68+
tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
69+
wr32(IGC_TQAVCTRL, tqavctrl);
70+
71+
wr32(IGC_QBVCYCLET_S, cycle);
72+
wr32(IGC_QBVCYCLET, cycle);
73+
74+
for (i = 0; i < adapter->num_tx_queues; i++) {
75+
struct igc_ring *ring = adapter->tx_ring[i];
76+
u32 txqctl = 0;
77+
78+
wr32(IGC_STQT(i), ring->start_time);
79+
wr32(IGC_ENDQT(i), ring->end_time);
80+
81+
if (adapter->base_time) {
82+
/* If we have a base_time we are in "taprio"
83+
* mode and we need to be strict about the
84+
* cycles: only transmit a packet if it can be
85+
* completed during that cycle.
86+
*/
87+
txqctl |= IGC_TXQCTL_STRICT_CYCLE |
88+
IGC_TXQCTL_STRICT_END;
89+
}
90+
91+
wr32(IGC_TXQCTL(i), txqctl);
92+
}
93+
94+
nsec = rd32(IGC_SYSTIML);
95+
sec = rd32(IGC_SYSTIMH);
96+
97+
systim = ktime_set(sec, nsec);
98+
99+
if (ktime_compare(systim, base_time) > 0) {
100+
s64 n;
101+
102+
n = div64_s64(ktime_sub_ns(systim, base_time), cycle);
103+
base_time = ktime_add_ns(base_time, (n + 1) * cycle);
104+
}
105+
106+
baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l);
107+
108+
wr32(IGC_BASET_H, baset_h);
109+
wr32(IGC_BASET_L, baset_l);
110+
111+
adapter->flags |= IGC_FLAG_TSN_QBV_ENABLED;
112+
113+
return 0;
114+
}
115+
116+
int igc_tsn_offload_apply(struct igc_adapter *adapter)
117+
{
118+
bool is_any_enabled = adapter->base_time;
119+
120+
if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) && !is_any_enabled)
121+
return 0;
122+
123+
if (!is_any_enabled) {
124+
int err = igc_tsn_disable_offload(adapter);
125+
126+
if (err < 0)
127+
return err;
128+
129+
/* The BASET registers aren't cleared when writing
130+
* into them, force a reset if the interface is
131+
* running.
132+
*/
133+
if (netif_running(adapter->netdev))
134+
schedule_work(&adapter->reset_task);
135+
136+
return 0;
137+
}
138+
139+
return igc_tsn_enable_offload(adapter);
140+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/* Copyright (c) 2020 Intel Corporation */
3+
4+
#ifndef _IGC_TSN_H_
5+
#define _IGC_TSN_H_
6+
7+
int igc_tsn_offload_apply(struct igc_adapter *adapter);
8+
9+
#endif /* _IGC_BASE_H */

0 commit comments

Comments
 (0)