Skip to content

luci-app-ppe-qos: add a page for the PPE hardware QoS classifiers - #8965

Open
JuliusBairaktaris wants to merge 1 commit into
openwrt:masterfrom
JuliusBairaktaris:luci-app-ppe-qos
Open

luci-app-ppe-qos: add a page for the PPE hardware QoS classifiers#8965
JuliusBairaktaris wants to merge 1 commit into
openwrt:masterfrom
JuliusBairaktaris:luci-app-ppe-qos

Conversation

@JuliusBairaktaris

Copy link
Copy Markdown
Contributor

Adds a LuCI page for ppe-qos, the package that configures the Qualcomm PPE's
hardware QoS classifiers.

ppe-qos owns three things, and the page edits all three from one place under
Network → PPE QoS:

  • Marking rules — put a DSCP on a flow. The switch's own ACL engine does
    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.
  • DSCP → priority — applied per switch port with dcb app.
  • Small-packet priority — the driver's length-based classifier.

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 leaving
someone to look for them.

Dependency

Requires the ppe-qos package, which is part of openwrt/openwrt#24806 and not
yet merged. This cannot build until that lands, so it should not be merged
before it.

Copilot AI lite review requested due to automatic review settings August 22, 2026 18:23
@openwrt openwrt Bot added add package Introduces a new package Makefile build script not following guidelines Pull request does not follow formatting guidelines labels Aug 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +52 to +53
o.placeholder = _('auto');
o.optional = true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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):

Suggested change
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)';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.``

Suggested change
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

Comment on lines +119 to +123
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)'));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
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

Comment on lines +45 to +46
for (var i = 0; i <= 7; i++)
o.value(i, _('Priority %d').format(i));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
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))';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@openwrt openwrt Bot removed the not following guidelines Pull request does not follow formatting guidelines label Aug 23, 2026

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed after the force-push (10332fbfee8122). 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

Comment on lines +51 to +52
prio_options(o);
o.default = '5';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +125 to +129
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)'));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

add package Introduces a new package Makefile build script

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants