Skip to content

Commit

Permalink
Enable and modify memory limits for packet receiving
Browse files Browse the repository at this point in the history
Modify memory cleanup function values based on stack memory size

Enable packet ingress for low memory devices to prevent deadlocks
  • Loading branch information
Mika Tervonen authored and Mika Tervonen committed Jan 26, 2021
1 parent e2b028d commit 7ca6c24
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 24 deletions.
28 changes: 27 additions & 1 deletion source/6LoWPAN/ws/ws_bootstrap.c
Expand Up @@ -28,6 +28,7 @@
#include "mac_common_defines.h"
#include "sw_mac.h"
#include "ccmLIB.h"
#include "Core/include/ns_monitor.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "6LoWPAN/Bootstraps/protocol_6lowpan.h"
#include "6LoWPAN/Bootstraps/protocol_6lowpan_interface.h"
Expand Down Expand Up @@ -1010,6 +1011,30 @@ static void ws_bootstrap_dhcp_info_notify_cb(int8_t interface, dhcp_option_notif

}

static void ws_bootstrap_memory_configuration()
{
/* Configure memory limits for garbage collection based on total memory size
* Starting from these values
* 5% for High mark
* 2% for critical mark
* 1% for Routing limit
* Memory High Critical Drop routing
* 32K RAM 3200 bytes 1280 Bytes 1024 bytes
* 64K RAM 3200 bytes 1280 Bytes 1024 bytes
* 128K RAM 6400 bytes 2560 Bytes 1280 bytes
* 320K RAM 16000 byte 6400 Bytes 3200 bytes
* 640K RAM 32000 byte 12800 Bytes 6400 bytes
* 1000K RAM 50000 bytes 20000 Bytes 10000 bytes
* 4000K RAM 120000 bytes 40000 Bytes 10000 bytes
* */
// In small memory devices there needs to lower limit so that there some change to be usable
// and there is no use for having very large values on high memory devices
ns_monitor_packet_ingress_rate_limit_by_memory(1024, 10000, 1);

ns_monitor_heap_gc_threshold_set(3200, 120000, 95, 1280, 40000, 98);
return;
}


static int8_t ws_bootstrap_up(protocol_interface_info_entry_t *cur)
{
Expand Down Expand Up @@ -1066,7 +1091,8 @@ static int8_t ws_bootstrap_up(protocol_interface_info_entry_t *cur)
dhcp_client_solicit_timeout_set(cur->id, WS_DHCP_SOLICIT_TIMEOUT, WS_DHCP_SOLICIT_MAX_RT, WS_DHCP_SOLICIT_MAX_RC);
dhcp_client_option_notification_cb_set(cur->id, ws_bootstrap_dhcp_info_notify_cb);


// Configure memory limits and garbage collection values;
ws_bootstrap_memory_configuration();
ws_nud_table_reset(cur);

ws_bootstrap_candidate_table_reset(cur);
Expand Down
4 changes: 2 additions & 2 deletions source/Core/include/ns_monitor.h
Expand Up @@ -33,9 +33,9 @@ int ns_monitor_clear(void);

void ns_monitor_timer(uint16_t seconds);

int ns_monitor_heap_gc_threshold_set(uint8_t percentage_high, uint8_t percentage_critical);
int ns_monitor_heap_gc_threshold_set(uint32_t high_min, uint32_t high_max, uint8_t high_percentage, uint32_t critical_min, uint32_t critical_max, uint8_t critical_percentage);

int ns_monitor_packet_ingress_rate_limit_by_memory(uint8_t free_heap_percentage);
int ns_monitor_packet_ingress_rate_limit_by_memory(uint32_t minimum_required, uint32_t Maximum_allowed, uint8_t free_heap_percentage);

bool ns_monitor_packet_allocation_allowed(void);

Expand Down
67 changes: 55 additions & 12 deletions source/Core/ns_monitor.c
Expand Up @@ -63,7 +63,7 @@ typedef struct ns_monitor__s {

static ns_monitor_t *ns_monitor_ptr = NULL;

static uint8_t ns_dyn_mem_rate_limiting_threshold_percentage = 0; // Percentage of free memory required to allow routing
static ns_mem_heap_size_t ns_dyn_mem_rate_limiting_threshold = 0; // amount of free memory required to allow routing 0 = disabled

typedef void (ns_maintenance_gc_cb)(bool full_gc);

Expand Down Expand Up @@ -176,32 +176,75 @@ int ns_monitor_clear(void)
return -1;
}

int ns_monitor_heap_gc_threshold_set(uint8_t percentage_high, uint8_t percentage_critical)
int ns_monitor_heap_gc_threshold_set(uint32_t high_min, uint32_t high_max, uint8_t high_percentage, uint32_t critical_min, uint32_t critical_max, uint8_t critical_percentage)
{
if (ns_monitor_ptr && (percentage_critical <= 100) && (percentage_high < percentage_critical)) {
if (ns_monitor_ptr && (critical_percentage <= 100) && (high_percentage < critical_percentage)) {
ns_monitor_ptr->heap_high_watermark = SET_WATERMARK(
ns_monitor_ptr->mem_stats->heap_sector_size,
percentage_high
high_percentage
);
if (ns_monitor_ptr->mem_stats->heap_sector_size - ns_monitor_ptr->heap_high_watermark < high_min) {
ns_monitor_ptr->heap_high_watermark = ns_monitor_ptr->mem_stats->heap_sector_size - high_min;
}

if (high_max && ns_monitor_ptr->mem_stats->heap_sector_size - ns_monitor_ptr->heap_high_watermark > high_max) {
ns_monitor_ptr->heap_high_watermark = ns_monitor_ptr->mem_stats->heap_sector_size - high_max;
}

ns_monitor_ptr->heap_critical_watermark = SET_WATERMARK(
ns_monitor_ptr->mem_stats->heap_sector_size,
percentage_critical
critical_percentage
);
tr_debug("Monitor set high:%lu, critical:%lu total:%lu", (unsigned long)ns_monitor_ptr->heap_high_watermark, (unsigned long)ns_monitor_ptr->heap_critical_watermark, (unsigned long)ns_monitor_ptr->mem_stats->heap_sector_size);
if (ns_monitor_ptr->mem_stats->heap_sector_size - ns_monitor_ptr->heap_critical_watermark < critical_min) {
ns_monitor_ptr->heap_critical_watermark = ns_monitor_ptr->mem_stats->heap_sector_size - critical_min;
}

if (critical_max && ns_monitor_ptr->mem_stats->heap_sector_size - ns_monitor_ptr->heap_critical_watermark > critical_max) {
ns_monitor_ptr->heap_critical_watermark = ns_monitor_ptr->mem_stats->heap_sector_size - critical_max;
}

tr_info("Monitor set high:%lu, critical:%lu total:%lu", (unsigned long)ns_monitor_ptr->heap_high_watermark, (unsigned long)ns_monitor_ptr->heap_critical_watermark, (unsigned long)ns_monitor_ptr->mem_stats->heap_sector_size);
return 0;
}

return -1;
}

int ns_monitor_packet_ingress_rate_limit_by_memory(uint8_t free_heap_percentage)
int ns_monitor_packet_ingress_rate_limit_by_memory(uint32_t minimum_required, uint32_t Maximum_allowed, uint8_t free_heap_percentage)
{
if (free_heap_percentage < 100) {
ns_dyn_mem_rate_limiting_threshold_percentage = free_heap_percentage;
/* To make this function dynamic and useful in larger range of memories the minimum value can be given
*
* example limit(1024, 1)
* 32k RAM Limit = 1024
* 64k RAM Limit = 1024
* 128k RAM Limit = 1280
* 320k RAM Limit = 3200
*/
if (free_heap_percentage == 0 && minimum_required == 0) {
// Disable rate limiting
ns_dyn_mem_rate_limiting_threshold = 0;
return 0;
}
if (free_heap_percentage > 100) {
// Sanity check this should not be high at all, but dont want to limit without any good reason
return -1;
}

return -1;
const mem_stat_t *ns_dyn_mem_stat = ns_dyn_mem_get_mem_stat();
if (ns_dyn_mem_stat && free_heap_percentage) {
ns_dyn_mem_rate_limiting_threshold = ns_dyn_mem_stat->heap_sector_size / 100 * free_heap_percentage;
}

if (ns_dyn_mem_rate_limiting_threshold < minimum_required) {
ns_dyn_mem_rate_limiting_threshold = minimum_required;
}

if (Maximum_allowed && ns_dyn_mem_rate_limiting_threshold > Maximum_allowed) {
ns_dyn_mem_rate_limiting_threshold = Maximum_allowed;
}
tr_info("Monitor rate limit incoming packets at:%lu", (unsigned long)ns_dyn_mem_rate_limiting_threshold);

return 0;
}

bool ns_monitor_packet_allocation_allowed(void)
Expand All @@ -212,8 +255,8 @@ bool ns_monitor_packet_allocation_allowed(void)

const mem_stat_t *ns_dyn_mem_stat = ns_dyn_mem_get_mem_stat();

if (ns_dyn_mem_stat && ns_dyn_mem_rate_limiting_threshold_percentage) {
if (ns_dyn_mem_stat->heap_sector_allocated_bytes > ns_dyn_mem_stat->heap_sector_size / 100 * (100 - ns_dyn_mem_rate_limiting_threshold_percentage)) {
if (ns_dyn_mem_stat && ns_dyn_mem_rate_limiting_threshold) {
if (ns_dyn_mem_stat->heap_sector_size - ns_dyn_mem_stat->heap_sector_allocated_bytes < ns_dyn_mem_rate_limiting_threshold) {
// Packet allocation not allowed as memory is running low.
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions source/Service_Libs/utils/ns_conf.c
Expand Up @@ -24,10 +24,10 @@

int ns_conf_gc_threshold_set(uint8_t percentage_high, uint8_t percentage_critical)
{
return ns_monitor_heap_gc_threshold_set(percentage_high, percentage_critical);
return ns_monitor_heap_gc_threshold_set(0, 0, percentage_high, 0, 0, percentage_critical);
}

int ns_conf_packet_ingress_rate_limit_by_mem(uint8_t free_heap_percentage)
{
return ns_monitor_packet_ingress_rate_limit_by_memory(free_heap_percentage);
return ns_monitor_packet_ingress_rate_limit_by_memory(0, 0, free_heap_percentage);
}
28 changes: 23 additions & 5 deletions test/nanostack/unittest/Core/monitor/test_monitor.c
Expand Up @@ -21,6 +21,7 @@
#include "NWK_INTERFACE/Include/protocol.h"
#include "nsdynmemLIB_stub.h"
#include "ipv6_routing_table_stub.h""
#include "ns_monitor.h"

bool test_ns_monitor_init()
{
Expand Down Expand Up @@ -68,44 +69,51 @@ bool test_ns_monitor_heap_gc_threshold_set()
nsdynmemlib_stub.returnCounter = 1;
ret_val = ns_monitor_init();
if (ret_val != 0) {
printf("Init Monitor FAIL %d\n", ret_val);
return false;
}

// high and critical are equal. NOK
ret_val = ns_monitor_heap_gc_threshold_set(100, 100);
ret_val = ns_monitor_heap_gc_threshold_set(0, 0, 100, 0, 0, 100);
if (ret_val == 0) {
printf("high and critical are equal. NOK\n");
return false;
}


// high is bigger than critical. NOK
ret_val = ns_monitor_heap_gc_threshold_set(100, 80);
ret_val = ns_monitor_heap_gc_threshold_set(0, 0, 100, 0, 0, 80);
if (ret_val == 0) {
printf("high is bigger than critical. NOK\n");
return false;
}


// Too big, NOK
ret_val = ns_monitor_heap_gc_threshold_set(80, 101);
ret_val = ns_monitor_heap_gc_threshold_set(0, 0, 80, 0, 0, 101);
if (ret_val == 0) {
printf("Too big, NOK\n");
return false;
}

// OK
ret_val = ns_monitor_heap_gc_threshold_set(50, 80);
ret_val = ns_monitor_heap_gc_threshold_set(0, 0, 50, 0, 0, 80);
if (ret_val != 0) {
printf("OK Failed\n");
return false;
}

// clear monitor
ret_val = ns_monitor_clear();
if (ret_val != 0) {
printf("clear monitor\n");
return false;
}

// clear monitor again - NOK
ret_val = ns_monitor_clear();
if (ret_val == 0) {
printf("clear monitor again - NOK\n");
return false;
}

Expand All @@ -124,19 +132,22 @@ bool test_ns_monitor_timer()
nsdynmemlib_stub.returnCounter = 1;
ret_val = ns_monitor_init();
if (ret_val != 0) {
printf("init fail\n");
return false;
}

// nothing happens
ns_monitor_timer(1);
if (ipv6_routing_table_stub_called) {
printf("nothing happens\n");
return false;
}

// allocation failure - critical gc
nsdynmemlib_mem_stats_stub.heap_alloc_fail_cnt = 1;
ns_monitor_timer(1);
if (!ipv6_routing_table_stub_called) {
printf("stub not called\n");
return false;
}
ipv6_routing_table_stub_called = false;
Expand All @@ -145,45 +156,52 @@ bool test_ns_monitor_timer()
nsdynmemlib_mem_stats_stub.heap_alloc_fail_cnt = 2;
ns_monitor_timer(1);
if (ipv6_routing_table_stub_called) {
printf("allocation failure again - nothing happens\n");
return false;
}
ipv6_routing_table_stub_called = false;

// Monitor state is changed to idle after allocation error
ns_monitor_timer(100);
if (ipv6_routing_table_stub_called) {
printf("Monitor state is changed to idle after allocation error\n");
return false;
}

// Set thresholds and allocation - High GC
ret_val = ns_monitor_heap_gc_threshold_set(40, 80);
ret_val = ns_monitor_heap_gc_threshold_set(0, 0, 40, 0, 0, 80);
if (ret_val != 0) {
printf("Set thresholds and allocation - High GC\n");
return false;
}
nsdynmemlib_mem_stats_stub.heap_sector_allocated_bytes = 45;
ns_monitor_timer(100);
if (!ipv6_routing_table_stub_called) {
printf("stub 2 not called\n");
return false;
}
ipv6_routing_table_stub_called = false;

// Timer elapses again - no GC
ns_monitor_timer(100);
if (ipv6_routing_table_stub_called) {
printf("Timer elapses again - no GC\n");
return false;
}

// more memory allocated - Critical GC
nsdynmemlib_mem_stats_stub.heap_sector_allocated_bytes = 95;
ns_monitor_timer(100);
if (!ipv6_routing_table_stub_called) {
printf("more memory allocated - Critical GC\n");
return false;
}
ipv6_routing_table_stub_called = false;

// clear monitor
ret_val = ns_monitor_clear();
if (ret_val != 0) {
printf("clear monitor\n");
return false;
}

Expand Down
9 changes: 9 additions & 0 deletions test/nanostack/unittest/stub/mbed_trace_stub.c
Expand Up @@ -90,10 +90,19 @@ void mbed_trace_include_filters_set(char *filters)

void mbed_tracef(uint8_t dlevel, const char *grp, const char *fmt, ...)
{
va_list arglist;
printf("Trace: level %d [%s]", dlevel, grp);
va_start(arglist, fmt);
vprintf(fmt, arglist);
printf("\n");
va_end(arglist);
}

void mbed_vtracef(uint8_t dlevel, const char *grp, const char *fmt, va_list ap)
{
printf("Trace: level %d [%s]", dlevel, grp);
vprintf(fmt, ap);
printf("\n");
}

const char *mbed_trace_last(void)
Expand Down
4 changes: 2 additions & 2 deletions test/nanostack/unittest/stub/ns_monitor_stub.c
Expand Up @@ -37,12 +37,12 @@ void ns_monitor_timer(uint16_t seconds)
{
}

int ns_monitor_heap_gc_threshold_set(uint8_t percentage_high, uint8_t percentage_critical)
int ns_monitor_heap_gc_threshold_set(uint32_t high_min, uint32_t high_max, uint8_t high_percentage, uint32_t critical_min, uint32_t critical_max, uint8_t critical_percentage)
{
return 0;
}

int ns_monitor_packet_ingress_rate_limit_by_memory(uint8_t free_heap_percentage)
int ns_monitor_packet_ingress_rate_limit_by_memory(uint32_t minimum_required, uint32_t Maximum_allowed, uint8_t free_heap_percentage)
{
return 0;
}
Expand Down

0 comments on commit 7ca6c24

Please sign in to comment.