[Intel-wired-lan] [next-queue PATCH v3 1/2] igc: Add support for taprio offloading
Brown, Aaron F
aaron.f.brown at intel.com
Thu Feb 27 03:59:00 UTC 2020
> From: Intel-wired-lan <intel-wired-lan-bounces at osuosl.org> On Behalf Of
> Vinicius Costa Gomes
> Sent: Friday, February 14, 2020 3:52 PM
> To: intel-wired-lan at lists.osuosl.org
> Subject: [Intel-wired-lan] [next-queue PATCH v3 1/2] 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 <vinicius.gomes at intel.com>
> ---
> drivers/net/ethernet/intel/igc/Makefile | 2 +-
> drivers/net/ethernet/intel/igc/igc.h | 7 +
> drivers/net/ethernet/intel/igc/igc_defines.h | 12 ++
> drivers/net/ethernet/intel/igc/igc_main.c | 113 +++++++++++++++
> drivers/net/ethernet/intel/igc/igc_regs.h | 12 ++
> drivers/net/ethernet/intel/igc/igc_tsn.c | 140 +++++++++++++++++++
> drivers/net/ethernet/intel/igc/igc_tsn.h | 9 ++
> 7 files changed, 294 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/intel/igc/igc_tsn.c
> create mode 100644 drivers/net/ethernet/intel/igc/igc_tsn.h
I'm using the TSN Scheduled TX Tools from https://gist.github.com/jeez/bd3afeff081ba64a695008dd8215866f, and the process (from both the README.etf and README.taprio) seems to work fine with an i210 (igb adapter) but when I try to use the same process with an i225 (igc based adapter) for the taprio session the system panics, freezing the system and leaving me a panic RIP frozen on my screen, but no hint of that panic got saved to disk (crash dump or log.) I tried to get a capture via a serial console, but unfortunately it freezes to fast to get the trace out and instead of a full trace I am left with a single line from the panic:
[ 718.732753] BUG: kernel NULL pointer dereference, address: 0000000000000008
I am able to get a jpg of the frozen screen which I can make available, though the start of the trace has scrolled off the top of the screen.
>
> diff --git a/drivers/net/ethernet/intel/igc/Makefile
> b/drivers/net/ethernet/intel/igc/Makefile
> index e3c164c12e10..3652f211f351 100644
> --- a/drivers/net/ethernet/intel/igc/Makefile
> +++ b/drivers/net/ethernet/intel/igc/Makefile
> @@ -8,4 +8,4 @@
> obj-$(CONFIG_IGC) += igc.o
>
> igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \
> -igc_ethtool.o igc_ptp.o igc_dump.o
> +igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o
> diff --git a/drivers/net/ethernet/intel/igc/igc.h
> b/drivers/net/ethernet/intel/igc/igc.h
> index 0014828eec46..4c40dc8f276c 100644
> --- a/drivers/net/ethernet/intel/igc/igc.h
> +++ b/drivers/net/ethernet/intel/igc/igc.h
> @@ -70,6 +70,7 @@ extern char igc_driver_version[];
> #define IGC_FLAG_HAS_MSIX BIT(13)
> #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_RSS_FIELD_IPV4_UDP BIT(6)
> #define IGC_FLAG_RSS_FIELD_IPV6_UDP BIT(7)
> @@ -287,6 +288,9 @@ struct igc_ring {
> u8 reg_idx; /* physical index of the ring */
> bool launchtime_enable; /* true if LaunchTime is
> enabled */
>
> + u32 start_time;
> + u32 end_time;
> +
> /* everything past this point are written often */
> u16 next_to_clean;
> u16 next_to_use;
> @@ -421,6 +425,9 @@ struct igc_adapter {
> u32 max_frame_size;
> u32 min_frame_size;
>
> + ktime_t base_time;
> + ktime_t cycle_time;
> +
> /* OS defined structs */
> struct pci_dev *pdev;
> /* lock for statistics */
> diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h
> b/drivers/net/ethernet/intel/igc/igc_defines.h
> index 044c0f8d4c16..3077d0a69b04 100644
> --- a/drivers/net/ethernet/intel/igc/igc_defines.h
> +++ b/drivers/net/ethernet/intel/igc/igc_defines.h
> @@ -377,6 +377,11 @@
> #define I225_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */
> #define IGC_RXPBS_CFG_TS_EN 0x80000000 /* Timestamp in Rx
> buffer */
>
> +#define IGC_TXPBSIZE_TSN 0x04145145 /* 5k bytes buffer for each
> queue */
> +
> +#define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA
> packet size */
> +#define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */
> +
> /* Time Sync Interrupt Causes */
> #define IGC_TSICR_SYS_WRAP BIT(0) /* SYSTIM Wrap around. */
> #define IGC_TSICR_TXTS BIT(1) /* Transmit Timestamp. */
> @@ -431,6 +436,13 @@
> #define IGC_TSYNCTXCTL_START_SYNC 0x80000000 /*
> initiate sync */
> #define IGC_TSYNCTXCTL_TXSYNSIG 0x00000020 /*
> Sample TX tstamp in PHY sop */
>
> +/* Transmit Scheduling */
> +#define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001
> +#define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008
> +
> +#define IGC_TXQCTL_STRICT_CYCLE 0x00000002
> +#define IGC_TXQCTL_STRICT_END 0x00000004
> +
> /* Receive Checksum Control */
> #define IGC_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable
> */
> #define IGC_RXCSUM_PCSD 0x00002000 /* packet checksum
> disabled */
> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c
> b/drivers/net/ethernet/intel/igc/igc_main.c
> index 3407af11dff8..5fb52768de18 100644
> --- a/drivers/net/ethernet/intel/igc/igc_main.c
> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> @@ -9,11 +9,13 @@
> #include <linux/udp.h>
> #include <linux/ip.h>
> #include <linux/pm_runtime.h>
> +#include <net/pkt_sched.h>
>
> #include <net/ipv6.h>
>
> #include "igc.h"
> #include "igc_hw.h"
> +#include "igc_tsn.h"
>
> #define DRV_VERSION "0.0.1-k"
> #define DRV_SUMMARY "Intel(R) 2.5G Ethernet Linux Driver"
> @@ -106,6 +108,9 @@ void igc_reset(struct igc_adapter *adapter)
> /* Re-enable PTP, where applicable. */
> igc_ptp_reset(adapter);
>
> + /* Re-enable TSN offloading, where applicable. */
> + igc_tsn_offload_apply(adapter);
> +
> igc_get_phy_info(hw);
> }
>
> @@ -4492,6 +4497,113 @@ static int igc_ioctl(struct net_device *netdev,
> struct ifreq *ifr, int cmd)
> }
> }
>
> +static bool validate_schedule(const struct tc_taprio_qopt_offload *qopt)
> +{
> + int queue_uses[IGC_MAX_TX_QUEUES] = { };
> + size_t n;
> +
> + if (qopt->cycle_time_extension)
> + return false;
> +
> + for (n = 0; n < qopt->num_entries; n++) {
> + const struct tc_taprio_sched_entry *e;
> + int i;
> +
> + e = &qopt->entries[n];
> +
> + /* i225 only supports "global" frame preemption
> + * settings.
> + */
> + if (e->command != TC_TAPRIO_CMD_SET_GATES)
> + return false;
> +
> + for (i = 0; i < IGC_MAX_TX_QUEUES; i++) {
> + if (e->gate_mask & BIT(i))
> + queue_uses[i]++;
> +
> + if (queue_uses[i] > 1)
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
> +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 (adapter->base_time)
> + return -EALREADY;
> +
> + if (!validate_schedule(qopt))
> + return -EINVAL;
> +
> + adapter->cycle_time = qopt->cycle_time;
> + adapter->base_time = qopt->base_time;
> +
> + /* FIXME: be a little smarter about cases when the gate for a
> + * queue stays open for more than one entry.
> + */
> + for (n = 0; n < qopt->num_entries; n++) {
> + struct tc_taprio_sched_entry *e = &qopt->entries[n];
> + int i;
> +
> + end_time += e->interval;
> +
> + for (i = 0; i < IGC_MAX_TX_QUEUES; i++) {
> + struct igc_ring *ring = adapter->tx_ring[i];
> +
> + if (!(e->gate_mask & BIT(i)))
> + continue;
> +
> + ring->start_time = start_time;
> + ring->end_time = end_time;
> + }
> +
> + start_time += e->interval;
> + }
> +
> + return 0;
> +}
> +
> +static int igc_tsn_enable_qbv_scheduling(struct igc_adapter *adapter,
> + struct tc_taprio_qopt_offload *qopt)
> +{
> + struct igc_hw *hw = &adapter->hw;
> + int err;
> +
> + if (hw->mac.type != igc_i225)
> + return -EOPNOTSUPP;
> +
> + err = igc_save_qbv_schedule(adapter, qopt);
> + 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)
> +{
> + struct igc_adapter *adapter = netdev_priv(dev);
> +
> + switch (type) {
> + case TC_SETUP_QDISC_TAPRIO:
> + return igc_tsn_enable_qbv_scheduling(adapter, type_data);
> +
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> static const struct net_device_ops igc_netdev_ops = {
> .ndo_open = igc_open,
> .ndo_stop = igc_close,
> @@ -4504,6 +4616,7 @@ static const struct net_device_ops igc_netdev_ops
> = {
> .ndo_set_features = igc_set_features,
> .ndo_features_check = igc_features_check,
> .ndo_do_ioctl = igc_ioctl,
> + .ndo_setup_tc = igc_setup_tc,
> };
>
> /* PCIe configuration access */
> diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h
> b/drivers/net/ethernet/intel/igc/igc_regs.h
> index d4af53a80f11..96dee3c1a5f7 100644
> --- a/drivers/net/ethernet/intel/igc/igc_regs.h
> +++ b/drivers/net/ethernet/intel/igc/igc_regs.h
> @@ -231,6 +231,18 @@
>
> #define IGC_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */
>
> +/* Transmit Scheduling Registers */
> +#define IGC_TQAVCTRL 0x3570
> +#define IGC_TXQCTL(_n) (0x3344 + 0x4 * (_n))
> +#define IGC_BASET_L 0x3314
> +#define IGC_BASET_H 0x3318
> +#define IGC_QBVCYCLET 0x331C
> +#define IGC_QBVCYCLET_S 0x3320
> +
> +#define IGC_STQT(_n) (0x3324 + 0x4 * (_n))
> +#define IGC_ENDQT(_n) (0x3334 + 0x4 * (_n))
> +#define IGC_DTXMXPKTSZ 0x355C
> +
> /* System Time Registers */
> #define IGC_SYSTIML 0x0B600 /* System time register Low - RO */
> #define IGC_SYSTIMH 0x0B604 /* System time register High - RO */
> diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c
> b/drivers/net/ethernet/intel/igc/igc_tsn.c
> new file mode 100644
> index 000000000000..257fe970afe8
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
> @@ -0,0 +1,140 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2019 Intel Corporation */
> +
> +#include "igc.h"
> +#include "igc_tsn.h"
> +
> +/* Returns the TSN specific registers to their default values after
> + * TSN offloading is disabled.
> + */
> +static int igc_tsn_disable_offload(struct igc_adapter *adapter)
> +{
> + struct igc_hw *hw = &adapter->hw;
> + 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);
> +
> + tqavctrl = rd32(IGC_TQAVCTRL);
> + tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN |
> + IGC_TQAVCTRL_ENHANCED_QAV);
> + 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);
> + }
> +
> + wr32(IGC_QBVCYCLET_S, NSEC_PER_SEC);
> + wr32(IGC_QBVCYCLET, NSEC_PER_SEC);
> +
> + adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED;
> +
> + return 0;
> +}
> +
> +static int igc_tsn_enable_offload(struct igc_adapter *adapter)
> +{
> + struct igc_hw *hw = &adapter->hw;
> + u32 tqavctrl, baset_l, baset_h;
> + u32 sec, nsec, cycle;
> + 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;
> +
> + wr32(IGC_TSAUXC, 0);
> + wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN);
> + wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN);
> +
> + tqavctrl = rd32(IGC_TQAVCTRL);
> + tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN |
> IGC_TQAVCTRL_ENHANCED_QAV;
> + wr32(IGC_TQAVCTRL, tqavctrl);
> +
> + wr32(IGC_QBVCYCLET_S, cycle);
> + wr32(IGC_QBVCYCLET, cycle);
> +
> + for (i = 0; i < adapter->num_tx_queues; i++) {
> + struct igc_ring *ring = adapter->tx_ring[i];
> + u32 txqctl = 0;
> +
> + wr32(IGC_STQT(i), ring->start_time);
> + wr32(IGC_ENDQT(i), ring->end_time);
> +
> + if (adapter->base_time) {
> + /* If we have a base_time we are in "taprio"
> + * mode and we need to be strict about the
> + * cycles: only transmit a packet if it can be
> + * completed during that cycle.
> + */
> + txqctl |= IGC_TXQCTL_STRICT_CYCLE |
> + IGC_TXQCTL_STRICT_END;
> + }
> +
> + wr32(IGC_TXQCTL(i), txqctl);
> + }
> +
> + nsec = rd32(IGC_SYSTIML);
> + sec = rd32(IGC_SYSTIMH);
> +
> + systim = ktime_set(sec, nsec);
> +
> + if (ktime_compare(systim, base_time) > 0) {
> + s64 n;
> +
> + n = div64_s64(ktime_sub_ns(systim, base_time), cycle);
> + base_time = ktime_add_ns(base_time, (n + 1) * cycle);
> + }
> +
> + baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l);
> +
> + 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)
> +{
> + bool is_any_enabled = adapter->base_time;
> +
> + if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) &&
> !is_any_enabled)
> + return 0;
> +
> + if (!is_any_enabled) {
> + int err = igc_tsn_disable_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);
> +
> + return 0;
> + }
> +
> + return igc_tsn_enable_offload(adapter);
> +}
> diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.h
> b/drivers/net/ethernet/intel/igc/igc_tsn.h
> new file mode 100644
> index 000000000000..f76bc86ddccd
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/igc/igc_tsn.h
> @@ -0,0 +1,9 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2020 Intel Corporation */
> +
> +#ifndef _IGC_TSN_H_
> +#define _IGC_TSN_H_
> +
> +int igc_tsn_offload_apply(struct igc_adapter *adapter);
> +
> +#endif /* _IGC_BASE_H */
> --
> 2.25.0
>
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan
More information about the Intel-wired-lan
mailing list