b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From: Felix Fietkau <nbd@nbd.name> |
| 2 | Date: Sun, 11 Oct 2020 22:28:32 +0200 |
| 3 | Subject: [PATCH] net: ethernet: mediatek: mtk_eth_soc: add flow offloading |
| 4 | support |
| 5 | |
| 6 | Only supports IPv4 for now |
| 7 | |
| 8 | Signed-off-by: Felix Fietkau <nbd@nbd.name> |
| 9 | --- |
| 10 | create mode 100644 drivers/net/ethernet/mediatek/mtk_offload.c |
| 11 | create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c |
| 12 | |
| 13 | --- a/drivers/net/ethernet/mediatek/Makefile |
| 14 | +++ b/drivers/net/ethernet/mediatek/Makefile |
| 15 | @@ -4,4 +4,4 @@ |
| 16 | # |
| 17 | |
| 18 | obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o |
| 19 | -mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o |
| 20 | +mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_offload.o |
| 21 | --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c |
| 22 | +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c |
| 23 | @@ -20,6 +20,8 @@ |
| 24 | #include <linux/pinctrl/devinfo.h> |
| 25 | #include <linux/phylink.h> |
| 26 | #include <linux/jhash.h> |
| 27 | +#include <linux/netfilter.h> |
| 28 | +#include <net/netfilter/nf_flow_table.h> |
| 29 | #include <net/dsa.h> |
| 30 | |
| 31 | #include "mtk_eth_soc.h" |
| 32 | @@ -1362,8 +1364,12 @@ static int mtk_poll_rx(struct napi_struc |
| 33 | (trxd.rxd2 & RX_DMA_VTAG)) |
| 34 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), |
| 35 | RX_DMA_VID(trxd.rxd3)); |
| 36 | - skb_record_rx_queue(skb, 0); |
| 37 | - napi_gro_receive(napi, skb); |
| 38 | + if (mtk_offload_check_rx(eth, skb, trxd.rxd4) == 0) { |
| 39 | + skb_record_rx_queue(skb, 0); |
| 40 | + napi_gro_receive(napi, skb); |
| 41 | + } else { |
| 42 | + dev_kfree_skb(skb); |
| 43 | + } |
| 44 | |
| 45 | skip_rx: |
| 46 | ring->data[idx] = new_data; |
| 47 | @@ -2902,6 +2908,25 @@ static int mtk_set_rxnfc(struct net_devi |
| 48 | return ret; |
| 49 | } |
| 50 | |
| 51 | +static int |
| 52 | +mtk_flow_offload(enum flow_offload_type type, struct flow_offload *flow, |
| 53 | + struct flow_offload_hw_path *src, |
| 54 | + struct flow_offload_hw_path *dest) |
| 55 | +{ |
| 56 | + struct mtk_mac *mac = netdev_priv(src->dev); |
| 57 | + struct mtk_eth *eth = mac->hw; |
| 58 | + |
| 59 | + if (!eth->soc->offload_version) |
| 60 | + return -EINVAL; |
| 61 | + |
| 62 | + if (src->dev->base_addr != dest->dev->base_addr) |
| 63 | + return -EINVAL; |
| 64 | + |
| 65 | + mac = netdev_priv(src->dev); |
| 66 | + |
| 67 | + return mtk_flow_offload_add(eth, type, flow, src, dest); |
| 68 | +} |
| 69 | + |
| 70 | static const struct ethtool_ops mtk_ethtool_ops = { |
| 71 | .get_link_ksettings = mtk_get_link_ksettings, |
| 72 | .set_link_ksettings = mtk_set_link_ksettings, |
| 73 | @@ -2933,6 +2958,7 @@ static const struct net_device_ops mtk_n |
| 74 | #ifdef CONFIG_NET_POLL_CONTROLLER |
| 75 | .ndo_poll_controller = mtk_poll_controller, |
| 76 | #endif |
| 77 | + .ndo_flow_offload = mtk_flow_offload, |
| 78 | }; |
| 79 | |
| 80 | static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) |
| 81 | @@ -3198,6 +3224,10 @@ static int mtk_probe(struct platform_dev |
| 82 | eth->base + MTK_ETH_PPE_BASE, 2); |
| 83 | if (err) |
| 84 | goto err_free_dev; |
| 85 | + |
| 86 | + err = mtk_flow_offload_init(eth); |
| 87 | + if (err) |
| 88 | + goto err_free_dev; |
| 89 | } |
| 90 | |
| 91 | for (i = 0; i < MTK_MAX_DEVS; i++) { |
| 92 | --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h |
| 93 | +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h |
| 94 | @@ -949,6 +949,7 @@ struct mtk_eth { |
| 95 | int ip_align; |
| 96 | |
| 97 | struct mtk_ppe ppe; |
| 98 | + struct flow_offload __rcu **foe_flow_table; |
| 99 | }; |
| 100 | |
| 101 | /* struct mtk_mac - the structure that holds the info about the MACs of the |
| 102 | @@ -993,4 +994,12 @@ int mtk_gmac_sgmii_path_setup(struct mtk |
| 103 | int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); |
| 104 | int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); |
| 105 | |
| 106 | +int mtk_flow_offload_init(struct mtk_eth *eth); |
| 107 | +int mtk_flow_offload_add(struct mtk_eth *eth, |
| 108 | + enum flow_offload_type type, |
| 109 | + struct flow_offload *flow, |
| 110 | + struct flow_offload_hw_path *src, |
| 111 | + struct flow_offload_hw_path *dest); |
| 112 | +int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4); |
| 113 | + |
| 114 | #endif /* MTK_ETH_H */ |
| 115 | --- /dev/null |
| 116 | +++ b/drivers/net/ethernet/mediatek/mtk_offload.c |
| 117 | @@ -0,0 +1,146 @@ |
| 118 | +/* This program is free software; you can redistribute it and/or modify |
| 119 | + * it under the terms of the GNU General Public License as published by |
| 120 | + * the Free Software Foundation; version 2 of the License |
| 121 | + * |
| 122 | + * This program is distributed in the hope that it will be useful, |
| 123 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 124 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 125 | + * GNU General Public License for more details. |
| 126 | + * |
| 127 | + * Copyright (C) 2018 John Crispin <john@phrozen.org> |
| 128 | + */ |
| 129 | + |
| 130 | +#include <net/netfilter/nf_flow_table.h> |
| 131 | +#include "mtk_eth_soc.h" |
| 132 | + |
| 133 | +static int |
| 134 | +mtk_offload_prepare_v4(struct mtk_eth *eth, struct mtk_foe_entry *entry, |
| 135 | + struct flow_offload_tuple *s_tuple, |
| 136 | + struct flow_offload_tuple *d_tuple, |
| 137 | + struct flow_offload_hw_path *src, |
| 138 | + struct flow_offload_hw_path *dest) |
| 139 | +{ |
| 140 | + int dest_port = 1; |
| 141 | + |
| 142 | + if (dest->dev == eth->netdev[1]) |
| 143 | + dest_port = 2; |
| 144 | + |
| 145 | + mtk_foe_entry_prepare(entry, MTK_PPE_PKT_TYPE_IPV4_HNAPT, s_tuple->l4proto, |
| 146 | + dest_port, dest->eth_src, dest->eth_dest); |
| 147 | + mtk_foe_entry_set_ipv4_tuple(entry, false, |
| 148 | + s_tuple->src_v4.s_addr, s_tuple->src_port, |
| 149 | + s_tuple->dst_v4.s_addr, s_tuple->dst_port); |
| 150 | + mtk_foe_entry_set_ipv4_tuple(entry, true, |
| 151 | + d_tuple->dst_v4.s_addr, d_tuple->dst_port, |
| 152 | + d_tuple->src_v4.s_addr, d_tuple->src_port); |
| 153 | + |
| 154 | + if (dest->flags & FLOW_OFFLOAD_PATH_PPPOE) |
| 155 | + mtk_foe_entry_set_pppoe(entry, dest->pppoe_sid); |
| 156 | + |
| 157 | + if (dest->flags & FLOW_OFFLOAD_PATH_VLAN) |
| 158 | + mtk_foe_entry_set_vlan(entry, dest->vlan_id); |
| 159 | + |
| 160 | + if (dest->flags & FLOW_OFFLOAD_PATH_DSA) |
| 161 | + mtk_foe_entry_set_dsa(entry, dest->dsa_port); |
| 162 | + |
| 163 | + return 0; |
| 164 | +} |
| 165 | + |
| 166 | +int mtk_flow_offload_add(struct mtk_eth *eth, |
| 167 | + enum flow_offload_type type, |
| 168 | + struct flow_offload *flow, |
| 169 | + struct flow_offload_hw_path *src, |
| 170 | + struct flow_offload_hw_path *dest) |
| 171 | +{ |
| 172 | + struct flow_offload_tuple *otuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple; |
| 173 | + struct flow_offload_tuple *rtuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple; |
| 174 | + struct mtk_foe_entry orig, reply; |
| 175 | + u32 ohash, rhash, timestamp; |
| 176 | + |
| 177 | + if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP) |
| 178 | + return -EINVAL; |
| 179 | + |
| 180 | + if (type == FLOW_OFFLOAD_DEL) { |
| 181 | + ohash = (unsigned long)flow->priv; |
| 182 | + rhash = ohash >> 16; |
| 183 | + ohash &= 0xffff; |
| 184 | + mtk_foe_entry_clear(ð->ppe, ohash); |
| 185 | + mtk_foe_entry_clear(ð->ppe, rhash); |
| 186 | + rcu_assign_pointer(eth->foe_flow_table[ohash], NULL); |
| 187 | + rcu_assign_pointer(eth->foe_flow_table[rhash], NULL); |
| 188 | + synchronize_rcu(); |
| 189 | + |
| 190 | + return 0; |
| 191 | + } |
| 192 | + |
| 193 | + switch (otuple->l3proto) { |
| 194 | + case AF_INET: |
| 195 | + if (mtk_offload_prepare_v4(eth, &orig, otuple, rtuple, src, dest) || |
| 196 | + mtk_offload_prepare_v4(eth, &reply, rtuple, otuple, dest, src)) |
| 197 | + return -EINVAL; |
| 198 | + break; |
| 199 | + default: |
| 200 | + return -EINVAL; |
| 201 | + } |
| 202 | + |
| 203 | + timestamp = mtk_r32(eth, 0x0010); |
| 204 | + |
| 205 | + ohash = mtk_foe_entry_commit(ð->ppe, &orig, timestamp); |
| 206 | + if (ohash < 0) |
| 207 | + return -EINVAL; |
| 208 | + |
| 209 | + rhash = mtk_foe_entry_commit(ð->ppe, &reply, timestamp); |
| 210 | + if (rhash < 0) { |
| 211 | + mtk_foe_entry_clear(ð->ppe, ohash); |
| 212 | + return -EINVAL; |
| 213 | + } |
| 214 | + |
| 215 | + rcu_assign_pointer(eth->foe_flow_table[ohash], flow); |
| 216 | + rcu_assign_pointer(eth->foe_flow_table[rhash], flow); |
| 217 | + |
| 218 | + ohash |= rhash << 16; |
| 219 | + flow->priv = (void *)(unsigned long)ohash; |
| 220 | + |
| 221 | + return 0; |
| 222 | +} |
| 223 | + |
| 224 | +static void mtk_offload_keepalive(struct mtk_eth *eth, unsigned int hash) |
| 225 | +{ |
| 226 | + struct flow_offload *flow; |
| 227 | + |
| 228 | + rcu_read_lock(); |
| 229 | + flow = rcu_dereference(eth->foe_flow_table[hash]); |
| 230 | + if (flow) |
| 231 | + flow->timeout = jiffies + 30 * HZ; |
| 232 | + rcu_read_unlock(); |
| 233 | +} |
| 234 | + |
| 235 | +int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4) |
| 236 | +{ |
| 237 | + unsigned int hash; |
| 238 | + |
| 239 | + switch (FIELD_GET(MTK_RXD4_PPE_CPU_REASON, rxd4)) { |
| 240 | + case MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR: |
| 241 | + case MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR: |
| 242 | + case MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR: |
| 243 | + hash = FIELD_GET(MTK_RXD4_FOE_ENTRY, rxd4); |
| 244 | + mtk_offload_keepalive(eth, hash); |
| 245 | + return -1; |
| 246 | + case MTK_PPE_CPU_REASON_PACKET_SAMPLING: |
| 247 | + return -1; |
| 248 | + default: |
| 249 | + return 0; |
| 250 | + } |
| 251 | +} |
| 252 | + |
| 253 | +int mtk_flow_offload_init(struct mtk_eth *eth) |
| 254 | +{ |
| 255 | + eth->foe_flow_table = devm_kcalloc(eth->dev, MTK_PPE_ENTRIES, |
| 256 | + sizeof(*eth->foe_flow_table), |
| 257 | + GFP_KERNEL); |
| 258 | + |
| 259 | + if (!eth->foe_flow_table) |
| 260 | + return -ENOMEM; |
| 261 | + |
| 262 | + return 0; |
| 263 | +} |
| 264 | --- a/drivers/net/ethernet/mediatek/mtk_ppe.c |
| 265 | +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c |
| 266 | @@ -375,6 +375,8 @@ int mtk_ppe_init(struct mtk_ppe *ppe, st |
| 267 | |
| 268 | ppe->foe_table = foe; |
| 269 | |
| 270 | + mtk_ppe_debugfs_init(ppe); |
| 271 | + |
| 272 | return 0; |
| 273 | } |
| 274 | |
| 275 | --- a/drivers/net/ethernet/mediatek/mtk_ppe.h |
| 276 | +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h |
| 277 | @@ -271,4 +271,7 @@ int mtk_foe_entry_set_pppoe(struct mtk_f |
| 278 | int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, |
| 279 | u16 timestamp); |
| 280 | |
| 281 | +/* internal */ |
| 282 | +int mtk_ppe_debugfs_init(struct mtk_ppe *ppe); |
| 283 | + |
| 284 | #endif |
| 285 | --- /dev/null |
| 286 | +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c |
| 287 | @@ -0,0 +1,114 @@ |
| 288 | +/* This program is free software; you can redistribute it and/or modify |
| 289 | + * it under the terms of the GNU General Public License as published by |
| 290 | + * the Free Software Foundation; version 2 of the License |
| 291 | + * |
| 292 | + * This program is distributed in the hope that it will be useful, |
| 293 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 294 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 295 | + * GNU General Public License for more details. |
| 296 | + * |
| 297 | + * Copyright (C) 2014-2016 Sean Wang <sean.wang@mediatek.com> |
| 298 | + * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org> |
| 299 | + * Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> |
| 300 | + */ |
| 301 | + |
| 302 | +#include <linux/kernel.h> |
| 303 | +#include <linux/debugfs.h> |
| 304 | +#include "mtk_eth_soc.h" |
| 305 | + |
| 306 | +static const char *mtk_foe_entry_state_str[] = { |
| 307 | + "INVALID", |
| 308 | + "UNBIND", |
| 309 | + "BIND", |
| 310 | + "FIN" |
| 311 | +}; |
| 312 | + |
| 313 | +static const char *mtk_foe_packet_type_str[] = { |
| 314 | + "IPV4_HNAPT", |
| 315 | + "IPV4_HNAT", |
| 316 | + "IPV6_1T_ROUTE", |
| 317 | + "IPV4_DSLITE", |
| 318 | + "IPV6_3T_ROUTE", |
| 319 | + "IPV6_5T_ROUTE", |
| 320 | + "IPV6_6RD", |
| 321 | +}; |
| 322 | + |
| 323 | +#define es(entry) (mtk_foe_entry_state_str[FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1)]) |
| 324 | +//#define ei(entry, end) (MTK_PPE_TBL_SZ - (int)(end - entry)) |
| 325 | +#define pt(entry) (mtk_foe_packet_type_str[FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1)]) |
| 326 | + |
| 327 | +static int mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private) |
| 328 | +{ |
| 329 | + struct mtk_ppe *ppe = m->private; |
| 330 | + int i, count; |
| 331 | + |
| 332 | + for (i = 0, count = 0; i < MTK_PPE_ENTRIES; i++) { |
| 333 | + struct mtk_foe_entry *entry = &ppe->foe_table[i]; |
| 334 | + |
| 335 | + if (!FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1)) |
| 336 | + continue; |
| 337 | + |
| 338 | + if (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1) == |
| 339 | + MTK_PPE_PKT_TYPE_IPV4_HNAPT) { |
| 340 | + struct mtk_foe_ipv4 *ip4 = &entry->ipv4; |
| 341 | + struct mtk_foe_mac_info *l2 = &ip4->l2; |
| 342 | + |
| 343 | + __be32 saddr = htonl(ip4->orig.src_ip); |
| 344 | + __be32 daddr = htonl(ip4->orig.dest_ip); |
| 345 | + __be32 nsaddr = htonl(ip4->new.src_ip); |
| 346 | + __be32 ndaddr = htonl(ip4->new.dest_ip); |
| 347 | + unsigned char h_dest[ETH_ALEN]; |
| 348 | + unsigned char h_source[ETH_ALEN]; |
| 349 | + |
| 350 | + *((__be32 *) h_source) = htonl(l2->src_mac_hi); |
| 351 | + *((__be16*) &h_source[4]) = htons(l2->src_mac_lo); |
| 352 | + *((__be32*) h_dest) = htonl(l2->dest_mac_hi); |
| 353 | + *((__be16*) &h_dest[4]) = htons(l2->dest_mac_lo); |
| 354 | + seq_printf(m, |
| 355 | + "(%x)0x%05x|state=%s|type=%s|" |
| 356 | + "%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|" |
| 357 | + "etype=0x%04x|info1=0x%x|info2=0x%x|" |
| 358 | + "vlan1=%d|vlan2=%d\n", |
| 359 | + count, i, es(entry), pt(entry), |
| 360 | + &saddr, ip4->orig.src_port, |
| 361 | + &daddr, ip4->orig.dest_port, |
| 362 | + &nsaddr, ip4->new.src_port, |
| 363 | + &ndaddr, ip4->new.dest_port, |
| 364 | + h_source, h_dest, |
| 365 | + ntohs(l2->etype), |
| 366 | + entry->ib1, |
| 367 | + ip4->ib2, |
| 368 | + l2->vlan1, |
| 369 | + l2->vlan2); |
| 370 | + count++; |
| 371 | + } else |
| 372 | + seq_printf(m, "0x%05x state=%s\n", count, es(entry)); |
| 373 | + } |
| 374 | + |
| 375 | + return 0; |
| 376 | +} |
| 377 | + |
| 378 | +static int mtk_ppe_debugfs_foe_open(struct inode *inode, struct file *file) |
| 379 | +{ |
| 380 | + return single_open(file, mtk_ppe_debugfs_foe_show, inode->i_private); |
| 381 | +} |
| 382 | + |
| 383 | +static const struct file_operations mtk_ppe_debugfs_foe_fops = { |
| 384 | + .open = mtk_ppe_debugfs_foe_open, |
| 385 | + .read = seq_read, |
| 386 | + .llseek = seq_lseek, |
| 387 | + .release = single_release, |
| 388 | +}; |
| 389 | + |
| 390 | +int mtk_ppe_debugfs_init(struct mtk_ppe *ppe) |
| 391 | +{ |
| 392 | + struct dentry *root; |
| 393 | + |
| 394 | + root = debugfs_create_dir("mtk_ppe", NULL); |
| 395 | + if (!root) |
| 396 | + return -ENOMEM; |
| 397 | + |
| 398 | + debugfs_create_file("entries", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_fops); |
| 399 | + |
| 400 | + return 0; |
| 401 | +} |