From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id B66BFA0542; Wed, 5 Oct 2022 11:20:40 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 32F0C42B71; Wed, 5 Oct 2022 11:20:13 +0200 (CEST) Received: from EUR02-VE1-obe.outbound.protection.outlook.com (mail-eopbgr20075.outbound.protection.outlook.com [40.107.2.75]) by mails.dpdk.org (Postfix) with ESMTP id 074FB40694 for ; Wed, 5 Oct 2022 11:20:09 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=MGO+2usDvTFV3CDUQZ/xsQ6DVaqiqfZB20rT6m/hwuzlVnMfFVSgNFvpLAS/ddYPbfp2Bhz1fCq1/qEPmEBS7RxlTObMs3PDQ1BJ6vK1An5uPuqAvub4PSZHenyfyowvZ3pYYQ6gTfhRrAqIkRSa6oLIUK/3Sbo4MBz933PLnkDJ0yaMO1tEnJiJMURFRt63xd0cDTYtbf6illhhT0K61hM1yL710qf4fUKKWrjeSmQPyMHivZInVbmTUGo/oTQTjmlgvzM7hP+QKHYTs93Drdzp/8TE2XxQFaT7Ho3+I+ZbXavQ17xvcEv22jER4xW5j+18zV6GGkx7fR1V+CfHaw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=inZIn2Yoc29YW6CgZpWCofcUjI54USNE4sS6gurDpNE=; b=NnSeGlu+UHl17ymU77Ow4QLNajn33h/lTs2C0tdI06A+daYkKd/Ha73zuOHEbw9nVXtga3IOCrRvwj0Eeh3NwAXm6KmU6SQxmqQXmZR6QSpbI9qo7dpo2+q5cCcczEmv7tUR5aYfixqgNYBCC1qesChyG1J9pBX/HqwugOhxVr5RAQt2i5IyIFoiWZFeGgIAFRiJ166/FLyD0JkXVgEPKcv5xizvGhg3LztWUNi4n0o7+mSnRrV+Y7teBMY3V7VAa31dIii7ATXU0yCUAZx09AvDaXffgHniYlaeOj/45IO7SZ57sRTD4PXckVbsLr3lABG60EG4i/WPmmdKijMHBQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 192.176.1.74) smtp.rcpttodomain=arm.com smtp.mailfrom=ericsson.com; dmarc=pass (p=reject sp=reject pct=100) action=none header.from=ericsson.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ericsson.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=inZIn2Yoc29YW6CgZpWCofcUjI54USNE4sS6gurDpNE=; b=Aoq/vCiKXnJebdkWuilLsCyzFxMNueh5uYHmGbLrTL4pEQTWbcVbUjvAWnOJC2mIi81QxVuumlbgZk+IbW4E16GHUmq+ONO0uzaxCf0FGV8+rGGL98Sq9VLLpM7aengYhWmAFLlYWi0aiU1PfPSQ+I8XO+R6ai1JfnZFR1MTios= Received: from OL1P279CA0026.NORP279.PROD.OUTLOOK.COM (2603:10a6:e10:13::13) by DU0PR07MB9194.eurprd07.prod.outlook.com (2603:10a6:10:42e::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5709.10; Wed, 5 Oct 2022 09:20:08 +0000 Received: from HE1EUR02FT019.eop-EUR02.prod.protection.outlook.com (2603:10a6:e10:13:cafe::3e) by OL1P279CA0026.outlook.office365.com (2603:10a6:e10:13::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5676.32 via Frontend Transport; Wed, 5 Oct 2022 09:20:07 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 192.176.1.74) smtp.mailfrom=ericsson.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=ericsson.com; Received-SPF: Pass (protection.outlook.com: domain of ericsson.com designates 192.176.1.74 as permitted sender) receiver=protection.outlook.com; client-ip=192.176.1.74; helo=oa.msg.ericsson.com; pr=C Received: from oa.msg.ericsson.com (192.176.1.74) by HE1EUR02FT019.mail.protection.outlook.com (10.152.10.70) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.5709.10 via Frontend Transport; Wed, 5 Oct 2022 09:20:07 +0000 Received: from ESESBMB503.ericsson.se (153.88.183.170) by ESESBMR506.ericsson.se (153.88.183.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2375.31; Wed, 5 Oct 2022 11:20:03 +0200 Received: from ESESSMB501.ericsson.se (153.88.183.162) by ESESBMB503.ericsson.se (153.88.183.170) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2375.31; Wed, 5 Oct 2022 11:20:02 +0200 Received: from seliicinfr00050.seli.gic.ericsson.se (153.88.183.153) by smtp.internal.ericsson.com (153.88.183.189) with Microsoft SMTP Server id 15.1.2375.31 via Frontend Transport; Wed, 5 Oct 2022 11:20:03 +0200 Received: from localhost.localdomain (seliicwb00002.seli.gic.ericsson.se [10.156.25.100]) by seliicinfr00050.seli.gic.ericsson.se (Postfix) with ESMTP id F1B771C0089; Wed, 5 Oct 2022 11:20:02 +0200 (CEST) From: =?UTF-8?q?Mattias=20R=C3=B6nnblom?= To: Van@dpdk.org, Haaren@dpdk.org, Harry CC: , Honnappa Nagarahalli , =?UTF-8?q?Morten=20Br=C3=B8rup?= , nd , , =?UTF-8?q?Mattias=20R=C3=B6nnblom?= Subject: [PATCH v2 1/6] service: reduce statistics overhead for parallel services Date: Wed, 5 Oct 2022 11:16:10 +0200 Message-ID: <20221005091615.94652-2-mattias.ronnblom@ericsson.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20221005091615.94652-1-mattias.ronnblom@ericsson.com> References: <20220906161352.296110-1-mattias.ronnblom@ericsson.com> <20221005091615.94652-1-mattias.ronnblom@ericsson.com> MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: HE1EUR02FT019:EE_|DU0PR07MB9194:EE_ X-MS-Office365-Filtering-Correlation-Id: 59a8e714-55bc-42a5-895f-08daa6b2cbad X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: KMQ0sTStlMBkvGXSXks5RKchZmmXhbYmPXjW3RjTpNeXH7i0VdXNjywi0+bOf+IX12lkl5bTPiDZI2kbdq1Ey1ZanGSNP8Jtc4rQIPmqXE+Ki6SOT/hbp91bmL05fMbcmZWZgCXD0bL6DM+fjb3sj4VBcdc1cINwtBYOYQcmN6mdDNxXvZVUtXU7MkcrgtNlcyfa0w9OInABuIxF5v5PhjCWyAFdeEmZvOZQ6f8uB4r94W9xr2goH3v0+FGKCo6z6X3/rH33MbpWSERBwQR+gMM8dA6sJpJps/oJkdgDoWnxcooLKYEWmxBwTzxga6Hc19MMjhkSdtms/irs0DX16da/INMzCdIS/dR3pqmnVO32cQy60nnY8W/zPSkZJ+REtGpVKXFeTKwZalbSYr8Vzs59xSjnxcOn3/Ypz/C9cD2tijQrlZp/ZusMk/XBbybllHf0WLKpyda1ITgsm9D6Q8Cfn6gzfyaZJ8gu+/PcEuUKT138PM54EurQy18R0P6GUBMyuAOoCEl46Axgw/ffiUih/M0R6J5YJE225i+/Ce+n2Y7EQE95ui41KYf0fHuG95y5jgBEBPGlBMYUk0SypYetWyHz3iFCEf0yleLYWM+hs4MB5JE25B9rRhcY66L9qWY4/XJQ750JvHMCzPMYmyXr/k9P+b0GtTlghHlmwa3lSkEwWTi21g8HzoOxygQy+fU4YpTymYpIOaNZPLmd8TR+CD8vXDFSnZXPHOirdgHV5KIxNa1Dusx39Esdyf4zkan0muenQuWe+Arz4u4ulkHkmslJLNzoljVdBxz1Qbcsu2YhaFhCVp/y1WmuPEv2 X-Forefront-Antispam-Report: CIP:192.176.1.74; CTRY:SE; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:oa.msg.ericsson.com; PTR:office365.se.ericsson.net; CAT:NONE; SFS:(13230022)(4636009)(136003)(346002)(376002)(396003)(39860400002)(451199015)(36840700001)(46966006)(40470700004)(1076003)(30864003)(336012)(2616005)(186003)(2906002)(41300700001)(26005)(82740400003)(5660300002)(6266002)(82960400001)(7636003)(86362001)(40480700001)(356005)(82310400005)(83380400001)(66574015)(47076005)(40460700003)(36756003)(36860700001)(6916009)(54906003)(316002)(8936002)(478600001)(4326008)(8676002)(70206006)(70586007)(6666004)(107886003); DIR:OUT; SFP:1101; X-OriginatorOrg: ericsson.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 05 Oct 2022 09:20:07.7571 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 59a8e714-55bc-42a5-895f-08daa6b2cbad X-MS-Exchange-CrossTenant-Id: 92e84ceb-fbfd-47ab-be52-080c6b87953f X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=92e84ceb-fbfd-47ab-be52-080c6b87953f; Ip=[192.176.1.74]; Helo=[oa.msg.ericsson.com] X-MS-Exchange-CrossTenant-AuthSource: HE1EUR02FT019.eop-EUR02.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU0PR07MB9194 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Move the statistics from the service data structure to the per-lcore struct. This eliminates contention for the counter cache lines, which decreases the producer-side statistics overhead for services deployed across many lcores. Prior to this patch, enabling statistics for a service with a per-service function call latency of 1000 clock cycles deployed across 16 cores on a Intel Xeon 6230N @ 2,3 GHz would incur a cost of ~10000 core clock cycles per service call. After this patch, the statistics overhead is reduce to 22 clock cycles per call. Signed-off-by: Mattias Rönnblom -- v2: * Introduce service stats struct which hold per-lcore service stats state, to improve spatial locality. --- lib/eal/common/rte_service.c | 190 +++++++++++++++++++++++------------ 1 file changed, 128 insertions(+), 62 deletions(-) diff --git a/lib/eal/common/rte_service.c b/lib/eal/common/rte_service.c index 94cb056196..6b807afacf 100644 --- a/lib/eal/common/rte_service.c +++ b/lib/eal/common/rte_service.c @@ -50,16 +50,12 @@ struct rte_service_spec_impl { * on currently. */ uint32_t num_mapped_cores; - - /* 32-bit builds won't naturally align a uint64_t, so force alignment, - * allowing regular reads to be atomic. - */ - uint64_t calls __rte_aligned(8); - uint64_t cycles_spent __rte_aligned(8); } __rte_cache_aligned; -/* Mask used to ensure uint64_t 8 byte vars are naturally aligned. */ -#define RTE_SERVICE_STAT_ALIGN_MASK (8 - 1) +struct service_stats { + uint64_t calls; + uint64_t cycles; +}; /* the internal values of a service core */ struct core_state { @@ -70,7 +66,7 @@ struct core_state { uint8_t is_service_core; /* set if core is currently a service core */ uint8_t service_active_on_lcore[RTE_SERVICE_NUM_MAX]; uint64_t loops; - uint64_t calls_per_service[RTE_SERVICE_NUM_MAX]; + struct service_stats service_stats[RTE_SERVICE_NUM_MAX]; } __rte_cache_aligned; static uint32_t rte_service_count; @@ -138,13 +134,16 @@ rte_service_finalize(void) rte_service_library_initialized = 0; } -/* returns 1 if service is registered and has not been unregistered - * Returns 0 if service never registered, or has been unregistered - */ -static inline int +static inline bool +service_registered(uint32_t id) +{ + return rte_services[id].internal_flags & SERVICE_F_REGISTERED; +} + +static inline bool service_valid(uint32_t id) { - return !!(rte_services[id].internal_flags & SERVICE_F_REGISTERED); + return id < RTE_SERVICE_NUM_MAX && service_registered(id); } static struct rte_service_spec_impl * @@ -155,7 +154,7 @@ service_get(uint32_t id) /* validate ID and retrieve service pointer, or return error value */ #define SERVICE_VALID_GET_OR_ERR_RET(id, service, retval) do { \ - if (id >= RTE_SERVICE_NUM_MAX || !service_valid(id)) \ + if (!service_valid(id)) \ return retval; \ service = &rte_services[id]; \ } while (0) @@ -217,7 +216,7 @@ rte_service_get_by_name(const char *name, uint32_t *service_id) int i; for (i = 0; i < RTE_SERVICE_NUM_MAX; i++) { - if (service_valid(i) && + if (service_registered(i) && strcmp(name, rte_services[i].spec.name) == 0) { *service_id = i; return 0; @@ -254,7 +253,7 @@ rte_service_component_register(const struct rte_service_spec *spec, return -EINVAL; for (i = 0; i < RTE_SERVICE_NUM_MAX; i++) { - if (!service_valid(i)) { + if (!service_registered(i)) { free_slot = i; break; } @@ -366,29 +365,27 @@ service_runner_do_callback(struct rte_service_spec_impl *s, { void *userdata = s->spec.callback_userdata; - /* Ensure the atomically stored variables are naturally aligned, - * as required for regular loads to be atomic. - */ - RTE_BUILD_BUG_ON((offsetof(struct rte_service_spec_impl, calls) - & RTE_SERVICE_STAT_ALIGN_MASK) != 0); - RTE_BUILD_BUG_ON((offsetof(struct rte_service_spec_impl, cycles_spent) - & RTE_SERVICE_STAT_ALIGN_MASK) != 0); - if (service_stats_enabled(s)) { uint64_t start = rte_rdtsc(); s->spec.callback(userdata); uint64_t end = rte_rdtsc(); uint64_t cycles = end - start; - cs->calls_per_service[service_idx]++; - if (service_mt_safe(s)) { - __atomic_fetch_add(&s->cycles_spent, cycles, __ATOMIC_RELAXED); - __atomic_fetch_add(&s->calls, 1, __ATOMIC_RELAXED); - } else { - uint64_t cycles_new = s->cycles_spent + cycles; - uint64_t calls_new = s->calls++; - __atomic_store_n(&s->cycles_spent, cycles_new, __ATOMIC_RELAXED); - __atomic_store_n(&s->calls, calls_new, __ATOMIC_RELAXED); - } + + /* The lcore service worker thread is the only writer, + * and thus only a non-atomic load and an atomic store + * is needed, and not the more expensive atomic + * add. + */ + struct service_stats *service_stats = + &cs->service_stats[service_idx]; + + __atomic_store_n(&service_stats->calls, + service_stats->calls + 1, + __ATOMIC_RELAXED); + + __atomic_store_n(&service_stats->cycles, + service_stats->cycles + cycles, + __ATOMIC_RELAXED); } else s->spec.callback(userdata); } @@ -436,7 +433,7 @@ rte_service_may_be_active(uint32_t id) int32_t lcore_count = rte_service_lcore_list(ids, RTE_MAX_LCORE); int i; - if (id >= RTE_SERVICE_NUM_MAX || !service_valid(id)) + if (!service_valid(id)) return -EINVAL; for (i = 0; i < lcore_count; i++) { @@ -483,16 +480,17 @@ service_runner_func(void *arg) */ while (__atomic_load_n(&cs->runstate, __ATOMIC_ACQUIRE) == RUNSTATE_RUNNING) { + const uint64_t service_mask = cs->service_mask; for (i = 0; i < RTE_SERVICE_NUM_MAX; i++) { - if (!service_valid(i)) + if (!service_registered(i)) continue; /* return value ignored as no change to code flow */ service_run(i, cs, service_mask, service_get(i), 1); } - cs->loops++; + __atomic_store_n(&cs->loops, cs->loops + 1, __ATOMIC_RELAXED); } /* Use SEQ CST memory ordering to avoid any re-ordering around @@ -608,8 +606,8 @@ static int32_t service_update(uint32_t sid, uint32_t lcore, uint32_t *set, uint32_t *enabled) { /* validate ID, or return error value */ - if (sid >= RTE_SERVICE_NUM_MAX || !service_valid(sid) || - lcore >= RTE_MAX_LCORE || !lcore_states[lcore].is_service_core) + if (!service_valid(sid) || lcore >= RTE_MAX_LCORE || + !lcore_states[lcore].is_service_core) return -EINVAL; uint64_t sid_mask = UINT64_C(1) << sid; @@ -813,21 +811,75 @@ rte_service_lcore_stop(uint32_t lcore) return 0; } +static uint64_t +lcore_attr_get_loops(unsigned int lcore) +{ + struct core_state *cs = &lcore_states[lcore]; + + return __atomic_load_n(&cs->loops, __ATOMIC_RELAXED); +} + +static uint64_t +lcore_attr_get_service_calls(uint32_t service_id, unsigned int lcore) +{ + struct core_state *cs = &lcore_states[lcore]; + + return __atomic_load_n(&cs->service_stats[service_id].calls, + __ATOMIC_RELAXED); +} + +static uint64_t +lcore_attr_get_service_cycles(uint32_t service_id, unsigned int lcore) +{ + struct core_state *cs = &lcore_states[lcore]; + + return __atomic_load_n(&cs->service_stats[service_id].cycles, + __ATOMIC_RELAXED); +} + +typedef uint64_t (*lcore_attr_get_fun)(uint32_t service_id, + unsigned int lcore); + +static uint64_t +attr_get(uint32_t id, lcore_attr_get_fun lcore_attr_get) +{ + unsigned int lcore; + uint64_t sum = 0; + + for (lcore = 0; lcore < RTE_MAX_LCORE; lcore++) + if (lcore_states[lcore].is_service_core) + sum += lcore_attr_get(id, lcore); + + return sum; +} + +static uint64_t +attr_get_service_calls(uint32_t service_id) +{ + return attr_get(service_id, lcore_attr_get_service_calls); +} + +static uint64_t +attr_get_service_cycles(uint32_t service_id) +{ + return attr_get(service_id, lcore_attr_get_service_cycles); +} + int32_t rte_service_attr_get(uint32_t id, uint32_t attr_id, uint64_t *attr_value) { - struct rte_service_spec_impl *s; - SERVICE_VALID_GET_OR_ERR_RET(id, s, -EINVAL); + if (!service_valid(id)) + return -EINVAL; if (!attr_value) return -EINVAL; switch (attr_id) { - case RTE_SERVICE_ATTR_CYCLES: - *attr_value = s->cycles_spent; - return 0; case RTE_SERVICE_ATTR_CALL_COUNT: - *attr_value = s->calls; + *attr_value = attr_get_service_calls(id); + return 0; + case RTE_SERVICE_ATTR_CYCLES: + *attr_value = attr_get_service_cycles(id); return 0; default: return -EINVAL; @@ -849,7 +901,7 @@ rte_service_lcore_attr_get(uint32_t lcore, uint32_t attr_id, switch (attr_id) { case RTE_SERVICE_LCORE_ATTR_LOOPS: - *attr_value = cs->loops; + *attr_value = lcore_attr_get_loops(lcore); return 0; default: return -EINVAL; @@ -859,11 +911,17 @@ rte_service_lcore_attr_get(uint32_t lcore, uint32_t attr_id, int32_t rte_service_attr_reset_all(uint32_t id) { - struct rte_service_spec_impl *s; - SERVICE_VALID_GET_OR_ERR_RET(id, s, -EINVAL); + unsigned int lcore; + + if (!service_valid(id)) + return -EINVAL; + + for (lcore = 0; lcore < RTE_MAX_LCORE; lcore++) { + struct core_state *cs = &lcore_states[lcore]; + + cs->service_stats[id] = (struct service_stats) {}; + } - s->cycles_spent = 0; - s->calls = 0; return 0; } @@ -885,17 +943,25 @@ rte_service_lcore_attr_reset_all(uint32_t lcore) } static void -service_dump_one(FILE *f, struct rte_service_spec_impl *s) +service_dump_one(FILE *f, uint32_t id) { + struct rte_service_spec_impl *s; + uint64_t service_calls; + uint64_t service_cycles; + + service_calls = attr_get_service_calls(id); + service_cycles = attr_get_service_cycles(id); + /* avoid divide by zero */ - int calls = 1; + if (service_calls == 0) + service_calls = 1; + + s = service_get(id); - if (s->calls != 0) - calls = s->calls; fprintf(f, " %s: stats %d\tcalls %"PRIu64"\tcycles %" PRIu64"\tavg: %"PRIu64"\n", - s->spec.name, service_stats_enabled(s), s->calls, - s->cycles_spent, s->cycles_spent / calls); + s->spec.name, service_stats_enabled(s), service_calls, + service_cycles, service_cycles / service_calls); } static void @@ -906,9 +972,9 @@ service_dump_calls_per_lcore(FILE *f, uint32_t lcore) fprintf(f, "%02d\t", lcore); for (i = 0; i < RTE_SERVICE_NUM_MAX; i++) { - if (!service_valid(i)) + if (!service_registered(i)) continue; - fprintf(f, "%"PRIu64"\t", cs->calls_per_service[i]); + fprintf(f, "%"PRIu64"\t", cs->service_stats[i].calls); } fprintf(f, "\n"); } @@ -924,16 +990,16 @@ rte_service_dump(FILE *f, uint32_t id) struct rte_service_spec_impl *s; SERVICE_VALID_GET_OR_ERR_RET(id, s, -EINVAL); fprintf(f, "Service %s Summary\n", s->spec.name); - service_dump_one(f, s); + service_dump_one(f, id); return 0; } /* print all services, as UINT32_MAX was passed as id */ fprintf(f, "Services Summary\n"); for (i = 0; i < RTE_SERVICE_NUM_MAX; i++) { - if (!service_valid(i)) + if (!service_registered(i)) continue; - service_dump_one(f, &rte_services[i]); + service_dump_one(f, i); } fprintf(f, "Service Cores Summary\n"); -- 2.34.1