1
1

airoha: Add the capability to offload dscp field via netfilter flowtable

Introduce the capability to hw offload via netfilter flowtable APIs the
IP TOS info. Implement the sw offloading for DSCP field via the
netfilter flowtable APIs.

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://github.com/openwrt/openwrt/pull/23423
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
Lorenzo Bianconi 2026-05-18 12:25:41 +02:00 committed by Christian Marangi
parent 286f377389
commit a7b5bb233f
No known key found for this signature in database
GPG Key ID: AC001D09ADBFEAD7
2 changed files with 244 additions and 0 deletions

View File

@ -0,0 +1,150 @@
From 6408cdca652b1f85e5b8582c283203d11f4dedcb Mon Sep 17 00:00:00 2001
Message-ID: <6408cdca652b1f85e5b8582c283203d11f4dedcb.1779086987.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sun, 17 May 2026 21:11:27 +0200
Subject: [PATCH net-next 1/2] net: netfilter: flowtable: Add the capability to
offload dscp field
Introduce the capability to hw offload via netfilter flowtable APIs the
IP TOS info. Implement the sw offloading for DSCP field via the
netfilter flowtable APIs.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
include/net/netfilter/nf_flow_table.h | 2 ++
net/netfilter/nf_flow_table_ip.c | 12 ++++++++++++
net/netfilter/nf_flow_table_offload.c | 5 +++++
net/netfilter/nft_flow_offload.c | 22 ++++++++++++++++++++++
4 files changed, 41 insertions(+)
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -29,6 +29,7 @@ struct nf_flow_key {
struct flow_dissector_key_ipv4_addrs ipv4;
struct flow_dissector_key_ipv6_addrs ipv6;
};
+ struct flow_dissector_key_ip ip;
struct flow_dissector_key_keyid enc_key_id;
union {
struct flow_dissector_key_ipv4_addrs enc_ipv4;
@@ -138,6 +139,7 @@ struct flow_offload_tuple {
encap_num:2,
in_vlan_ingress:2;
u16 mtu;
+ u8 dscp;
union {
struct {
struct dst_entry *dst_cache;
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -372,6 +372,7 @@ static int nf_flow_offload_forward(struc
struct flow_offload *flow;
unsigned int thoff, mtu;
struct iphdr *iph;
+ u8 dscp;
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
@@ -401,6 +402,12 @@ static int nf_flow_offload_forward(struc
iph = ip_hdr(skb);
nf_flow_nat_ip(flow, skb, thoff, dir, iph);
+ dscp = FIELD_GET(INET_DSCP_MASK, ipv4_get_dsfield(iph));
+ if (tuplehash->tuple.dscp != dscp)
+ ipv4_change_dsfield(iph, INET_ECN_MASK,
+ FIELD_PREP(INET_DSCP_MASK,
+ tuplehash->tuple.dscp));
+
ip_decrease_ttl(iph);
skb_clear_tstamp(skb);
@@ -651,6 +658,7 @@ static int nf_flow_offload_ipv6_forward(
struct flow_offload *flow;
unsigned int thoff, mtu;
struct ipv6hdr *ip6h;
+ u8 dscp;
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
@@ -679,6 +687,12 @@ static int nf_flow_offload_ipv6_forward(
ip6h = ipv6_hdr(skb);
nf_flow_nat_ipv6(flow, skb, dir, ip6h);
+ dscp = FIELD_GET(INET_DSCP_MASK, ipv6_get_dsfield(ip6h));
+ if (tuplehash->tuple.dscp != dscp)
+ ipv6_change_dsfield(ip6h, INET_ECN_MASK,
+ FIELD_PREP(INET_DSCP_MASK,
+ tuplehash->tuple.dscp));
+
ip6h->hop_limit--;
skb_clear_tstamp(skb);
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -103,6 +103,7 @@ static int nf_flow_rule_match(struct nf_
NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_BASIC, basic);
NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6);
+ NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IP, ip);
NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_TCP, tcp);
NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_PORTS, tp);
@@ -168,6 +169,10 @@ static int nf_flow_rule_match(struct nf_
match->dissector.used_keys |= BIT_ULL(key->control.addr_type);
mask->basic.n_proto = 0xffff;
+ key->ip.tos = FIELD_PREP(INET_DSCP_MASK, tuple->dscp);
+ mask->ip.tos = 0xff;
+ match->dissector.used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_IP);
+
switch (tuple->l4proto) {
case IPPROTO_TCP:
key->tcp.flags = 0;
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -8,6 +8,7 @@
#include <linux/spinlock.h>
#include <linux/netfilter/nf_conntrack_common.h>
#include <linux/netfilter/nf_tables.h>
+#include <net/dsfield.h>
#include <net/ip.h> /* for ipv4 options. */
#include <net/inet_dscp.h>
#include <net/netfilter/nf_tables.h>
@@ -279,6 +280,27 @@ static int nft_flow_route(const struct n
return 0;
}
+static void nft_flow_set_dscp(const struct nft_pktinfo *pkt,
+ struct flow_offload *flow,
+ enum ip_conntrack_dir dir)
+{
+ struct flow_offload_tuple *tuple = &flow->tuplehash[dir].tuple;
+ struct sk_buff *skb = pkt->skb;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ tuple->dscp = FIELD_GET(INET_DSCP_MASK,
+ ipv4_get_dsfield(ip_hdr(skb)));
+ break;
+ case htons(ETH_P_IPV6):
+ tuple->dscp = FIELD_GET(INET_DSCP_MASK,
+ ipv6_get_dsfield(ipv6_hdr(skb)));
+ break;
+ default:
+ break;
+ }
+}
+
static bool nft_flow_offload_skip(struct sk_buff *skb, int family)
{
if (skb_sec_path(skb))
@@ -371,6 +393,9 @@ static void nft_flow_offload_eval(const
if (!flow)
goto err_flow_alloc;
+ nft_flow_set_dscp(pkt, flow, dir);
+ nft_flow_set_dscp(pkt, flow, !dir);
+
flow_offload_route_init(flow, &route);
if (tcph)
flow_offload_ct_tcp(ct);

View File

@ -0,0 +1,94 @@
From b9870ade9498f4119d3f8f8368fcd13e1fa0c7c9 Mon Sep 17 00:00:00 2001
Message-ID: <b9870ade9498f4119d3f8f8368fcd13e1fa0c7c9.1779086987.git.lorenzo@kernel.org>
In-Reply-To: <6408cdca652b1f85e5b8582c283203d11f4dedcb.1779086987.git.lorenzo@kernel.org>
References: <6408cdca652b1f85e5b8582c283203d11f4dedcb.1779086987.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 18 May 2026 08:36:20 +0200
Subject: [PATCH net-next 2/2] net: airoha: Set hw QoS parameter according to
the packet dscp
Introduce the capability to hw offload via netfilter flowtable APIs the
IP TOS info in order to configure hw queue and dscp field.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_ppe.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -11,6 +11,7 @@
#include <linux/rhashtable.h>
#include <net/ipv6.h>
#include <net/pkt_cls.h>
+#include <net/route.h>
#include "airoha_regs.h"
#include "airoha_eth.h"
@@ -298,7 +299,7 @@ static int airoha_ppe_foe_entry_prepare(
struct airoha_foe_entry *hwe,
struct net_device *dev, int type,
struct airoha_flow_data *data,
- int l4proto)
+ int l4proto, u8 dsfield)
{
u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val;
int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev);
@@ -331,7 +332,7 @@ static int airoha_ppe_foe_entry_prepare(
info.wcid);
} else {
struct airoha_gdm_port *port = netdev_priv(dev);
- u8 pse_port, channel;
+ u8 pse_port, channel, priority;
if (!airoha_is_valid_gdm_port(eth, port))
return -EINVAL;
@@ -350,9 +351,13 @@ static int airoha_ppe_foe_entry_prepare(
*/
channel = dsa_port >= 0 ? dsa_port : port->id;
channel = channel % AIROHA_NUM_QOS_CHANNELS;
- qdata |= FIELD_PREP(AIROHA_FOE_CHANNEL, channel);
+ priority = rt_tos2priority(dsfield);
+ priority = priority % AIROHA_NUM_QOS_QUEUES;
+ qdata |= FIELD_PREP(AIROHA_FOE_CHANNEL, channel) |
+ FIELD_PREP(AIROHA_FOE_QID, priority);
val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port) |
+ FIELD_PREP(AIROHA_FOE_IB2_DSCP, dsfield) |
AIROHA_FOE_IB2_PSE_QOS;
/* For downlink traffic consume SRAM memory for hw
* forwarding descriptors queue.
@@ -1044,9 +1049,9 @@ static int airoha_ppe_flow_offload_repla
struct net_device *odev = NULL;
struct flow_action_entry *act;
struct airoha_foe_entry hwe;
+ u8 dsfield = 0, l4proto = 0;
int err, i, offload_type;
u16 addr_type = 0;
- u8 l4proto = 0;
if (rhashtable_lookup(&eth->flow_table, &f->cookie,
airoha_flow_table_params))
@@ -1076,6 +1081,13 @@ static int airoha_ppe_flow_offload_repla
return -EOPNOTSUPP;
}
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
+ struct flow_match_ip match;
+
+ flow_rule_match_ip(rule, &match);
+ dsfield = match.key->tos;
+ }
+
switch (addr_type) {
case 0:
offload_type = PPE_PKT_TYPE_BRIDGE;
@@ -1141,7 +1153,7 @@ static int airoha_ppe_flow_offload_repla
return -EINVAL;
err = airoha_ppe_foe_entry_prepare(eth, &hwe, odev, offload_type,
- &data, l4proto);
+ &data, l4proto, dsfield);
if (err)
return err;