Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-airoha-add-per-flow-stats-support-to-hw-flowtable-offloading'

Lorenzo Bianconi says:

====================
net: airoha: Add per-flow stats support to hw flowtable offloading

Introduce per-flow stats accounting to the flowtable hw offload in the
airoha_eth driver. Flow stats are split in the PPE and NPU modules:
- PPE: accounts for high 32bit of per-flow stats
- NPU: accounts for low 32bit of per-flow stats

v1: https://lore.kernel.org/20250514-airoha-en7581-flowstats-v1-0-c00ede12a2ca@kernel.org
====================

Link: https://patch.msgid.link/20250516-airoha-en7581-flowstats-v2-0-06d5fbf28984@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+427 -74
+7
drivers/net/ethernet/airoha/Kconfig
··· 24 24 This driver supports the gigabit ethernet MACs in the 25 25 Airoha SoC family. 26 26 27 + config NET_AIROHA_FLOW_STATS 28 + default y 29 + bool "Airoha flow stats" 30 + depends on NET_AIROHA && NET_AIROHA_NPU 31 + help 32 + Enable Aiorha flowtable statistic counters. 33 + 27 34 endif #NET_VENDOR_AIROHA
+33
drivers/net/ethernet/airoha/airoha_eth.h
··· 50 50 #define PPE_NUM 2 51 51 #define PPE1_SRAM_NUM_ENTRIES (8 * 1024) 52 52 #define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES) 53 + #ifdef CONFIG_NET_AIROHA_FLOW_STATS 54 + #define PPE1_STATS_NUM_ENTRIES (4 * 1024) 55 + #else 56 + #define PPE1_STATS_NUM_ENTRIES 0 57 + #endif /* CONFIG_NET_AIROHA_FLOW_STATS */ 58 + #define PPE_STATS_NUM_ENTRIES (2 * PPE1_STATS_NUM_ENTRIES) 59 + #define PPE1_SRAM_NUM_DATA_ENTRIES (PPE1_SRAM_NUM_ENTRIES - PPE1_STATS_NUM_ENTRIES) 60 + #define PPE_SRAM_NUM_DATA_ENTRIES (2 * PPE1_SRAM_NUM_DATA_ENTRIES) 53 61 #define PPE_DRAM_NUM_ENTRIES (16 * 1024) 54 62 #define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES) 55 63 #define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1) ··· 269 261 270 262 u16 pppoe_id; 271 263 u16 src_mac_lo; 264 + 265 + u32 meter; 272 266 }; 273 267 274 268 #define AIROHA_FOE_IB1_UNBIND_PREBIND BIT(24) ··· 305 295 #define AIROHA_FOE_DPI BIT(7) 306 296 #define AIROHA_FOE_TUNNEL BIT(6) 307 297 #define AIROHA_FOE_TUNNEL_ID GENMASK(5, 0) 298 + 299 + #define AIROHA_FOE_TUNNEL_MTU GENMASK(31, 16) 300 + #define AIROHA_FOE_ACNT_GRP3 GENMASK(15, 9) 301 + #define AIROHA_FOE_METER_GRP3 GENMASK(8, 5) 302 + #define AIROHA_FOE_METER_GRP2 GENMASK(4, 0) 308 303 309 304 struct airoha_foe_bridge { 310 305 u32 dest_mac_hi; ··· 394 379 u32 ib2; 395 380 396 381 struct airoha_foe_mac_info_common l2; 382 + 383 + u32 meter; 397 384 }; 398 385 399 386 struct airoha_foe_entry { ··· 412 395 }; 413 396 u8 data[PPE_ENTRY_SIZE]; 414 397 }; 398 + }; 399 + 400 + struct airoha_foe_stats { 401 + u32 bytes; 402 + u32 packets; 403 + }; 404 + 405 + struct airoha_foe_stats64 { 406 + u64 bytes; 407 + u64 packets; 415 408 }; 416 409 417 410 struct airoha_flow_data { ··· 474 447 struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */ 475 448 u32 hash; 476 449 450 + struct airoha_foe_stats64 stats; 477 451 enum airoha_flow_entry_type type; 478 452 479 453 struct rhash_head node; ··· 551 523 struct hlist_head *foe_flow; 552 524 u16 foe_check_time[PPE_NUM_ENTRIES]; 553 525 526 + struct airoha_foe_stats *foe_stats; 527 + dma_addr_t foe_stats_dma; 528 + 554 529 struct dentry *debugfs_dir; 555 530 }; 556 531 ··· 613 582 void airoha_ppe_deinit(struct airoha_eth *eth); 614 583 struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, 615 584 u32 hash); 585 + void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, 586 + struct airoha_foe_stats64 *stats); 616 587 617 588 #ifdef CONFIG_DEBUG_FS 618 589 int airoha_ppe_debugfs_init(struct airoha_ppe *ppe);
+123 -55
drivers/net/ethernet/airoha/airoha_npu.c
··· 12 12 #include <linux/of_reserved_mem.h> 13 13 #include <linux/regmap.h> 14 14 15 + #include "airoha_eth.h" 15 16 #include "airoha_npu.h" 16 17 17 18 #define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin" ··· 73 72 PPE_FUNC_SET_WAIT_HWNAT_INIT, 74 73 PPE_FUNC_SET_WAIT_HWNAT_DEINIT, 75 74 PPE_FUNC_SET_WAIT_API, 75 + PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP, 76 76 }; 77 77 78 78 enum { ··· 117 115 u32 size; 118 116 u32 data; 119 117 } set_info; 118 + struct { 119 + u32 npu_stats_addr; 120 + u32 foe_stats_addr; 121 + } stats_info; 120 122 }; 121 123 }; 122 124 ··· 130 124 u16 core = 0; /* FIXME */ 131 125 u32 val, offset = core << 4; 132 126 dma_addr_t dma_addr; 133 - void *addr; 134 127 int ret; 135 128 136 - addr = kmemdup(p, size, GFP_ATOMIC); 137 - if (!addr) 138 - return -ENOMEM; 139 - 140 - dma_addr = dma_map_single(npu->dev, addr, size, DMA_TO_DEVICE); 129 + dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE); 141 130 ret = dma_mapping_error(npu->dev, dma_addr); 142 131 if (ret) 143 - goto out; 132 + return ret; 144 133 145 134 spin_lock_bh(&npu->cores[core].lock); 146 135 ··· 156 155 spin_unlock_bh(&npu->cores[core].lock); 157 156 158 157 dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE); 159 - out: 160 - kfree(addr); 161 158 162 159 return ret; 163 160 } ··· 260 261 261 262 static int airoha_npu_ppe_init(struct airoha_npu *npu) 262 263 { 263 - struct ppe_mbox_data ppe_data = { 264 - .func_type = NPU_OP_SET, 265 - .func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT, 266 - .init_info = { 267 - .ppe_type = PPE_TYPE_L2B_IPV4_IPV6, 268 - .wan_mode = QDMA_WAN_ETHER, 269 - }, 270 - }; 264 + struct ppe_mbox_data *ppe_data; 265 + int err; 271 266 272 - return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, 273 - sizeof(struct ppe_mbox_data)); 267 + ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); 268 + if (!ppe_data) 269 + return -ENOMEM; 270 + 271 + ppe_data->func_type = NPU_OP_SET; 272 + ppe_data->func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT; 273 + ppe_data->init_info.ppe_type = PPE_TYPE_L2B_IPV4_IPV6; 274 + ppe_data->init_info.wan_mode = QDMA_WAN_ETHER; 275 + 276 + err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, 277 + sizeof(*ppe_data)); 278 + kfree(ppe_data); 279 + 280 + return err; 274 281 } 275 282 276 283 static int airoha_npu_ppe_deinit(struct airoha_npu *npu) 277 284 { 278 - struct ppe_mbox_data ppe_data = { 279 - .func_type = NPU_OP_SET, 280 - .func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT, 281 - }; 285 + struct ppe_mbox_data *ppe_data; 286 + int err; 282 287 283 - return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, 284 - sizeof(struct ppe_mbox_data)); 288 + ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); 289 + if (!ppe_data) 290 + return -ENOMEM; 291 + 292 + ppe_data->func_type = NPU_OP_SET; 293 + ppe_data->func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT; 294 + 295 + err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, 296 + sizeof(*ppe_data)); 297 + kfree(ppe_data); 298 + 299 + return err; 285 300 } 286 301 287 302 static int airoha_npu_ppe_flush_sram_entries(struct airoha_npu *npu, 288 303 dma_addr_t foe_addr, 289 304 int sram_num_entries) 290 305 { 291 - struct ppe_mbox_data ppe_data = { 292 - .func_type = NPU_OP_SET, 293 - .func_id = PPE_FUNC_SET_WAIT_API, 294 - .set_info = { 295 - .func_id = PPE_SRAM_RESET_VAL, 296 - .data = foe_addr, 297 - .size = sram_num_entries, 298 - }, 299 - }; 306 + struct ppe_mbox_data *ppe_data; 307 + int err; 300 308 301 - return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, 302 - sizeof(struct ppe_mbox_data)); 309 + ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); 310 + if (!ppe_data) 311 + return -ENOMEM; 312 + 313 + ppe_data->func_type = NPU_OP_SET; 314 + ppe_data->func_id = PPE_FUNC_SET_WAIT_API; 315 + ppe_data->set_info.func_id = PPE_SRAM_RESET_VAL; 316 + ppe_data->set_info.data = foe_addr; 317 + ppe_data->set_info.size = sram_num_entries; 318 + 319 + err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, 320 + sizeof(*ppe_data)); 321 + kfree(ppe_data); 322 + 323 + return err; 303 324 } 304 325 305 326 static int airoha_npu_foe_commit_entry(struct airoha_npu *npu, 306 327 dma_addr_t foe_addr, 307 328 u32 entry_size, u32 hash, bool ppe2) 308 329 { 309 - struct ppe_mbox_data ppe_data = { 310 - .func_type = NPU_OP_SET, 311 - .func_id = PPE_FUNC_SET_WAIT_API, 312 - .set_info = { 313 - .data = foe_addr, 314 - .size = entry_size, 315 - }, 316 - }; 330 + struct ppe_mbox_data *ppe_data; 317 331 int err; 318 332 319 - ppe_data.set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY 320 - : PPE_SRAM_SET_ENTRY; 333 + ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC); 334 + if (!ppe_data) 335 + return -ENOMEM; 321 336 322 - err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, 323 - sizeof(struct ppe_mbox_data)); 337 + ppe_data->func_type = NPU_OP_SET; 338 + ppe_data->func_id = PPE_FUNC_SET_WAIT_API; 339 + ppe_data->set_info.data = foe_addr; 340 + ppe_data->set_info.size = entry_size; 341 + ppe_data->set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY 342 + : PPE_SRAM_SET_ENTRY; 343 + 344 + err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, 345 + sizeof(*ppe_data)); 324 346 if (err) 325 - return err; 347 + goto out; 326 348 327 - ppe_data.set_info.func_id = PPE_SRAM_SET_VAL; 328 - ppe_data.set_info.data = hash; 329 - ppe_data.set_info.size = sizeof(u32); 349 + ppe_data->set_info.func_id = PPE_SRAM_SET_VAL; 350 + ppe_data->set_info.data = hash; 351 + ppe_data->set_info.size = sizeof(u32); 330 352 331 - return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, 332 - sizeof(struct ppe_mbox_data)); 353 + err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, 354 + sizeof(*ppe_data)); 355 + out: 356 + kfree(ppe_data); 357 + 358 + return err; 333 359 } 334 360 335 - struct airoha_npu *airoha_npu_get(struct device *dev) 361 + static int airoha_npu_stats_setup(struct airoha_npu *npu, 362 + dma_addr_t foe_stats_addr) 363 + { 364 + int err, size = PPE_STATS_NUM_ENTRIES * sizeof(*npu->stats); 365 + struct ppe_mbox_data *ppe_data; 366 + 367 + if (!size) /* flow stats are disabled */ 368 + return 0; 369 + 370 + ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC); 371 + if (!ppe_data) 372 + return -ENOMEM; 373 + 374 + ppe_data->func_type = NPU_OP_SET; 375 + ppe_data->func_id = PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP; 376 + ppe_data->stats_info.foe_stats_addr = foe_stats_addr; 377 + 378 + err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, 379 + sizeof(*ppe_data)); 380 + if (err) 381 + goto out; 382 + 383 + npu->stats = devm_ioremap(npu->dev, 384 + ppe_data->stats_info.npu_stats_addr, 385 + size); 386 + if (!npu->stats) 387 + err = -ENOMEM; 388 + out: 389 + kfree(ppe_data); 390 + 391 + return err; 392 + } 393 + 394 + struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) 336 395 { 337 396 struct platform_device *pdev; 338 397 struct device_node *np; ··· 426 369 dev_name(dev)); 427 370 npu = ERR_PTR(-EINVAL); 428 371 goto error_module_put; 372 + } 373 + 374 + if (stats_addr) { 375 + int err; 376 + 377 + err = airoha_npu_stats_setup(npu, *stats_addr); 378 + if (err) { 379 + dev_err(dev, "failed to allocate npu stats buffer\n"); 380 + npu = ERR_PTR(err); 381 + goto error_module_put; 382 + } 429 383 } 430 384 431 385 return npu;
+3 -1
drivers/net/ethernet/airoha/airoha_npu.h
··· 17 17 struct work_struct wdt_work; 18 18 } cores[NPU_NUM_CORES]; 19 19 20 + struct airoha_foe_stats __iomem *stats; 21 + 20 22 struct { 21 23 int (*ppe_init)(struct airoha_npu *npu); 22 24 int (*ppe_deinit)(struct airoha_npu *npu); ··· 32 30 } ops; 33 31 }; 34 32 35 - struct airoha_npu *airoha_npu_get(struct device *dev); 33 + struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr); 36 34 void airoha_npu_put(struct airoha_npu *npu);
+254 -16
drivers/net/ethernet/airoha/airoha_ppe.c
··· 84 84 85 85 airoha_fe_rmw(eth, REG_PPE_TB_CFG(i), 86 86 PPE_TB_CFG_SEARCH_MISS_MASK | 87 + PPE_TB_CFG_KEEPALIVE_MASK | 87 88 PPE_TB_ENTRY_SIZE_MASK, 88 89 FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) | 89 90 FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0)); ··· 103 102 104 103 if (airoha_ppe2_is_enabled(eth)) { 105 104 sram_num_entries = 106 - PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_ENTRIES); 105 + PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_DATA_ENTRIES); 107 106 airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), 108 107 PPE_SRAM_TB_NUM_ENTRY_MASK | 109 108 PPE_DRAM_TB_NUM_ENTRY_MASK, ··· 120 119 dram_num_entries)); 121 120 } else { 122 121 sram_num_entries = 123 - PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_ENTRIES); 122 + PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_DATA_ENTRIES); 124 123 airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), 125 124 PPE_SRAM_TB_NUM_ENTRY_MASK | 126 125 PPE_DRAM_TB_NUM_ENTRY_MASK, ··· 418 417 return hash; 419 418 } 420 419 420 + static u32 airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, u32 hash) 421 + { 422 + if (!airoha_ppe2_is_enabled(ppe->eth)) 423 + return hash; 424 + 425 + return hash >= PPE_STATS_NUM_ENTRIES ? hash - PPE1_STATS_NUM_ENTRIES 426 + : hash; 427 + } 428 + 429 + static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe, 430 + struct airoha_npu *npu, 431 + int index) 432 + { 433 + memset_io(&npu->stats[index], 0, sizeof(*npu->stats)); 434 + memset(&ppe->foe_stats[index], 0, sizeof(*ppe->foe_stats)); 435 + } 436 + 437 + static void airoha_ppe_foe_flow_stats_reset(struct airoha_ppe *ppe, 438 + struct airoha_npu *npu) 439 + { 440 + int i; 441 + 442 + for (i = 0; i < PPE_STATS_NUM_ENTRIES; i++) 443 + airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, i); 444 + } 445 + 446 + static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe, 447 + struct airoha_npu *npu, 448 + struct airoha_foe_entry *hwe, 449 + u32 hash) 450 + { 451 + int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); 452 + u32 index, pse_port, val, *data, *ib2, *meter; 453 + u8 nbq; 454 + 455 + index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); 456 + if (index >= PPE_STATS_NUM_ENTRIES) 457 + return; 458 + 459 + if (type == PPE_PKT_TYPE_BRIDGE) { 460 + data = &hwe->bridge.data; 461 + ib2 = &hwe->bridge.ib2; 462 + meter = &hwe->bridge.l2.meter; 463 + } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { 464 + data = &hwe->ipv6.data; 465 + ib2 = &hwe->ipv6.ib2; 466 + meter = &hwe->ipv6.meter; 467 + } else { 468 + data = &hwe->ipv4.data; 469 + ib2 = &hwe->ipv4.ib2; 470 + meter = &hwe->ipv4.l2.meter; 471 + } 472 + 473 + airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, index); 474 + 475 + val = FIELD_GET(AIROHA_FOE_CHANNEL | AIROHA_FOE_QID, *data); 476 + *data = (*data & ~AIROHA_FOE_ACTDP) | 477 + FIELD_PREP(AIROHA_FOE_ACTDP, val); 478 + 479 + val = *ib2 & (AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | 480 + AIROHA_FOE_IB2_PSE_QOS | AIROHA_FOE_IB2_FAST_PATH); 481 + *meter |= FIELD_PREP(AIROHA_FOE_TUNNEL_MTU, val); 482 + 483 + pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2); 484 + nbq = pse_port == 1 ? 6 : 5; 485 + *ib2 &= ~(AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | 486 + AIROHA_FOE_IB2_PSE_QOS); 487 + *ib2 |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, 6) | 488 + FIELD_PREP(AIROHA_FOE_IB2_NBQ, nbq); 489 + } 490 + 421 491 struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, 422 492 u32 hash) 423 493 { ··· 542 470 struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); 543 471 u32 ts = airoha_ppe_get_timestamp(ppe); 544 472 struct airoha_eth *eth = ppe->eth; 473 + struct airoha_npu *npu; 474 + int err = 0; 545 475 546 476 memcpy(&hwe->d, &e->d, sizeof(*hwe) - sizeof(hwe->ib1)); 547 477 wmb(); ··· 552 478 e->ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_TIMESTAMP, ts); 553 479 hwe->ib1 = e->ib1; 554 480 481 + rcu_read_lock(); 482 + 483 + npu = rcu_dereference(eth->npu); 484 + if (!npu) { 485 + err = -ENODEV; 486 + goto unlock; 487 + } 488 + 489 + airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); 490 + 555 491 if (hash < PPE_SRAM_NUM_ENTRIES) { 556 492 dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); 557 493 bool ppe2 = airoha_ppe2_is_enabled(eth) && 558 494 hash >= PPE1_SRAM_NUM_ENTRIES; 559 - struct airoha_npu *npu; 560 - int err = -ENODEV; 561 495 562 - rcu_read_lock(); 563 - npu = rcu_dereference(eth->npu); 564 - if (npu) 565 - err = npu->ops.ppe_foe_commit_entry(npu, addr, 566 - sizeof(*hwe), hash, 567 - ppe2); 568 - rcu_read_unlock(); 569 - 570 - return err; 496 + err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe), 497 + hash, ppe2); 571 498 } 499 + unlock: 500 + rcu_read_unlock(); 572 501 573 - return 0; 502 + return err; 574 503 } 575 504 576 505 static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe, ··· 659 582 l2->common.etype = ETH_P_IPV6; 660 583 661 584 hwe.bridge.ib2 = e->data.bridge.ib2; 585 + hwe.bridge.data = e->data.bridge.data; 662 586 airoha_ppe_foe_commit_entry(ppe, &hwe, hash); 663 587 664 588 return 0; ··· 757 679 spin_unlock_bh(&ppe_lock); 758 680 759 681 return 0; 682 + } 683 + 684 + static int airoha_ppe_get_entry_idle_time(struct airoha_ppe *ppe, u32 ib1) 685 + { 686 + u32 state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); 687 + u32 ts, ts_mask, now = airoha_ppe_get_timestamp(ppe); 688 + int idle; 689 + 690 + if (state == AIROHA_FOE_STATE_BIND) { 691 + ts = FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, ib1); 692 + ts_mask = AIROHA_FOE_IB1_BIND_TIMESTAMP; 693 + } else { 694 + ts = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, ib1); 695 + now = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, now); 696 + ts_mask = AIROHA_FOE_IB1_UNBIND_TIMESTAMP; 697 + } 698 + idle = now - ts; 699 + 700 + return idle < 0 ? idle + ts_mask + 1 : idle; 701 + } 702 + 703 + static void 704 + airoha_ppe_foe_flow_l2_entry_update(struct airoha_ppe *ppe, 705 + struct airoha_flow_table_entry *e) 706 + { 707 + int min_idle = airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); 708 + struct airoha_flow_table_entry *iter; 709 + struct hlist_node *n; 710 + 711 + lockdep_assert_held(&ppe_lock); 712 + 713 + hlist_for_each_entry_safe(iter, n, &e->l2_flows, l2_subflow_node) { 714 + struct airoha_foe_entry *hwe; 715 + u32 ib1, state; 716 + int idle; 717 + 718 + hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); 719 + ib1 = READ_ONCE(hwe->ib1); 720 + 721 + state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); 722 + if (state != AIROHA_FOE_STATE_BIND) { 723 + iter->hash = 0xffff; 724 + airoha_ppe_foe_remove_flow(ppe, iter); 725 + continue; 726 + } 727 + 728 + idle = airoha_ppe_get_entry_idle_time(ppe, ib1); 729 + if (idle >= min_idle) 730 + continue; 731 + 732 + min_idle = idle; 733 + e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_TIMESTAMP; 734 + e->data.ib1 |= ib1 & AIROHA_FOE_IB1_BIND_TIMESTAMP; 735 + } 736 + } 737 + 738 + static void airoha_ppe_foe_flow_entry_update(struct airoha_ppe *ppe, 739 + struct airoha_flow_table_entry *e) 740 + { 741 + struct airoha_foe_entry *hwe_p, hwe = {}; 742 + 743 + spin_lock_bh(&ppe_lock); 744 + 745 + if (e->type == FLOW_TYPE_L2) { 746 + airoha_ppe_foe_flow_l2_entry_update(ppe, e); 747 + goto unlock; 748 + } 749 + 750 + if (e->hash == 0xffff) 751 + goto unlock; 752 + 753 + hwe_p = airoha_ppe_foe_get_entry(ppe, e->hash); 754 + if (!hwe_p) 755 + goto unlock; 756 + 757 + memcpy(&hwe, hwe_p, sizeof(*hwe_p)); 758 + if (!airoha_ppe_foe_compare_entry(e, &hwe)) { 759 + e->hash = 0xffff; 760 + goto unlock; 761 + } 762 + 763 + e->data.ib1 = hwe.ib1; 764 + unlock: 765 + spin_unlock_bh(&ppe_lock); 766 + } 767 + 768 + static int airoha_ppe_entry_idle_time(struct airoha_ppe *ppe, 769 + struct airoha_flow_table_entry *e) 770 + { 771 + airoha_ppe_foe_flow_entry_update(ppe, e); 772 + 773 + return airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); 760 774 } 761 775 762 776 static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, ··· 1066 896 return 0; 1067 897 } 1068 898 899 + void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, 900 + struct airoha_foe_stats64 *stats) 901 + { 902 + u32 index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); 903 + struct airoha_eth *eth = ppe->eth; 904 + struct airoha_npu *npu; 905 + 906 + if (index >= PPE_STATS_NUM_ENTRIES) 907 + return; 908 + 909 + rcu_read_lock(); 910 + 911 + npu = rcu_dereference(eth->npu); 912 + if (npu) { 913 + u64 packets = ppe->foe_stats[index].packets; 914 + u64 bytes = ppe->foe_stats[index].bytes; 915 + struct airoha_foe_stats npu_stats; 916 + 917 + memcpy_fromio(&npu_stats, &npu->stats[index], 918 + sizeof(*npu->stats)); 919 + stats->packets = packets << 32 | npu_stats.packets; 920 + stats->bytes = bytes << 32 | npu_stats.bytes; 921 + } 922 + 923 + rcu_read_unlock(); 924 + } 925 + 926 + static int airoha_ppe_flow_offload_stats(struct airoha_gdm_port *port, 927 + struct flow_cls_offload *f) 928 + { 929 + struct airoha_eth *eth = port->qdma->eth; 930 + struct airoha_flow_table_entry *e; 931 + u32 idle; 932 + 933 + e = rhashtable_lookup(&eth->flow_table, &f->cookie, 934 + airoha_flow_table_params); 935 + if (!e) 936 + return -ENOENT; 937 + 938 + idle = airoha_ppe_entry_idle_time(eth->ppe, e); 939 + f->stats.lastused = jiffies - idle * HZ; 940 + 941 + if (e->hash != 0xffff) { 942 + struct airoha_foe_stats64 stats = {}; 943 + 944 + airoha_ppe_foe_entry_get_stats(eth->ppe, e->hash, &stats); 945 + f->stats.pkts += (stats.packets - e->stats.packets); 946 + f->stats.bytes += (stats.bytes - e->stats.bytes); 947 + e->stats = stats; 948 + } 949 + 950 + return 0; 951 + } 952 + 1069 953 static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port, 1070 954 struct flow_cls_offload *f) 1071 955 { ··· 1128 904 return airoha_ppe_flow_offload_replace(port, f); 1129 905 case FLOW_CLS_DESTROY: 1130 906 return airoha_ppe_flow_offload_destroy(port, f); 907 + case FLOW_CLS_STATS: 908 + return airoha_ppe_flow_offload_stats(port, f); 1131 909 default: 1132 910 break; 1133 911 } ··· 1155 929 1156 930 static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) 1157 931 { 1158 - struct airoha_npu *npu = airoha_npu_get(eth->dev); 932 + struct airoha_npu *npu = airoha_npu_get(eth->dev, 933 + &eth->ppe->foe_stats_dma); 1159 934 1160 935 if (IS_ERR(npu)) { 1161 936 request_module("airoha-npu"); 1162 - npu = airoha_npu_get(eth->dev); 937 + npu = airoha_npu_get(eth->dev, &eth->ppe->foe_stats_dma); 1163 938 } 1164 939 1165 940 return npu; ··· 1182 955 err = airoha_ppe_flush_sram_entries(eth->ppe, npu); 1183 956 if (err) 1184 957 goto error_npu_put; 958 + 959 + airoha_ppe_foe_flow_stats_reset(eth->ppe, npu); 1185 960 1186 961 rcu_assign_pointer(eth->npu, npu); 1187 962 synchronize_rcu(); ··· 1255 1026 GFP_KERNEL); 1256 1027 if (!ppe->foe_flow) 1257 1028 return -ENOMEM; 1029 + 1030 + foe_size = PPE_STATS_NUM_ENTRIES * sizeof(*ppe->foe_stats); 1031 + if (foe_size) { 1032 + ppe->foe_stats = dmam_alloc_coherent(eth->dev, foe_size, 1033 + &ppe->foe_stats_dma, 1034 + GFP_KERNEL); 1035 + if (!ppe->foe_stats) 1036 + return -ENOMEM; 1037 + } 1258 1038 1259 1039 err = rhashtable_init(&eth->flow_table, &airoha_flow_table_params); 1260 1040 if (err)
+7 -2
drivers/net/ethernet/airoha/airoha_ppe_debugfs.c
··· 61 61 u16 *src_port = NULL, *dest_port = NULL; 62 62 struct airoha_foe_mac_info_common *l2; 63 63 unsigned char h_source[ETH_ALEN] = {}; 64 + struct airoha_foe_stats64 stats = {}; 64 65 unsigned char h_dest[ETH_ALEN]; 65 66 struct airoha_foe_entry *hwe; 66 67 u32 type, state, ib2, data; ··· 145 144 cpu_to_be16(hwe->ipv4.l2.src_mac_lo); 146 145 } 147 146 147 + airoha_ppe_foe_entry_get_stats(ppe, i, &stats); 148 + 148 149 *((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi); 149 150 *((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo); 150 151 *((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi); 151 152 152 153 seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x" 153 - " vlan=%d,%d ib1=%08x ib2=%08x\n", 154 + " vlan=%d,%d ib1=%08x ib2=%08x" 155 + " packets=%llu bytes=%llu\n", 154 156 h_source, h_dest, l2->etype, data, 155 - l2->vlan1, l2->vlan2, hwe->ib1, ib2); 157 + l2->vlan1, l2->vlan2, hwe->ib1, ib2, 158 + stats.packets, stats.bytes); 156 159 } 157 160 158 161 return 0;