diff --git a/e2e_test/phases/phase16_rcu_concurrency.sh b/e2e_test/phases/phase16_rcu_concurrency.sh index 86017f4..9b9b571 100644 --- a/e2e_test/phases/phase16_rcu_concurrency.sh +++ b/e2e_test/phases/phase16_rcu_concurrency.sh @@ -1,19 +1,12 @@ #!/usr/bin/env bash # shellcheck shell=bash # --------------------------------------------------------------------------- -# Phase 16 — RCU reader/writer concurrency under data-plane load +# Phase 16 — subscriber-count churn under data-plane load # -# Exercises the per-lcore persistent-online RCU getter fast path: a high-rate -# iperf3 flow keeps the data-plane lcores hammering PPPD_GET_CCB / DHCP / stats -# getters (RCU readers on the fast path, reporting quiescent once per burst), -# while SetSubscriberCount is fired repeatedly — each call makes fastrg realloc -# the ppp_ccb / dhcp_ccb / per_subscriber_stats arrays and run -# rte_rcu_qsbr_synchronize (the RCU writer). -# -# If fast-path readers stopped reporting quiescent, synchronize would block and -# SetSubscriberCount would hang; if a reader saw a freed array the node would -# crash. Both steps passing under sustained load confirms reader/writer -# concurrency is correct. +# A high-rate iperf3 flow keeps the data-plane lcores busy while +# SetSubscriberCount is fired repeatedly; each change enables or disables +# preallocated subscriber slots on the control thread. The node must not hang +# or crash, and the data plane must keep forwarding afterwards. # --------------------------------------------------------------------------- # Helper: restore the subscriber count churned by the Step 67 loop below + # stop the iperf3 server. Idempotent (set_subscriber_count/pkill both tolerate diff --git a/src/dp.c b/src/dp.c index aecfe59..9095ce2 100644 --- a/src/dp.c +++ b/src/dp.c @@ -255,8 +255,6 @@ static struct rte_eth_conf port_conf_default = { .intr_conf = { .lsc = 1, /**< link status interrupt feature enabled */ }, }; -/* Per-lcore persistent-online flag for the RCU getter fast path (see fastrg.h). */ -BOOL fastrg_rcu_persistent[RTE_MAX_LCORE]; static int lsi_event_callback(U16 port_id, enum rte_eth_event_type type, void *param); @@ -2227,11 +2225,10 @@ void wan_ctrl_tx(FastRG_t *fastrg_ccb, U16 ccb_id, U8 *mu, U16 mulen) pkt->pkt_len = mulen; count_tx_packet(fastrg_ccb, pkt, WAN_PORT, ccb_id); unsigned int lcore_id = rte_lcore_id(); - /* Control TX callers must run on an EAL lcore. Persistent data-plane - * lcores are already pdump_rcu-online. Callback removal does not wait, so - * this window covers ctrl_thread's queue-0 TX burst in teardown's + /* Control TX callers must run on an EAL lcore. Callback removal does not + * wait, so this window covers ctrl_thread's queue-0 TX burst in teardown's * synchronize-before-free grace period. */ - BOOL rcu_toggle = (lcore_id != LCORE_ID_ANY) && !fastrg_rcu_persistent[lcore_id]; + BOOL rcu_toggle = (lcore_id != LCORE_ID_ANY); if (rcu_toggle) rte_rcu_qsbr_thread_online(fastrg_ccb->pdump_rcu, lcore_id); U16 cp_tx_q = fastrg_ccb->dp_ctrl_txq_cp[WAN_PORT]; @@ -2271,11 +2268,10 @@ void lan_ctrl_tx(FastRG_t *fastrg_ccb, U16 ccb_id, U8 *mu, U16 mulen) pkt->pkt_len = mulen; count_tx_packet(fastrg_ccb, pkt, LAN_PORT, ccb_id); unsigned int lcore_id = rte_lcore_id(); - /* Control TX callers must run on an EAL lcore. Persistent data-plane - * lcores are already pdump_rcu-online. Callback removal does not wait, so - * this window covers ctrl_thread's queue-0 TX burst in teardown's + /* Control TX callers must run on an EAL lcore. Callback removal does not + * wait, so this window covers ctrl_thread's queue-0 TX burst in teardown's * synchronize-before-free grace period. */ - BOOL rcu_toggle = (lcore_id != LCORE_ID_ANY) && !fastrg_rcu_persistent[lcore_id]; + BOOL rcu_toggle = (lcore_id != LCORE_ID_ANY); if (rcu_toggle) rte_rcu_qsbr_thread_online(fastrg_ccb->pdump_rcu, lcore_id); U16 cp_tx_q = fastrg_ccb->dp_ctrl_txq_cp[LAN_PORT]; diff --git a/src/dp.h b/src/dp.h index e943fb1..8910bdc 100644 --- a/src/dp.h +++ b/src/dp.h @@ -532,7 +532,6 @@ static inline void fastrg_rcu_dp_online(FastRG_t *fastrg_ccb) static inline void fastrg_rcu_dp_register(FastRG_t *fastrg_ccb) { fastrg_rcu_dp_online(fastrg_ccb); - fastrg_rcu_persistent[rte_lcore_id()] = TRUE; /* online first, then flip flag */ } /** @@ -557,8 +556,8 @@ static inline void fastrg_rcu_dp_quiescent(FastRG_t *fastrg_ccb) /** * @fn fastrg_rcu_dp_unregister * - * @brief Report QSBR-offline on both RCUs for the calling data-plane lcore - * and clear its persistent flag. MUST be called once when the thread + * @brief Report QSBR-offline on both RCUs for the calling data-plane lcore. + * MUST be called once when the thread * leaves its poll loop (on stop_flag), before returning. Without this * the thread exits while still recorded online, and the cleanup-path * rte_rcu_qsbr_synchronize() waits on it forever (it never reports @@ -570,7 +569,6 @@ static inline void fastrg_rcu_dp_quiescent(FastRG_t *fastrg_ccb) */ static inline void fastrg_rcu_dp_unregister(FastRG_t *fastrg_ccb) { - fastrg_rcu_persistent[rte_lcore_id()] = FALSE; /* flag first, then go offline */ fastrg_rcu_dp_offline(fastrg_ccb); } diff --git a/src/fastrg.h b/src/fastrg.h index 9671af2..679585b 100644 --- a/src/fastrg.h +++ b/src/fastrg.h @@ -49,13 +49,6 @@ enum { extern rte_atomic16_t stop_flag; extern rte_atomic16_t start_flag; -/* Set TRUE per data-plane lcore at thread startup (fastrg_rcu_dp_register). - * When set, the RCU ccb/stats getters take a lean load-only fast path: the lcore - * stays QSBR-online for its whole life and reports quiescent once per burst, - * instead of entering/exiting a critical section on every getter call. - * Control-plane / metrics threads leave it FALSE and use the full path. */ -extern BOOL fastrg_rcu_persistent[RTE_MAX_LCORE]; - #define NIC_MODEL_MAX_LEN 128 struct nic_info { diff --git a/unit_test/dnsd/dnsd_test.c b/unit_test/dnsd/dnsd_test.c index a4cd63a..fb685a1 100644 --- a/unit_test/dnsd/dnsd_test.c +++ b/unit_test/dnsd/dnsd_test.c @@ -333,7 +333,6 @@ static void dns_tcp_env_init(FastRG_t *fastrg_ccb) } memset(fastrg_ccb->per_subscriber_stats, 0, sizeof(fastrg_ccb->per_subscriber_stats)); - fastrg_rcu_persistent[rte_lcore_id()] = TRUE; } static void dns_tcp_ctx_init(dns_tcp_ctx_t *c, FastRG_t *fastrg_ccb) @@ -765,7 +764,6 @@ static void dns_udp_env_init(FastRG_t *fastrg_ccb) memset(fastrg_ccb->per_subscriber_stats, 0, sizeof(fastrg_ccb->per_subscriber_stats)); fastrg_ccb->per_subscriber_stats[rte_lcore_id()][WAN_PORT] = &dns_udp_wan_stats; - fastrg_rcu_persistent[rte_lcore_id()] = TRUE; } static void dns_udp_packet_init(dns_udp_ctx_t *c, FastRG_t *fastrg_ccb) diff --git a/unit_test/dp_test.c b/unit_test/dp_test.c index 37a8739..a0942f9 100644 --- a/unit_test/dp_test.c +++ b/unit_test/dp_test.c @@ -208,7 +208,6 @@ static void test_send2cp(FastRG_t *fastrg_ccb) tFastRG_MBX *received = NULL; void *object = NULL; unsigned int lcore_id = rte_lcore_id(); - BOOL old_persistent; struct per_ccb_stats *old_lan_stats; struct per_ccb_stats *old_wan_stats; unsigned int available_before; @@ -218,10 +217,8 @@ static void test_send2cp(FastRG_t *fastrg_ccb) printf("\nTesting send2cp function:\n"); TEST_ASSERT(lcore_id != LCORE_ID_ANY, "send2cp runs on an EAL lcore", "unit test should run on EAL lcore 0"); - old_persistent = fastrg_rcu_persistent[lcore_id]; old_lan_stats = fastrg_ccb->per_subscriber_stats[lcore_id][LAN_PORT]; old_wan_stats = fastrg_ccb->per_subscriber_stats[lcore_id][WAN_PORT]; - fastrg_rcu_persistent[lcore_id] = TRUE; fastrg_ccb->per_subscriber_stats[lcore_id][LAN_PORT] = NULL; fastrg_ccb->per_subscriber_stats[lcore_id][WAN_PORT] = NULL; @@ -282,7 +279,6 @@ static void test_send2cp(FastRG_t *fastrg_ccb) fastrg_ccb->free_mail_ring = old_free_mail_ring; fastrg_ccb->per_subscriber_stats[lcore_id][LAN_PORT] = old_lan_stats; fastrg_ccb->per_subscriber_stats[lcore_id][WAN_PORT] = old_wan_stats; - fastrg_rcu_persistent[lcore_id] = old_persistent; } /* Two lcores writing one TX queue corrupt its ring, so the layout must never diff --git a/unit_test/pppd/codec_test.c b/unit_test/pppd/codec_test.c index dc624da..98b4bd0 100644 --- a/unit_test/pppd/codec_test.c +++ b/unit_test/pppd/codec_test.c @@ -1443,12 +1443,10 @@ static void decode_env_init(FastRG_t *fastrg_ccb) } /* The mock FastRG_t is malloc'd, so the per-lcore stats grid is garbage — - * wipe it and install one WAN row. The persistent-RCU flag makes the - * stats getter skip the (unset) stats qsbr on this lcore. */ + * wipe it and install one WAN row. */ memset(fastrg_ccb->per_subscriber_stats, 0, sizeof(fastrg_ccb->per_subscriber_stats)); fastrg_ccb->per_subscriber_stats[rte_lcore_id()][WAN_PORT] = &decode_wan_stats; - fastrg_rcu_persistent[rte_lcore_id()] = TRUE; } static void decode_ccb_reset(FastRG_t *fastrg_ccb, U8 phase) diff --git a/unit_test/pppd/pppd_test.c b/unit_test/pppd/pppd_test.c index 69ad9e9..20f1547 100644 --- a/unit_test/pppd/pppd_test.c +++ b/unit_test/pppd/pppd_test.c @@ -50,7 +50,6 @@ static void pppd_env_init(FastRG_t *fastrg_ccb) } memset(fastrg_ccb->per_subscriber_stats, 0, sizeof(fastrg_ccb->per_subscriber_stats)); - fastrg_rcu_persistent[rte_lcore_id()] = TRUE; rte_timer_subsystem_init(); fastrg_ccb->lcore.ctrl_thread = rte_lcore_id(); diff --git a/unit_test/test.c b/unit_test/test.c index 6c1a91c..704cf68 100644 --- a/unit_test/test.c +++ b/unit_test/test.c @@ -20,6 +20,7 @@ void free_ccb(FastRG_t *ccb) } if (ccb->ppp_ccb) fastrg_mfree(ccb->ppp_ccb); if (ccb->dhcp_ccb) fastrg_mfree(ccb->dhcp_ccb); + if (ccb->pdump_rcu) fastrg_mfree(ccb->pdump_rcu); fastrg_mfree(ccb); } @@ -57,6 +58,18 @@ FastRG_t *init_ccb(int user_count) rte_rcu_qsbr_thread_register(ppp_rcu, 0); ccb->ppp_ccb_rcu = ppp_rcu; + /* ---- pdump_rcu ---------------------------------------------------- */ + ccb->pdump_rcu = fastrg_calloc(struct rte_rcu_qsbr, 1, rcu_sz, RTE_CACHE_LINE_SIZE); + if (ccb->pdump_rcu == NULL) + goto err; + if (rte_rcu_qsbr_init(ccb->pdump_rcu, RTE_MAX_LCORE) != 0) + goto err; + unsigned int lcore_id; + RTE_LCORE_FOREACH(lcore_id) { + if (rte_rcu_qsbr_thread_register(ccb->pdump_rcu, lcore_id) != 0) + goto err; + } + /* ---- ppp_ccb pointer array + individual CCBs --------------------- */ ccb->ppp_ccb = fastrg_calloc(ppp_ccb_t *, user_count, sizeof(ppp_ccb_t *), 0); if (ccb->ppp_ccb == NULL) goto err;