The sysupgrade image of the Ctera C200 V1 differs between builds in the
user and group names of the tar members:
--rw-r--r-- 0 buildbot (1000) buildbot (1000) 175 ... header
+-rw-r--r-- 0 rebuilder (1000) rebuilder (1000) 175 ... header
ctera-firmware sets the mtime of the members, but not their owner, so
the name of the build user ends up in the image. mvebu fixed the same
recipe in commit 0d42a1258f ("mvebu: improve reproducibility of ctera
firmware"); store the members as root here too.
Link: https://github.com/openwrt/openwrt/pull/25525
Signed-off-by: Paul Spooren <mail@aparcar.org>
The neighbour notifier drops every update that is not NUD_VALID, so once
the kernel has given up on a gateway - its entry failed to resolve, or
was deleted - the routes behind it keep forwarding in hardware to the
address it last answered from. The sender is never told the gateway is
gone, and the kernel never sees the traffic that would let it notice.
Queue those updates as well, and turn every route the hardware holds
for that gateway into TRAP2CPU, with the TTL bits clear like a route
trapped for want of a port. The CPU then resolves the gateway again, and
the update that brings a valid neighbour programs the route as before,
or it reports the gateway unreachable. A route never written stays as
it is. mlxsw does the same: it stops offloading a next hop whose
neighbour is not valid, and a route left without one traps.
An entry taken out of the table - flushed with its device, or collected
while stale - is marked dead and can keep a NUD_VALID state, so a dead
neighbour counts as not valid too, as it does for mlxsw. The state, the
dead flag and the address are read under the neighbour's lock. On a
table large enough for the periodic collection to run (gc_thresh1, 128
entries by default), the entry of a gateway that only the hardware
sends to goes idle and is collected after gc_stale_time; its routes then
pass through the CPU until the next packet resolves it again.
The RTL839x routes through PIE rules and keeps them as they are, as it
does for a next hop without a port. Its notifier now queues the invalid
updates too, which find nothing to write.
Tested on an RTL9303 with a /24 and a /32 through one gateway that stops
answering ARP until the switch's kernel marks it FAILED. Without this
change both rows stay at forward and the gateway still receives every
echo request; with it both rows trap, none reaches the gateway, and the
sender gets Destination Host Unreachable. Once the gateway answers
again, or after an ip neigh del and the traffic that follows, both rows
are back at forward. With the collection thresholds lowered until the
gateway's stale entry is collected, both rows stayed at forward before
the dead flag was tested and trap with it, back at forward with the
next traffic; a down and up of the gateway's interface, which flushes
its neighbours under the table lock, runs clean. The RTL839x was not
tested.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25524
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
otto_l3_nexthop_update() programs every route behind a gateway in one
loop body that has grown to a hundred lines. Move that body into
otto_l3_route_update_hw(), which writes one route to the hardware, and
leave the update to find the routes and call it.
The body moves as it was. A route it gives up on now returns instead of
continuing the loop, and the gateway it prints is the route's own, which
the loop has just compared equal to the one it was given. No functional
change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25524
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Since sysupgrade creates the provisioning volume by default, it takes the
lowest free volume ID before the kernel and rootfs volumes are created.
This shifts their IDs by one, so devices that hardcode the rootfs volume
in root= (e.g. root=/dev/ubiblock0_1) no longer boot after a sysupgrade.
Create the provisioning volume with the last volume ID instead.
A provisioning volume that already exists at a low ID is kept and has to
be removed by hand.
Fixes: 725a1dc534 ("base-files: create the provisioning partition on sysupgrade by default")
Fixes: https://github.com/openwrt/openwrt/issues/25501
Signed-off-by: Johan Alvarado <contact@c127.dev>
hostapd_ubus_add_bss() counted a reference to the ubus context whether
ubus_add_object() succeeded or not, and hostapd_ubus_free_bss() drops
one only for an object with an id. Every BSS whose object could not be
added, such as each further link of an AP MLD, which all ask for the
same name, kept the context referenced after it was freed. Count the
reference only where the object was added.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
hostapd_dpp_freq_bss() reads hapd->conf->mld_ap, which exists only
with CONFIG_IEEE80211BE, so ucode.c did not build without it. Without
it there is no AP MLD, and the BSS the caller reached answers.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
The existing 6 GHz auth_type override only covers mixed modes
(psk-sae, eap-eap2), upgrading them to their WPA3 equivalents.
However, if a user configures pure WPA2 (psk or eap) on a 6 GHz
radio, the configuration falls through unchanged and hostapd rejects
it with a confusing error about invalid AKM suite.
Extend the override to also cover pure WPA2 modes:
- psk / psk-sae -> sae
- eap / eap-eap2 -> eap2
This matches the existing silent-upgrade pattern and ensures the
radio comes up regardless of which legacy encryption the user
selected.
Signed-off-by: Michael Pfeifroth <micpf@westermo.com>
Link: https://github.com/openwrt/openwrt/pull/23914
Signed-off-by: Robert Marko <robimarko@gmail.com>
A blackhole, unreachable or prohibit route has no device behind its
nexthop: fib_create_info() refuses one for these types and skips the
nexthop check that would find one, so fib_nh_dev stays NULL.
otto_l3_fib_add_v4() hands that device to is_vlan_dev() before any
test, and otto_l3_fib_check_v4(), which the delete path runs as well,
does the same in its variable initialisers. On an RTL9303 with the L3
offload built in, "ip route add blackhole 10.99.8.0/24" oopses in the
rtl83xx work queue and panics the box:
CPU 0 Unable to handle kernel paging request at virtual address
00000000, epc == 8070fe3c, ra == 80710594
Workqueue: rtl83xx otto_l3_fib_event_work_do
epc : 8070fe3c otto_l3_fib_add_v4+0x1c0/0x794
Kernel panic - not syncing: Fatal exception
The faulting load is the priv_flags word is_vlan_dev() tests. Such
routes are common on a router, for address space it announces but does
not use.
Handle them where a route through a nexthop object is already handled,
for the same reasons: there is nothing to offload, and the route can
still replace one that was, so the entry for its destination is taken
out, and a host route is trapped where the host table can hold it. The
delete path takes the same exit.
Tested on an RTL9303 (Hasivo S1100W-8XGT-SE) with the L3 offload built
in. Without this change the command above panics the box; with it, a
blackhole /24 stays in the kernel with no entry in hardware, an
offloaded /24 replaced by a blackhole loses its entry and gets it back
when replaced through the gateway again, a blackhole /32 gets a trap
entry in the host table, unreachable and prohibit routes go in as
well, and every delete leaves no entry and no oops. RTL839x, which runs
this code in a stock build, was not tested.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25513
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
rteth_start_xmit() pads the skb before checking whether the TX
descriptor is still owned by hardware. If it returns NETDEV_TX_BUSY,
the networking stack retries transmission with the modified skb.
This can add FCS space repeatedly or prevent detection of a padded
DSA trailer on subsequent attempts.
Move DSA port detection and padding after the descriptor ownership
check so that the skb remains unchanged when returning NETDEV_TX_BUSY.
Assisted-by: ChatGPT (OpenAI GPT-6.1 Sol)
Link: https://github.com/openwrt/openwrt/pull/25519
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
All of the mux kmods depend on CONFIG_MULTIPLEXER being set, however its
currently tristate but with no user visible name so its not selectable by
passing it .config.
It can only be selected by other symbols, so when you select for example
GPIO mux kmod in reality nothing is built unless your target already
selects CONFIG_MULTIPLEXER by accident or ALL_KMODS is used.
So, as a workaround since it still works fine as tristate, simply add a
patch that makes it visible and thus user selectable.
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Remove the unused and incomplete rtl930x_l3_intf_add implementation.
The function is entirely commented out and has no active callers.
Active egress interface allocation is handled by otto_l3_alloc_egress_intf
in l3.c, although it does not implement the old dynamic MTU allocation.
No functional changes.
Assisted-by: ChatGPT (OpenAI GPT-6 Astra)
Link: https://github.com/openwrt/openwrt/pull/25512
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Port mirroring is spread across dsa.c and the four chip-specific files.
Consolidate it in mirror.c to continue organizing the driver by function.
Move the DSA mirror callbacks and chip-specific configuration helpers into
mirror.c. Add mirror.h for their declarations and rtldsa_mirror_config,
and include mirror.o in the driver build.
Keep the existing operation tables, per-switch state and locking.
No functional changes intended.
Assisted-by: ChatGPT (OpenAI GPT-6 Astra)
Link: https://github.com/openwrt/openwrt/pull/25512
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The routes waiting on a gateway are keyed on the gateway address alone,
so the neighbour that answers writes its MAC into every route that names
that address, whichever link the route leaves by. The neighbour table is
keyed on the device as well as the address, for the reason that one
address on two links is two neighbours.
Keep the device a route reaches its gateway on, and let a neighbour
update touch only the routes that were resolved on it. The device is
kept as an index and not a pointer: the work item runs long after the
event and holds no reference to anything.
A route whose gateway answers on the route's own nexthop device is
unaffected, which is every route whose gateway is reachable the way the
kernel was told it is. What changes is the route whose neighbour answers
somewhere else: it took the MAC of whichever neighbour of that address
answered last, and now it waits for the one on its own device, which is
the neighbour the kernel forwards through. IPv4 gets there through a
link-scope route for the gateway on a second device, through a second
routing table, or through RTNH_F_ONLINK out of a device the gateway is
not on - fib_check_nh_v4_gw() resolves the gateway with the nexthop
device and table of the route being added, so each of those validates.
Which switches run this at all: the RTL839x, whose L3 setup is not
behind the RTL930x offload symbol, and an RTL930x built with that
symbol, which none of the six subtarget configs sets. The RTL838x and
RTL931x have no setup, so their notifiers return on the missing one.
Assisted-by: Claude:claude-opus-5
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25507
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Programming a route once its gateway resolves stamps the entry type of
an IPv4 unicast route over whatever the route was added as, and builds
the destination match of the PIE rule from the IPv4 destination and a
mask made by shifting a word by 32 minus the prefix length.
The type the add handler set is the one the writers have to see, and a
prefix length that is not an IPv4 one has no such mask. Keep the first
and build the second only for the family it belongs to. The rule fields
are read only where the L3 tables do not carry the destination, and
there every route is IPv4 unicast, which is also why dropping the stamp
changes nothing today.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25507
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Routes are grouped by the gateway they use, so that resolving one
neighbour reprograms every route behind it. The key is four bytes wide,
which is the whole of an IPv4 gateway and a quarter of any other kind,
and the field is read nowhere else: it never reaches a register, and
what the hardware is given is the next hop MAC the resolution produced.
Widen it to a full address and store an IPv4 gateway v4-mapped. Within
one family the mapping is injective, so two IPv4 gateways share a key
exactly when they shared one before, including the connected routes that
arrive with no gateway at all and go on sharing a single one; mapping
instead of zero-extending is also what keeps that shared key distinct
from the all-zero address a gateway of another family can carry.
What a wider key cannot do is say which family a gateway belongs to. An
IPv4 gateway written v4-mapped is a valid IPv6 gateway, and the kernel
accepts one on an IPv6 route, so a single key can name two neighbours
that are looked up in different tables and answer with different
addresses. The routes behind a key are therefore reprogrammed, and a
delete matched, only for the family the event came from.
The allocators and the next hop update take the gateway by address
rather than by value, so the compiler refuses a bare address of the
wrong width where one enters them. The raw lookup in the delete path
takes a void pointer and gets no such help; it is fed the same mapped
address its route was keyed on. The one message of the next hop update
that names a gateway to the user reads the IPv4 address back out of the
mapped one, so it prints what it printed before.
Assisted-by: Claude:claude-opus-5
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25507
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The hardware answers a prefix lookup with the lowest matching row and
the lookup carries the entry type, so each address family wants its own
run of rows, sorted by prefix length: that is how the vendor SDK lays
the table out, IPv4 from the bottom and IPv6 from the top
(dal_longan_l3.c), and how IPv6 prefix routes are to be placed here.
The row bookkeeping cannot express that yet. It counts every placed
prefix route and renumbers every one at or below an insertion point,
without looking at the family, so inserting into one run would shift
rows belonging to the other and leave the list naming rows that no
longer hold those routes.
Nothing changes while every route that gets a row is IPv4 unicast, which
is what the next hop update writes into every route it programs.
Assisted-by: Claude:claude-opus-5
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25507
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The CAM search key of otto_l3_930x_route_lookup_hw() is built from two
fields, and the IPv6 side gets both wrong. The entry type belongs in
bits 20:19 of L3_HW_LU_KEY_CTRL, which is where the clear mask points,
but the value is written unshifted: the search would run as IPv4 and a
stray bit would land in the VID field below. The destination is written
as the top word of the address four times over, so three quarters of an
IPv6 address never reach the key.
The field position is the one the GPL SDK gives for ENTRY_TYPE, and the
SDK masks the destination with its prefix before writing the key, which
is what ipv6_addr_prefix() does here. A route with an IPv4 destination
is unaffected: its entry type is zero, so the unshifted write was a
no-op, and its branch is untouched.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25507
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
otto_l3_930x_net6_mask() writes the leading all-ones bytes and the
partial byte after them, and leaves the rest of the address alone. Both
callers hand it an uninitialised struct, so the partial byte is OR-ed
into whatever the stack held there and every byte past it keeps that,
and the route is programmed with a mask nobody chose. At a prefix length
of 128 the partial byte is s6_addr[16], one past the end of the address.
Neither can happen today, because no IPv6 route reaches a writer and the
function has never run. Fix it before one does.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25507
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Update b43-tools to latest git commit 36284a5 (release b43-fwcutter-021).
Includes upstream commit 61105cc ("Makefiles: Declare source
prerequisites for object rules"), which fixes build failures on macOS
and systems where $< resolved to generated dependency prerequisites
rather than the source file.
Changes:
36284a5 fwcutter: Update release script
8690498 ci: Reduce flags
e23a1bb Align all CFLAGS
8440ca7 Add build CI
672a056 Use true instead of hardcoding /bin/true
7fd711a fwcutter: Bump version
61105cc Makefiles: Declare source prerequisites for object rules
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/25500
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
rteth_930x_set_rx_mode() and rteth_931x_set_rx_mode() say they flood
every class of RMA address while the conduit is promiscuous, like the
RTL838x version. All three write 3 into every 2-bit action field of
RMA_CTRL_0..2. In the GPL SDK that is flood on RTL838x
(dal_maple_trap_rmaGroupAction_set()), but trap to the master CPU on
RTL930x and RTL931x (dal_longan_trap_rmaAction_set(),
dal_mango_trap_rmaAction_set()), which have no flood action for these
addresses at all. On an RTL9303, frames to 01:80:C2:00:00:0B and
01:80:C2:00:00:21 reach the CPU with the trapped-RMA reason, 28.
Trapping is also what fits here: whether such a frame goes any further
is for the bridge to decide. Keep the values and correct the comments.
No functional change.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25499
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The RTL93xx CPU tag reports a frame trapped by the reserved multicast
address (RMA) rules with reason 28, NIC_RX_REASON_RMA in the GPL SDK's
reasonTbl (nic_rtl9300.c, nic_rtl9310.c), whenever none of the reasons
for a specific kind of RMA frame (PTP, BPDU, LACP, LLDP, EAPOL, ...)
applies. rteth_93xx_decode_tag() counts only 19 to 27 as not forwarded
by hardware, so these frames are reported as forwarded.
They do reach the CPU: rteth_930x_set_rx_mode() sets every RMA action
in RMA_CTRL_0..2 to 3 while the conduit is promiscuous, and on RTL930x
3 traps the frame to the (master) CPU rather than flooding it.
Count 28 as trapped too. On its own this changes nothing yet, since
dsa_switch_rcv() marks every frame from the metadata path as forwarded
whatever the driver decoded. It is what a receive path that honours the
decode needs, to forward a trapped frame for 01:80:C2:00:00:2x, which a
bridge is meant to forward, in software instead of dropping it.
Tested on an RTL9303 (Hasivo S1100W-8XGT-SE) with a throwaway print of
the CPU tag: five frames to 01:80:C2:00:00:0B and five to
01:80:C2:00:00:21 from a host all arrive with reason 28, reported as
not forwarded with this change. RTL931x has the same reason in the SDK
and was not tested.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25499
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The powerpc kernel of the WD My Book Live differs between builds. The
difference sits in the vDSO, which the kernel embeds with its debug
information as vdso32.so.dbg:
-GNU C11 14.4.0 ... -mstack-protector-guard=tls -mstack-protector-guard-reg=r2 -mstack-protector-guard-offset=872 -m32 -g -O2 ...
+GNU C11 14.4.0 ... -m32 -g -O2 ...
stack_protector_prepare appends these flags to KBUILD_CFLAGS with $(eval)
from its recipe, while vdso_prepare builds the vDSO from a sibling
prerequisite of prepare. Nothing orders the two, so whether the flags
end up in DW_AT_producer depends on the order in which make starts them.
Add a pending patch that makes vdso_prepare depend on
stack_protector_prepare.
Link: https://github.com/openwrt/openwrt/pull/25429
Signed-off-by: Paul Spooren <mail@aparcar.org>
b0911074d3cf plat: update closed-source object for AN7581 and AN7583
705478c3d84f plat: add DRAM calibration closed-source object for EN7523
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
The GoodTop GT-SWTXG8FM is an eight-port SFP+ managed switch based
on the Realtek RTL9303.
Device specification:
- SoC: Realtek RTL9303 rev B
- RAM: 128 MiB
- Flash: 16 MiB Winbond W25Q128 SPI NOR
- Ethernet: 8x SFP+ cages
- Button: reset, GPIO 5 active-low
- System LED: GPIO 0
- Power input: 12 V DC
- Bootloader: Realtek U-Boot 2011.12
- Vendor board model: RTL9303_8XGE
Serial:
- Stock firmware configures ttyS0 at 38400 baud.
- The physical UART header location and pinout were not identified
during bring-up. Serial access is not required for normal install.
Flash layout:
- 0x000000-0x0e0000: loader / LOADER
- 0x0e0000-0x0f0000: bdinfo / BDINFO
- 0x0f0000-0x100000: sysinfo / SYSINFO
- 0x100000-0x200000: vendor configuration / JFFS2 CFG
- 0x200000-0x300000: vendor log / JFFS2 LOG
- 0x300000-0xf00000: firmware / RUNTIME
- 0xf00000-0x1000000: oeminfo / OEMINFO
The factory MAC address is read from ethaddr in BDINFO through NVMEM.
The vendor bootloader uses legacy uImage magic 0x83800000.
Generate OpenWrt images with this magic and configure the firmware
splitter accordingly.
A dedicated squashfs-factory.bix image is generated for installation
from the stock web interface. The regular squashfs-sysupgrade.bin image
is used for subsequent OpenWrt upgrades.
Installation:
1. Open Management -> Firmware -> Manual Upgrade in the stock web UI.
2. Select Upgrade and HTTP.
3. Upload the squashfs-factory.bix image.
4. Wait for "Upgrade Image Done", then reboot the switch.
The stock web updater has been verified on hardware. Persistent
squashfs boot, writable JFFS2 overlay, reboot persistence and normal
OpenWrt sysupgrade have also been validated.
All eight SFP+ cages have been individually validated with 10G links.
Port LEDs, the system LED, reset button and DSA VLAN operation have
been tested.
Return to stock was validated using the original 12 MiB RUNTIME backup
from the same physical device. Under OpenWrt, copy that image to
/tmp/RUNTIME.bin and restore only the firmware partition with:
mtd -f write /tmp/RUNTIME.bin firmware
mtd verify /tmp/RUNTIME.bin firmware
sync
reboot
The loader, BDINFO, SYSINFO and OEMINFO partitions are not rewritten
by this procedure and were verified unchanged after restoration.
Signed-off-by: Christian Talavera <ctalaveraw@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/25391
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
This patch is meant for platforms without OF and only for ARM SoCs. All
ARM stuff has OF. Removes warnings like:
b53-srab-switch b53-srab-switch: error -EINVAL: invalid resource (null)
b53-srab-switch b53-srab-switch: probe with driver b53-srab-switch failed with error -22
On platforms using b53.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/25420
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The target has switched to kernel 6.18 a while ago.
Remove the now unused 6.12 config and patches
Signed-off-by: Sander van Deijck <sander@vandeijck.com>
A router that drops an expired packet without a word is a hole in every
traceroute through it, and a routing loop that ends quietly instead of
being reported. Now that the rows this driver writes decrement the hop
count and check it, the packets that fail that check exist, and the
setup leaves the action for them at zero, which is drop.
Trap them to the CPU instead, and the kernel answers with the ICMP the
router owes. The field is TTL_FAIL_ACT at bit 17 of the IPv4 route
control and HL_FAIL_ACT at bit 21 of the IPv6 one, two bits each, and
the Realtek GPL SDK gives their values in order as drop, trap to CPU,
trap to master CPU - _actIpucRouteCtrlTtlFail and
_actIp6ucRouteCtrlHlFail in dal_longan_l3.c.
The trap has no limiter of its own, and neither has any other trap this
driver sets: the rows for the switch's own addresses and the row that
catches what no other row matches all hand their packets to the CPU the
same way. A packet only gets here after being routed, so reaching the
CPU this way costs a sender the same effort as reaching it by any of
those. If L3 exceptions should have a queue and a bucket of their own,
that is worth doing for all of them at once and not for this one.
Only an RTL930x built with the offload symbol runs this setup, and no
subtarget config sets it. The RTL839x has a setup of its own and does
not pass through here.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25312
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Opening or closing a place in the prefix block moves rows one at a time,
and the mover stops at the first row it cannot read or write. It says
whether it had written anything, -EAGAIN or -EIO, but not how many rows
it had already copied. Both callers read any error as "nothing
happened" and leave the list naming the rows the routes were at, which
after -EIO is wrong.
What the table really holds then is a block shifted from some row on,
one route present twice - at the row it was at and at the row its copy
reached - and a driver that believes neither. Two things follow from
that, and both are silent:
The next route placed in that family is written from those names. Its
row is computed as the first row at or below its prefix length, and the
end of the block as the count of rows in use, which is now one short of
where the last route really is. So the write lands on a row that still
holds a live route, and that route stops forwarding without anything
saying so. It is found out only when it is removed, as "was not in
hardware".
And the copy the interrupted move left behind is a valid row with a
valid next hop index. Removing the route clears the row the list names,
not the copy, so the copy goes on forwarding with an index that will be
handed to another route.
Neither can be undone from here. There is no count to renumber from, and
the only thing that could copy the rows back is the engine that just
failed. So stop: the table is marked unusable, and from that point
nothing is placed and nothing is compacted, because both are arithmetic
on names that are known to be wrong. The placement stops only on -EIO:
after -EAGAIN the block is as it was, and the route gets no row this
time. The compaction stops on either, because the leaving row is written
invalid before the move: even a move that wrote nothing leaves a hole
the row count does not see, and the next placement would push the last
live route off the end of the block. What is already in the table keeps
forwarding and can still be removed one row at a time, which needs no
arithmetic. A route that arrives after that gets no row, and what
becomes of its traffic is whatever becomes of any destination the table
does not hold on that switch - on this branch, dropped in silicon.
The move can fail: the table command is polled with a 10 ms timeout, and
the two sites that poll it already log when it expires, which is the
driver's own statement that a table that does not answer is a thing that
happens. It has not been seen on an RTL9303 here, and this cannot be
provoked from outside, so what is claimed for it is that it fails closed
- not that it was watched doing so.
What it does not fix: the duplicate row. It stays valid until the driver
is loaded again, and it forwards. Clearing it means asking the CAM for a
route that is in two places and invalidating both, on a path that has
just been told the table is not to be trusted - a second mechanism for a
state this one has already declared broken and loud.
Assisted-by: Claude:claude-opus-5
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25312
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
otto_l3_930x_route_rows_move() copies prefix route rows one at a time,
reading each and writing it one place up or down, and returns whatever
error stopped it. A caller cannot tell a move that failed before
touching the table from one that left the block half shifted.
Return -EAGAIN when no row was written: the table could not be
acquired, or the first row could not be read. Return -EIO for a failed
write, and for a failed read once any row has been written, since the
block is half shifted from then on.
No functional change: both callers still test for any error. The next
commit tells the two apart.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25312
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
A router decrements the hop count of every packet it forwards. The rows
this driver writes do not: both writers encode a decrement bit and a
check bit, both readers decode them, the debugfs dump prints them, and
nothing has ever set either. They are written as the zero the allocator
left behind.
Measured on an RTL9303 with the L3 offload built in, IPv4 through an
offloaded route and sent with a hop count of 64: the far side sees 64
on an image that does not carry this commit and 63 on one built at it.
The switch was a hop that cost nothing, so a routing loop through it
never expired and no traceroute could see it.
The Realtek GPL SDK builds a route entry with both bits set unless the
caller asks for neither - dal_longan_l3.c has, for the host and the
prefix entry alike,
pEntry->ttl_dec = (pRoute->flags & RTK_L3_FLAG_TTL_DEC_IGNORE)? 0 : 1;
pEntry->ttl_chk = (pRoute->flags & RTK_L3_FLAG_TTL_CHK_IGNORE)? 0 : 1;
so this is the vendor's default for an entry created with no flags of
its own, not a value chosen here.
Set them where the route is given its action, and only when that
action forwards: a row that traps or drops is not a hop and keeps them
clear, like every other trapping row this driver writes. That includes
a route trapped while its next hop has no port. With both bits set on
such a row anyway, a packet sent at 64 still reaches the far side at 63
on an RTL9303: the silicon does not spend the hop on a packet it traps,
so this keeps the rows consistent rather than saving a decrement.
On its own this leaves a packet whose hop count runs out dropped in
silicon rather than answered: the action for that is a field of the
route control register, left at zero by a setup this commit does not
touch, and zero is drop. A later commit in this series sets it, so the
packet reaches the CPU and the ICMP a router owes comes back.
Where it takes effect: the RTL930x writers are the ones that encode
these bits, and that family needs the offload symbol no subtarget
config sets. The RTL839x runs this file in a stock build, but its route
writer puts neither bit in a register, so the assignment is a no-op
there; the RTL838x and RTL931x have no L3 setup at all. Where they are
programmed, a packet arriving with a hop count of one stops being
forwarded, which is the point of them.
Assisted-by: Claude:claude-opus-5
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25312
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
A route the add path allocates but cannot program stays behind. When
the router MAC or the egress interface cannot be allocated, or the host
table has no slot for it, otto_l3_fib_add_v4() jumps to out_free_rt,
which frees nothing, and the host trap for a route through a nexthop
object returns the same way. The route keeps its gateway hash entry,
its id and its place on the route list until the kernel deletes it.
Move the part of otto_l3_route_remove() that drops the driver's own
record of a route, namely the hash entry, the id bit, the list entry and
the allocation, into otto_l3_route_free(), and call it on those paths
as well. The hash entry now goes last rather than first; nothing in
between looks the route up there.
The delete that later comes for such a route finds nothing and logs
so, where it used to find the unprogrammed route and remove it.
Tested on an RTL9303 (Hasivo S1100W-8XGT-SE) with the L3 offload on,
this series on main at bfb9858944. Every removal goes through the new
function: a /24 and a /32 through a gateway, each replaced through a
nexthop object, through the other gateway and back, then deleted, and
a replace between two gateways. The entries come and go as before, the
/32 through the nexthop object as a trap, and the log shows no removal
error. With the IPv6 unicast series on top, two /128 routes are
offloaded and deleted without an entry error. The failure paths the new
calls sit on were not reached on hardware.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25312
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The PCS controller is managed and has no explicit remove path.
Initialize its mutex with devm_mutex_init() so lock debugging state is
released together with the managed controller resources.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The driver does not use any declarations provided by of_platform.h.
Remove the unnecessary include.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
RTPCS_931X_MAC_SERDES_MODE_CTRL has no users. The driver configures
RTL931x SerDes modes through the separate register field definitions,
so remove the stale macro.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Place the opening braces of three RTL930x SerDes configuration arrays
on their declaration lines. This follows the kernel coding style and
clears the corresponding checkpatch errors.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The RTL931x XSG write helper uses spaces for indentation instead of the
kernel's usual tabs. Retab the function to satisfy checkpatch and match
the surrounding code.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
All supported switch families use identical phylink PCS callbacks.
Replace the four duplicate operation tables with one common table to
avoid unnecessary per-family definitions.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The activate and deactivate callbacks ignore failures while changing
the SerDes core analog power state and continue with further
configuration. Return those errors immediately so callers do not
proceed from an unknown power state.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The first SerDes start requests a switch queue reset but discards any
regmap failure. Return the write result so PCS configuration does not
report success when the required recovery reset failed.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The RTL838x initialization callback reports success even when powering
off or resetting the SerDes fails. Check the first regmap write and
return the result of the reset write so probe can report either
failure.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>