luci-app-ppe-qos: add a page for the PPE hardware QoS classifiers - #8965
luci-app-ppe-qos: add a page for the PPE hardware QoS classifiers#8965JuliusBairaktaris wants to merge 1 commit into
Conversation
openwrt-ai
left a comment
There was a problem hiding this comment.
Reviewed 1 new commit; the commit message matches what the diff does. Comments inline.
The ppe-qos backend is not in openwrt/openwrt yet, so the UCI option names (small_packet_len, small_packet_prio, wan_port, class, dscp, prio) could not be cross-checked against a consumer — the dependency on openwrt/openwrt#24806 is already noted in the PR body.
Generated by Claude Code
| o.placeholder = _('auto'); | ||
| o.optional = true; |
There was a problem hiding this comment.
wan_port is the only option on the page with no datatype, so it accepts arbitrary free text — spaces, ;, $(...) — which is then written to UCI and consumed by the ppe-qos shell/init code that runs dcb app dev <port>. Since the description says the value is derived from the WAN interface's device with the VLAN id stripped, netdevname is the matching validator (1–15 chars, no :, /, % or whitespace):
| o.placeholder = _('auto'); | |
| o.optional = true; | |
| o.placeholder = _('auto'); | |
| o.datatype = 'netdevname'; | |
| o.optional = true; |
If the backend actually expects a numeric switch port index rather than a device name, use uinteger instead — but it should not stay unvalidated either way.
Generated by Claude Code
| _('Frames up to this IP length take the priority below whatever their marking: ' + | ||
| 'ACKs, handshakes, DNS, VoIP and game traffic jump the bulk queue. 0 disables. ' + | ||
| 'A value just under the MTU means "everything that is not a full-size bulk frame".')); | ||
| o.datatype = 'range(0,1500)'; |
There was a problem hiding this comment.
range() validates a decimal, not an integer — range(min, max) calls parseDecimal(), so 128.5 passes here and gets written to UCI as a packet length. The sister QoS app uses the and(uinteger,…) idiom for exactly this, e.g. sqm.js:98.``
| o.datatype = 'range(0,1500)'; | |
| o.datatype = 'and(uinteger,range(0,1500))'; |
Same applies to the dscp value on line 117 (range(0,63) accepts 46.5).
Generated by Claude Code
| o.value('46', _('46 (EF - voice)')); | ||
| o.value('40', _('40 (CS5 - signalling, DNS)')); | ||
| o.value('34', _('34 (AF41 - calls)')); | ||
| o.value('32', _('32 (CS4 - games)')); | ||
| o.value('48', _('48 (CS6 - control)')); |
There was a problem hiding this comment.
nit: the suggestion list runs 46, 40, 34, 32, 48 — descending except for CS6, which is appended out of order. The CLASSES array on lines 6-14 that feeds the marking rules is ordered ascending by class, so the two dropdowns present the same DSCP scale in opposite/inconsistent orders. Ascending matches CLASSES:
| o.value('46', _('46 (EF - voice)')); | |
| o.value('40', _('40 (CS5 - signalling, DNS)')); | |
| o.value('34', _('34 (AF41 - calls)')); | |
| o.value('32', _('32 (CS4 - games)')); | |
| o.value('48', _('48 (CS6 - control)')); | |
| o.value('32', _('32 (CS4 - games)')); | |
| o.value('34', _('34 (AF41 - calls)')); | |
| o.value('40', _('40 (CS5 - signalling, DNS)')); | |
| o.value('46', _('46 (EF - voice)')); | |
| o.value('48', _('48 (CS6 - control)')); |
Generated by Claude Code
| for (var i = 0; i <= 7; i++) | ||
| o.value(i, _('Priority %d').format(i)); |
There was a problem hiding this comment.
nit: this is the same 0-7 priority scale as the prio selector on lines 126-128, but only that one tells the user where the 4-and-up cutoff is. Here the default is 5 with no hint why, and the page's whole premise is that "4 or more" is the meaningful threshold. Labelling both identically makes the connection visible:
| for (var i = 0; i <= 7; i++) | |
| o.value(i, _('Priority %d').format(i)); | |
| for (var i = 0; i <= 7; i++) | |
| o.value(i, i >= 4 ? _('Priority %d (served past the bulk queue)').format(i) | |
| : _('Priority %d (bulk queue)').format(i)); |
Generated by Claude Code
|
|
||
| o = s.option(form.Value, 'src_ip', _('Source address'), | ||
| _('Optional. Addresses or CIDRs, space separated; IPv4 and IPv6 may be mixed.')); | ||
| o.datatype = 'list(neg(ipmask))'; |
There was a problem hiding this comment.
Does the ppe-qos ACL backend actually support negated matches? neg() strips a leading ! and accepts the value, so this field (and dest_ip on line 87, src_port on line 93, dest_port on line 99) will happily take !192.168.1.0/24. None of the four descriptions mention negation, and the section intro says "The switch matches addresses and ports only". The datatype is copied from luci-app-firewall, where nftables does support inversion; a switch ACL engine typically does not. If ppe-qos cannot invert, dropping neg( here would stop the UI accepting rules the backend will silently mis-handle or reject.
Generated by Claude Code
ppe-qos configures which hardware egress queue of the Qualcomm PPE a flow rides: marking rules that put a DSCP on a flow with the switch's own ACL engine, a DSCP-to-priority map applied per switch port with dcb app, and the driver's small-packet priority. This edits all three from one page under Network. The page says what a class is worth, since that is the part which is not obvious: a mark only changes the queue if the map turns it into a priority of 4 or more, which is where the upper band of a shaped port starts, and it reaches Wi-Fi either way because mac80211 reads the DSCP for its access category. Requires the ppe-qos package from openwrt/openwrt#24806. Assisted-by: Claude <noreply@anthropic.com> Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
10332fb to
fee8122
Compare
openwrt-ai
left a comment
There was a problem hiding this comment.
Re-reviewed after the force-push (10332fb → fee8122). All five points from the previous round are addressed: wan_port now has netdevname, both bare range() datatypes became and(uinteger,range(...)) (idiom confirmed in-tree at routes.js:93),`` the DSCP suggestions are in ascending order, the two priority selectors share the new prio_options() helper, and `neg()` is gone from the four address/port datatypes. The amended commit message still matches what the diff does, and the added `PKG_MAINTAINER`/`PKG_LICENSE` follow the placement used by `luci-app-sqm`.
Two remaining points inline, both minor.
The ppe-qos backend is still absent from openwrt/openwrt master, so the UCI option names (small_packet_len, small_packet_prio, wan_port, class, dscp, prio) and the accepted class keywords remain uncheckable against a consumer — the dependency on openwrt/openwrt#24806 is noted in the PR body.
Generated by Claude Code
| prio_options(o); | ||
| o.default = '5'; |
There was a problem hiding this comment.
Selecting Priority 5 here deletes small_packet_prio from UCI rather than writing it: form.js:2152 removes an option when fval == this.default and rmempty is still at its default of true. So the device then runs on whatever ppe-qos compiles in as its own default, while the page keeps showing 5.
That is only harmless if the backend default is also 5. Every other option that carries a meaningful value is pinned — the enabled flag right above on line 41 has the same default = '1' plus an explicit o.rmempty = false, and class, dscp and prio all set it too. small_packet_prio is the one option with a default that is not pinned; is that deliberate, or should it get o.rmempty = false like its neighbour?
Generated by Claude Code
| o.value('32', _('32 (CS4 - games)')); | ||
| o.value('34', _('34 (AF41 - calls)')); | ||
| o.value('40', _('40 (CS5 - signalling, DNS)')); | ||
| o.value('46', _('46 (EF - voice)')); | ||
| o.value('48', _('48 (CS6 - control)')); |
There was a problem hiding this comment.
nit: AF31 is offered as a marking class (CLASSES, line 9) but DSCP 26 is missing from these suggestions, so a user who marks a flow "multimedia streaming" finds nothing to select here and has to know that AF31 means 26 and type it. The other three unlisted classes are self-explanatory — CS0/CS1/CS3 are exactly the ones the section text means by "anything unlisted stays at priority 0" — which makes AF31 look like an oversight rather than the same policy.
The field is a form.Value with choices, so typing 26 does work; this is only about the suggestion list matching the classes the page itself offers:
| o.value('32', _('32 (CS4 - games)')); | |
| o.value('34', _('34 (AF41 - calls)')); | |
| o.value('40', _('40 (CS5 - signalling, DNS)')); | |
| o.value('46', _('46 (EF - voice)')); | |
| o.value('48', _('48 (CS6 - control)')); | |
| o.value('26', _('26 (AF31 - streaming)')); | |
| o.value('32', _('32 (CS4 - games)')); | |
| o.value('34', _('34 (AF41 - calls)')); | |
| o.value('40', _('40 (CS5 - signalling, DNS)')); | |
| o.value('46', _('46 (EF - voice)')); | |
| o.value('48', _('48 (CS6 - control)')); |
Generated by Claude Code
Adds a LuCI page for
ppe-qos, the package that configures the Qualcomm PPE'shardware QoS classifiers.
ppe-qosowns three things, and the page edits all three from one place underNetwork → PPE QoS:
the rewrite, so the mark reaches flows the PPE has offloaded, which an
nftables mangle rule cannot: an offloaded flow never returns to the CPU after
its first packets.
dcb app.The page spells out what a class is actually worth, since that is the part
which is not obvious from the fields: a mark only changes the queue if the map
turns it into a priority of 4 or more, which is where the upper band of a
shaped port begins, and it reaches Wi-Fi either way because mac80211 reads the
DSCP for its access category. Shaper rates and queue depth are not here — they
belong to sqm-scripts'
hw_ppe.qos, and the page says so rather than leavingsomeone to look for them.
Dependency
Requires the
ppe-qospackage, which is part of openwrt/openwrt#24806 and notyet merged. This cannot build until that lands, so it should not be merged
before it.