on a different CPU. If completion runs before sent_queue, DQL sees more bytes completed than queued and hits BUG_ON in dql_completed(). root@OpenWrt:/tmp# iperf3 -c 192.168.1.2 -u -b 1G Connecting to host 192.168.1.2, port 5201 [ 5] local 192.168.1.1 port 43382 connected to 192.168.1.2 port 5201 [ ID] Interval Transfer Bitrate Total Datagrams [ 5] 0.00-1.00 sec 23.7 MBytes 199 Mbits/sec 17176 [ 5] 1.00-2.00 sec 23.6 MBytes 198 Mbits/sec 17097 [ 5] 2.00-3.00 sec 23.6 MBytes 198 Mbits/sec 17075 [ 333.342658] Kernel bug detected[#1]: [ 333.346294] CPU: 1 UID: 0 PID: 2095 Comm: napi/qdma0_eth- Tainted: G W O 6.12.62 #0 [ 333.355189] Tainted: [W]=WARN, [O]=OOT_MODULE [ 333.359555] Hardware name: DASAN H660GM-A [ 333.363568] $ 0 : 00000000 810d3368 05e02a3c 00000000 [ 333.368842] $ 4 : 00000001 000005d2 00000000 81a45060 [ 333.374106] $ 8 : fffffff7 05e02a3c 00000000 00000000 [ 333.379371] $12 : 83ecdd64 00000000 00000000 00000000 [ 333.384635] $16 : 8487c980 00000005 00000086 80a9e080 [ 333.389900] $20 : 00000001 ffffffff 80a9e080 84e2f840 [ 333.395164] $24 : 00000002 8186c178 [ 333.400435] $28 : 83ecc000 83ecdd70 80a9e0c8 83e9b3b0 [ 333.405707] Hi : 00000086 [ 333.408608] Lo : 00000000 [ 333.411489] epc : 814d6044 dql_completed+0x2c/0x30c [ 333.416589] ra : 83e9b3b0 en75_poll_tx_complete+0x248/0x564 [econet_eth] [ 333.423587] Status: 11000003 KERNEL EXL IE [ 333.427808] Cause : 50800434 (ExcCode 0d) [ 333.431819] PrId : 0001992f (MIPS 1004Kc) [ 333.435920] Modules linked in: econet_eth(O) pppoe ppp_async nft_fib_inet nf_flow_table_inet pppox ppp_generic nft_reject_ipv6 nft_reject_ipv4 nft_reject_inet nft_reject nft_redir nft_quota nft_numgen nft_nat nft_masq nft_log nft_limit nft_hash nft_flow_offload nft_fib_ipv6 nft_fib_ipv4 nft_fib nft_ct nft_chain_nat nf_tables nf_nat nf_flow_table nf_conntrack mt76x2e(O) mt76x2_common(O) mt76x02_lib(O) mt7615e(O) mt7615_common(O) mt7603e(O) mt76_connac_lib(O) mt76(O) mac80211(O) cfg80211(O) slhc nfnetlink nf_reject_ipv6 nf_reject_ipv4 nf_log_syslog nf_defrag_ipv6 nf_defrag_ipv4 libcrc32c hwmon crc_ccitt compat(O) i2c_dev i2c_core sha512_generic seqiv sha3_generic jitterentropy_rng drbg hmac geniv rng cmac crc32c_generic [ 333.499944] Process napi/qdma0_eth- (pid: 2095, threadinfo=d7f163a2, task=98eab57d, tls=00000000) [ [ 333.508886] Stack : 83e9b168 80a46a40 80a9e0c8 83ecde57 11000000 83f0e8bc 80000005 00000086 5] [ 333.517509] 80a9e080 8487c940 00000005 83e9b3b0 00000000 00000000 80a9e0c8 00000040 [ 333.526106] 00000000 83ecde57 83ecde58 80a9e1a8 00000001 8187552c 810d321c 81620000 3.[ 333.534624] 814d0000 816178bc 80a9e0c8 00000040 83ecde58 83ecde57 83ecde58 81a40000 00[ 333.543138] 81a458e4 00000cff 81cb0000 81625b88 81cc8d80 00000040 00000001 81061c50 -4[ 333.551648] ... .0[ 333.554268] Call Trace: 0 [ 333.556875] [<814d6044>] dql_completed+0x2c/0x30c [ 333.561756] [<83e9b3b0>] en75_poll_tx_complete+0x248/0x564 [econet_eth] se[ 333.568536] [<81625b88>] __napi_poll+0x4c/0x22c c [ 333.573229] [<81625e3c>] napi_threaded_poll_loop+0xd4/0x218 2[ 333.578960] [<8162601c>] napi_threaded_poll+0x9c/0xc0 0.[ 333.584184] [<81061d64>] kthread+0x114/0x13c 5 [ 333.588613] [<81002038>] ret_from_kernel_thread+0x14/0x1c MB[ 333.594185] yte[ 333.595834] Code: 00808025 30c6ffff 0065202b <00040336> 8e0a0040 8e0b004c 00454021 006a3823 8e040048 s [ 333.605830] [ 333.607545] ---[ end trace 0000000000000000 ]--- 17[ 333.612273] Kernel panic - not syncing: Fatal exception in interrupt 2 [ 333.618818] Rebooting in 3 seconds.. Call netdev_tx_sent_queue() before en75_qdma_xmit() so that bytes are registered with DQL before hardware can complete them. If xmit fails, undo with netdev_tx_completed_queue().
EcoNet EN751221 Ethernet Driver
This is an ethernet driver for EcoNet EN751221 devices, it is likely to be easily ported to EN751627 and EN7528, but it is significantly different from the ethernet used in EN7523, EN7580, EN7581, and EN7583. For those you should use airoha_eth.
The EcoNet Ethernet system has 32 channels, each of which has 8 queues. WRR prioritization can be done between queues within a channel and (to some extent) between channels. This driver does no QoS at the moment, but does expose all 256 channel/queue combinations as queues to the kernel, ensuring that separate flows will generally get fair treatment.
Current status
- Loads and unloads correctly as long as reset controller is provided
- Sending and receiving is fast and seems to be correct
- NAPI based
- QDMA and GDM (port) subsystems are in separate sub-modules
- Stats collected
- Debugfs introspection
- "Kernel Quality" - not much needed to be considered for upstreaming
TODO
- Short term:
- Implement DSA for the integrated MT7530 switch
- Send upstream to Linux for reviews
- Medium term:
- Implement FlowTable offloading
- Implement QoS flow grouping by sender/recipient to protect users from each other
- Integrate FlowTable offloading with QoS channel selection
- Implement jumbo frames over 2KB (requires shutdown/restart of QDMA on change of MTU, to increase buffer sizes)
- EN751627 + EN7528 support
File structure
econet_eth.c- The main driver and entrypoint.econet_qdma.c- Controls only the QDMA engine, is oblivious to the rest of the ethernet system. Receives and send packets.econet_port.c- Controls the GDM (LAN or WAN) port, registers the net_device with the kernel. Has a reference to the QDMA in order to send packets.econet_eth_debug.c- Provides debugfs introspection.econet_eth.h- Main internal header file.qdma_desc.h- Header file with packet descriptor for communicating with the QDMA engine.qdma_regs.h- Struct representation of MMIO registers of QDMA enginegdm_regs.h- Struct representation of MMIO registers of GDM port control
How to use
First, you must edit your dts / dtsi file and add an entry for the
ethernet device.
Then you need to build the kernel module using ./build.sh. The build.sh
script expects that next to your econet_eth directory, there is ../openwrt,
an OpenWrt tree that has been compiled for the EcoNet device. If your OpenWrt
is in a different location then you'll need to edit it.
DeviceTree Entry
First, you should have econet,en751221-scu for being able to reset the
ethernet when loading and unloading the driver, otherwise it will only load
once per reboot. See: https://github.com/openwrt/openwrt/pull/21545
chip_scu: syscon@1fa20000 {
compatible = "econet,en751221-chip-scu", "syscon";
reg = <0x1fa20000 0x388>;
};
scuclk: clock-controller@1fb00000 {
compatible = "econet,en751221-scu", "syscon";
reg = <0x1fb00000 0x970>;
#clock-cells = <1>;
#reset-cells = <1>;
};
Then add this entry to your DT:
ethernet: ethernet@1fb50000 {
compatible = "econet,en751221-eth";
reg = <0x1fb50000 0x10000>;
// If you didn't include the system controller, remove
// these resets. You will get a warning on load and you
// won't be able to load the driver more than once per reboot.
resets = <&scuclk EN751221_FE_RST>,
<&scuclk EN751221_FE_QDMA1_RST>,
<&scuclk EN751221_FE_QDMA2_RST>,
<&scuclk EN751221_GSW_RST>,
<&scuclk EN751221_XPON_MAC_RST>,
<&scuclk EN751221_XPON_PHY_RST>;
reset-names = "fe", "qdma0", "qdma1", "gsw",
"xpon-mac", "xpon-phy";
#address-cells = <1>;
#size-cells = <0>;
interrupt-parent = <&intc>;
interrupts = <21>, <22>;
gmac0: mac@0 {
compatible = "econet,eth-mac";
reg = <0>;
phy-mode = "trgmii";
fixed-link {
speed = <1000>;
full-duplex;
pause;
};
};
gmac1: mac@1 {
compatible = "econet,eth-mac";
reg = <1>;
status = "disable";
phy-mode = "rgmii-rxid";
};
mdio: mdio-bus {
#address-cells = <1>;
#size-cells = <0>;
switch0: switch@1f {
compatible = "mediatek,mt7530";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x1f>;
mediatek,mcm;
//resets = <&rstctrl 2>;
reset-names = "mcm";
ports {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
port@0 {
status = "disabled";
reg = <0>;
label = "lan0";
};
port@1 {
status = "disabled";
reg = <1>;
label = "lan1";
};
port@2 {
status = "disabled";
reg = <2>;
label = "lan2";
};
port@3 {
status = "disabled";
reg = <3>;
label = "lan3";
};
port@4 {
/* status = "disabled"; */
reg = <4>;
label = "lan4";
};
port@6 {
reg = <6>;
label = "cpu";
//ethernet = <&gmac0>;
phy-mode = "trgmii";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
};
};
License and Credits
All of this code is licensed GPL-2.0-only.
It is based on the work here https://github.com/gchmiel/en7512_kernel5 which is in turn based on the the Mediatek MT7621 driver on upstream.