Skip to content

Commit 64c969e

Browse files
author
Arto Kinnunen
committed
Nanostack heap garbage collection
Nanostack monitors heap usage and tries to make heap garbage collection if heap usage is above the thresholds. A new nanostack API added for nanostack monitoring.
1 parent d3330b2 commit 64c969e

File tree

9 files changed

+315
-5
lines changed

9 files changed

+315
-5
lines changed

nanostack/ns_file_system.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
#ifndef _NS_FILE_SYSTEM_H_
1919
#define _NS_FILE_SYSTEM_H_
2020

21+
#ifdef __cplusplus
22+
extern "C" {
23+
#endif
24+
2125
/**
2226
* \file ns_file_system.h
2327
* \brief Nanostack file system API.
@@ -35,9 +39,7 @@
3539
* \return 0 in success, negative value in case of error.
3640
*
3741
*/
38-
#ifdef __cplusplus
39-
extern "C" {
40-
#endif
42+
4143
int ns_file_system_set_root_path(const char *root_path);
4244

4345
/**
@@ -47,6 +49,7 @@ int ns_file_system_set_root_path(const char *root_path);
4749
*
4850
*/
4951
char *ns_file_system_get_root_path(void);
52+
5053
#ifdef __cplusplus
5154
}
5255
#endif

nanostack/ns_monitor_api.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2019, Arm Limited and affiliates.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#ifndef _NS_MONITOR_API_H_
19+
#define _NS_MONITOR_API_H_
20+
21+
#ifdef __cplusplus
22+
extern "C" {
23+
#endif
24+
25+
/**
26+
* \file ns_monitor_api.h
27+
* \brief Nanostack monitoring API.
28+
*/
29+
30+
/**
31+
* \brief Set threshold for memory garbage collection.
32+
*
33+
* Nanostack heap usage is monitored in regular intervals. If too much memory has been used then garbage collection (GC)
34+
* is triggered. GC has two adjustable thresholds: HIGH and CRITICAL. HIGH threshold is lower one and once exceeded
35+
* a GC will try to release memory that is used for caching. When CRITTICAL threshold is exceeded them GC will try to release
36+
* memory more aggressiveliy.
37+
*
38+
* Nanostack memory monitoring can only work if memory statistics are enabled to nsdynmemLIB.
39+
40+
*
41+
* \param percentage_high Percentage of total heap when garbage collection is first time triggered
42+
* \param percentage_critical Percentage of total heap when critical garbage collection is triggered
43+
*
44+
* \return 0 in success, negative value in case of error.
45+
*
46+
*/
47+
int ns_monitor_api_gc_threshold_set(uint8_t percentage_high, uint8_t percentage_critical);
48+
49+
50+
#ifdef __cplusplus
51+
}
52+
#endif
53+
54+
#endif /* _NS_MONITOR_API_H_ */

source/Core/include/ns_monitor.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2019, Arm Limited and affiliates.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* \file ns_monitor.h
20+
* \brief Utility functions concerning IPv6 stack monitoring.
21+
*
22+
* Module can monitor nanostack heap usage and release memory if heap usage is too high.
23+
* Mmeory monitoring can work if memory statistics are enabled in nsdynmemLIB.
24+
*
25+
*/
26+
27+
#ifndef _NS_MONITOR_H
28+
#define _NS_MONITOR_H
29+
30+
int ns_monitor_init(void);
31+
32+
int ns_monitor_clear(void);
33+
34+
void ns_monitor_timer(uint16_t seconds);
35+
36+
int ns_monitor_heap_gc_threshold_set(uint8_t percentage_high, uint8_t percentage_critical);
37+
38+
#endif // _NS_MONITOR_H
39+

source/Core/ns_monitor.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* Copyright (c) 2019, Arm Limited and affiliates.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* \file ns_monitor.c
20+
* \brief Utility functions for nanostack maintenance
21+
*
22+
* This module tracks stack current heap usage and triggers GC if heap usage is too high.
23+
* GC is triggered by:
24+
* 1. Heap usage is above HEAP_USAGE_HIGH
25+
* 2. Heap usage is above HEAP_USAGE_CRITICAL
26+
* 3. If nsdynmemLIB memory allocation has failed since last check
27+
*/
28+
29+
#include "nsconfig.h"
30+
#include "ns_types.h"
31+
#define HAVE_DEBUG
32+
#include "ns_trace.h"
33+
#include "nsdynmemLIB.h"
34+
#include "ipv6_stack/ipv6_routing_table.h"
35+
#include "NWK_INTERFACE/Include/protocol.h"
36+
37+
#define TRACE_GROUP "mntr"
38+
39+
typedef enum {
40+
NS_MONITOR_STATE_HEAP_GC_IDLE = 0,
41+
NS_MONITOR_STATE_HEAP_GC_HIGH,
42+
NS_MONITOR_STATE_GC_CRITICAL
43+
} ns_monitor_state_e;
44+
45+
#define HEAP_HIGH_THRESHOLD (0.7) /* Heap usage HIGH threshold */
46+
#define HEAP_CRITICAL_THRESHOLD (0.9) /* Heap usage CRITICAL threshold */
47+
48+
#define NS_MAINTENANCE_TIMER_INTERVAL 10 // Maintenance interval
49+
50+
typedef struct ns_monitor__s {
51+
ns_mem_heap_size_t heap_high_watermark;
52+
ns_mem_heap_size_t heap_critical_watermark;
53+
uint32_t prev_heap_alloc_fail_cnt;
54+
ns_monitor_state_e ns_monitor_heap_gc_state;
55+
const mem_stat_t *mem_stats;
56+
uint16_t ns_maintenance_timer;
57+
} ns_monitor_t;
58+
59+
static ns_monitor_t *ns_monitor_ptr = NULL;
60+
61+
typedef void (ns_maintenance_gc_cb)(void);
62+
63+
/*
64+
* Garbage collection functions.
65+
* Add more GC performing functions to the table
66+
*
67+
*/
68+
static ns_maintenance_gc_cb *ns_maintenance_gc_functions[] = {
69+
ipv6_destination_cache_forced_gc
70+
};
71+
72+
static void ns_monitor_heap_gc(bool full_gc)
73+
{
74+
(void) full_gc;
75+
76+
for (unsigned int i = 0; i < sizeof(ns_maintenance_gc_functions) / sizeof(ns_maintenance_gc_functions[0]); i++) {
77+
(ns_maintenance_gc_functions[i])();
78+
}
79+
}
80+
81+
static void ns_monitor_periodic_heap_health_check(void)
82+
{
83+
if (ns_monitor_ptr->mem_stats->heap_sector_allocated_bytes > ns_monitor_ptr->heap_critical_watermark) {
84+
if (ns_monitor_ptr->ns_monitor_heap_gc_state != NS_MONITOR_STATE_GC_CRITICAL) {
85+
ns_mem_heap_size_t prev_heap_sector_allocated_bytes = ns_monitor_ptr->mem_stats->heap_sector_allocated_bytes;
86+
// Heap usage above CRITICAL
87+
ns_monitor_heap_gc(true);
88+
ns_monitor_ptr->ns_monitor_heap_gc_state = NS_MONITOR_STATE_GC_CRITICAL;
89+
tr_info("Stack GC critical: freed %lu bytes", (unsigned long)(prev_heap_sector_allocated_bytes - ns_monitor_ptr->mem_stats->heap_sector_allocated_bytes));
90+
}
91+
} else if (ns_monitor_ptr->mem_stats->heap_sector_allocated_bytes > ns_monitor_ptr->heap_high_watermark) {
92+
if (ns_monitor_ptr->ns_monitor_heap_gc_state == NS_MONITOR_STATE_HEAP_GC_IDLE) {
93+
ns_mem_heap_size_t prev_heap_sector_allocated_bytes = ns_monitor_ptr->mem_stats->heap_sector_allocated_bytes;
94+
// Heap usage above HIGH
95+
ns_monitor_heap_gc(false);
96+
ns_monitor_ptr->ns_monitor_heap_gc_state = NS_MONITOR_STATE_HEAP_GC_HIGH;
97+
tr_info("Stack GC high: freed %lu bytes", (unsigned long)(prev_heap_sector_allocated_bytes - ns_monitor_ptr->mem_stats->heap_sector_allocated_bytes));
98+
}
99+
} else if (ns_monitor_ptr->mem_stats->heap_sector_allocated_bytes <= ns_monitor_ptr->heap_high_watermark) {
100+
// Heap usage in normal range
101+
ns_monitor_ptr->ns_monitor_heap_gc_state = NS_MONITOR_STATE_HEAP_GC_IDLE;
102+
}
103+
}
104+
105+
void ns_monitor_timer(uint16_t seconds)
106+
{
107+
if (ns_monitor_ptr) {
108+
ns_monitor_ptr->ns_maintenance_timer += seconds;
109+
110+
if (ns_monitor_ptr->mem_stats->heap_alloc_fail_cnt > ns_monitor_ptr->prev_heap_alloc_fail_cnt) {
111+
// Heap allocation failure occurred since last check
112+
ns_monitor_ptr->prev_heap_alloc_fail_cnt = ns_monitor_ptr->mem_stats->heap_alloc_fail_cnt;
113+
if (ns_monitor_ptr->ns_monitor_heap_gc_state != NS_MONITOR_STATE_GC_CRITICAL) {
114+
ns_monitor_ptr->ns_monitor_heap_gc_state = NS_MONITOR_STATE_GC_CRITICAL;
115+
ns_monitor_heap_gc(true);
116+
ns_monitor_ptr->ns_maintenance_timer = 0;
117+
}
118+
}
119+
120+
if (ns_monitor_ptr->ns_maintenance_timer >= NS_MAINTENANCE_TIMER_INTERVAL) {
121+
ns_monitor_ptr->ns_maintenance_timer -= NS_MAINTENANCE_TIMER_INTERVAL;
122+
tr_debug("ns_monitor_maintenance_timer(), used %lu/%lu", (unsigned long)ns_monitor_ptr->mem_stats->heap_sector_allocated_bytes, (unsigned long)ns_monitor_ptr->mem_stats->heap_sector_size);
123+
ns_monitor_periodic_heap_health_check();
124+
}
125+
}
126+
}
127+
128+
int ns_monitor_init(void)
129+
{
130+
if (ns_monitor_ptr || !ns_dyn_mem_get_mem_stat()) {
131+
// already initialized or memory statistics not available
132+
return -2;
133+
}
134+
135+
ns_monitor_ptr = ns_dyn_mem_alloc(sizeof(ns_monitor_t));
136+
137+
if (ns_monitor_ptr) {
138+
ns_monitor_ptr->mem_stats = ns_dyn_mem_get_mem_stat();
139+
ns_monitor_ptr->heap_high_watermark = ns_monitor_ptr->mem_stats->heap_sector_size * HEAP_HIGH_THRESHOLD;
140+
ns_monitor_ptr->heap_critical_watermark = ns_monitor_ptr->mem_stats->heap_sector_size * HEAP_CRITICAL_THRESHOLD;
141+
ns_monitor_ptr->ns_monitor_heap_gc_state = NS_MONITOR_STATE_HEAP_GC_IDLE;
142+
ns_monitor_ptr->ns_maintenance_timer = 0;
143+
ns_monitor_ptr->prev_heap_alloc_fail_cnt = 0;
144+
tr_debug("monitor high %lu, critical %lu", (unsigned long)ns_monitor_ptr->heap_high_watermark, (unsigned long)ns_monitor_ptr->heap_critical_watermark);
145+
return 0;
146+
}
147+
148+
return -1;
149+
}
150+
151+
int ns_monitor_clear(void)
152+
{
153+
if (ns_monitor_ptr) {
154+
ns_dyn_mem_free(ns_monitor_ptr);
155+
ns_monitor_ptr = NULL;
156+
return 0;
157+
}
158+
159+
return -1;
160+
}
161+
162+
int ns_monitor_heap_gc_threshold_set(uint8_t percentage_high, uint8_t percentage_critical)
163+
{
164+
if (ns_monitor_ptr && (percentage_critical <= 100) && (percentage_high < percentage_critical)) {
165+
ns_monitor_ptr->heap_high_watermark = ns_monitor_ptr->mem_stats->heap_sector_size * percentage_high / 100;
166+
ns_monitor_ptr->heap_critical_watermark = ns_monitor_ptr->mem_stats->heap_sector_size * percentage_critical / 100;
167+
return 0;
168+
}
169+
170+
return -1;
171+
}

source/NWK_INTERFACE/protocol_core.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "ns_trace.h"
2525
#include "nsdynmemLIB.h"
2626
#include "Core/include/ns_socket.h"
27+
#include "Core/include/ns_monitor.h"
2728
#include "NWK_INTERFACE/Include/protocol.h"
2829
#include "NWK_INTERFACE/Include/protocol_timer.h"
2930
#include "platform/arm_hal_interrupt.h"
@@ -306,8 +307,7 @@ void core_timer_event_handle(uint16_t ticksUpdate)
306307
ws_pae_controller_slow_timer(seconds);
307308
#endif
308309
protocol_6lowpan_mle_timer(seconds);
309-
/* This limit bad behaviour device's MLE link reject generation */
310-
310+
ns_monitor_timer(seconds);
311311
} else {
312312
protocol_core_seconds_timer -= ticksUpdate;
313313
}
@@ -387,6 +387,7 @@ void protocol_core_init(void)
387387
protocol_core_timer_info.core_security_ticks_counter = SEC_LIB_X_100MS_COUNTER;
388388

389389
protocol_timer_start(PROTOCOL_TIMER_STACK_TIM, protocol_core_cb, 100);
390+
ns_monitor_init();
390391

391392
}
392393

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2017-2018, Arm Limited and affiliates.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "ns_types.h"
19+
20+
#include "Core/include/ns_monitor.h"
21+
22+
int ns_monitor_api_gc_threshold_set(uint8_t percentage_high, uint8_t percentage_critical)
23+
{
24+
return ns_monitor_heap_gc_threshold_set(percentage_high, percentage_critical);
25+
}

source/ipv6_stack/ipv6_routing_table.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,20 @@ void ipv6_destination_redirect(const uint8_t *dest_addr, const uint8_t *sender_a
10721072
}
10731073
#endif
10741074

1075+
void ipv6_destination_cache_forced_gc(void)
1076+
{
1077+
int gc_count = ns_list_count(&ipv6_destination_cache);
1078+
1079+
/* Minimize size of destination cache, keep absolutely minimum number of entries */
1080+
ns_list_foreach_reverse_safe(ipv6_destination_t, entry, &ipv6_destination_cache) {
1081+
if (entry->lifetime == 0 || gc_count > destination_cache_config.long_term_entries) {
1082+
ns_list_remove(&ipv6_destination_cache, entry);
1083+
ipv6_destination_release(entry);
1084+
gc_count--;
1085+
}
1086+
}
1087+
}
1088+
10751089
static void ipv6_destination_release(ipv6_destination_t *dest)
10761090
{
10771091
if (--dest->refcount == 0) {

source/ipv6_stack/ipv6_routing_table.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ void ipv6_destination_cache_timer(uint8_t ticks);
222222
#ifdef HAVE_IPV6_ND
223223
void ipv6_destination_redirect(const uint8_t *dest_addr, const uint8_t *sender_addr, const uint8_t *redirect_addr, int8_t interface_id, addrtype_t ll_type, const uint8_t *ll_address);
224224
#endif
225+
void ipv6_destination_cache_forced_gc(void);
225226

226227
/* Combined Routing Table (RFC 4191) and Prefix List (RFC 4861) */
227228
/* On-link prefixes have the on_link flag set and next_hop is unset */

sources.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ SRCS += \
5252
source/Common_Protocols/tcp.c \
5353
source/Common_Protocols/udp.c \
5454
source/Core/ns_address_internal.c \
55+
source/Core/ns_monitor.c \
5556
source/Core/buffer_dyn.c \
5657
source/Core/sockbuf.c \
5758
source/Core/ns_socket.c \
@@ -201,6 +202,7 @@ SRCS += \
201202
source/Service_Libs/utils/ns_crc.c \
202203
source/Service_Libs/utils/isqrt.c \
203204
source/Service_Libs/utils/ns_file_system.c \
205+
source/Service_Libs/utils/ns_monitor_api.c \
204206
source/Service_Libs/mdns/ns_mdns_api.c \
205207
source/Service_Libs/mdns/ns_fnet_port.c \
206208
source/Service_Libs/mdns/ns_fnet_events.c \

0 commit comments

Comments
 (0)