diff --git a/src/common/pf_alarm.c b/src/common/pf_alarm.c index 649ced76c..3a1912757 100644 --- a/src/common/pf_alarm.c +++ b/src/common/pf_alarm.c @@ -59,6 +59,7 @@ */ #ifdef UNIT_TEST +#define os_get_current_time_us mock_os_get_current_time_us #endif /* @@ -826,11 +827,12 @@ static void pf_alarm_apms_timeout ( if ( pf_scheduler_add ( - net, + &net->scheduler_data, p_apmx->timeout_us, pf_alarm_apms_timeout, p_apmx, - &p_apmx->resend_timeout) != 0) + &p_apmx->resend_timeout, + current_time) != 0) { LOG_ERROR ( PF_ALARM_LOG, @@ -1460,11 +1462,12 @@ static int pf_alarm_apms_apms_a_data_req ( retransmission timer */ if ( pf_scheduler_add ( - net, + &net->scheduler_data, p_apmx->timeout_us, pf_alarm_apms_timeout, p_apmx, - &p_apmx->resend_timeout) != 0) + &p_apmx->resend_timeout, + os_get_current_time_us()) != 0) { LOG_ERROR ( PF_ALARM_LOG, @@ -1549,7 +1552,9 @@ static int pf_alarm_apmx_close (pnet_t * net, pf_ar_t * p_ar, uint8_t err_code) { /* Free resources */ /* StopTimer */ - pf_scheduler_remove_if_running (net, &p_ar->apmx[ix].resend_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &p_ar->apmx[ix].resend_timeout); p_ar->apmx[ix].p_ar = NULL; p_ar->apmx[ix].apms_state = PF_APMS_STATE_CLOSED; diff --git a/src/common/pf_cpm_driver_sw.c b/src/common/pf_cpm_driver_sw.c index a44df4079..14af4b52c 100644 --- a/src/common/pf_cpm_driver_sw.c +++ b/src/common/pf_cpm_driver_sw.c @@ -88,11 +88,12 @@ static void pf_cpm_control_interval_expired ( /* Timer auto-reload */ if ( pf_scheduler_add ( - net, + &net->scheduler_data, p_iocr->cpm.control_interval, pf_cpm_control_interval_expired, arg, - &p_iocr->cpm.ci_timeout) != 0) + &p_iocr->cpm.ci_timeout, + current_time) != 0) { LOG_ERROR ( PF_CPM_LOG, @@ -154,7 +155,7 @@ static int pf_cpm_driver_sw_close_req ( pf_cpm_t * p_cpm = &p_ar->iocrs[crep].cpm; p_cpm->ci_running = false; /* StopTimer */ - pf_scheduler_remove_if_running (net, &p_cpm->ci_timeout); + pf_scheduler_remove_if_running (&net->scheduler_data, &p_cpm->ci_timeout); pf_eth_frame_id_map_remove (net, p_cpm->frame_id[0]); if (p_cpm->nbr_frame_id == 2) @@ -424,11 +425,12 @@ static int pf_cpm_driver_sw_activate_req ( } pf_scheduler_init_handle (&p_cpm->ci_timeout, "cpm"); ret = pf_scheduler_add ( - net, + &net->scheduler_data, p_cpm->control_interval, pf_cpm_control_interval_expired, p_iocr, - &p_cpm->ci_timeout); + &p_cpm->ci_timeout, + os_get_current_time_us()); if (ret != 0) { LOG_ERROR (PF_CPM_LOG, "CPM_DRV_SW(%d): Timeout not started\n", __LINE__); diff --git a/src/common/pf_dcp.c b/src/common/pf_dcp.c index aebf1a012..59b2ed707 100644 --- a/src/common/pf_dcp.c +++ b/src/common/pf_dcp.c @@ -36,6 +36,7 @@ */ #ifdef UNIT_TEST +#define os_get_current_time_us mock_os_get_current_time_us #endif #include @@ -241,11 +242,12 @@ static void pf_dcp_restart_sam_timeout (pnet_t * net, const pnet_ethaddr_t * mac memcpy (&net->dcp_sam, mac, sizeof (net->dcp_sam)); (void)pf_scheduler_restart ( - net, + &net->scheduler_data, PF_DCP_SAM_TIMEOUT, pf_dcp_clear_sam, NULL, - &net->dcp_sam_timeout); + &net->dcp_sam_timeout, + os_get_current_time_us()); } /** @@ -588,7 +590,6 @@ static int pf_dcp_get_req ( * @param current_time In: The current system time, in microseconds, * when the scheduler is started to execute * stored tasks. - * Not used here. */ static void pf_dcp_control_signal_led ( pnet_t * net, @@ -626,11 +627,12 @@ static void pf_dcp_control_signal_led ( /* Schedule another round */ (void)pf_scheduler_add ( - net, + &net->scheduler_data, PF_DCP_SIGNAL_LED_HALF_INTERVAL, pf_dcp_control_signal_led, (void *)(uintptr_t)state, - &net->dcp_led_timeout); + &net->dcp_led_timeout, + current_time); } else { @@ -657,11 +659,12 @@ int pf_dcp_trigger_signal_led (pnet_t * net) "DCP(%d): Received request to flash LED\n", __LINE__); (void)pf_scheduler_add ( - net, + &net->scheduler_data, PF_DCP_SIGNAL_LED_HALF_INTERVAL, pf_dcp_control_signal_led, (void *)(2 * PF_DCP_SIGNAL_LED_NUMBER_OF_FLASHES - 1), - &net->dcp_led_timeout); + &net->dcp_led_timeout, + os_get_current_time_us()); } else { @@ -1942,11 +1945,12 @@ static int pf_dcp_identify_req ( #endif (void)pf_scheduler_add ( - net, + &net->scheduler_data, response_delay, pf_dcp_responder, p_rsp, - &net->dcp_identresp_timeout); + &net->dcp_identresp_timeout, + os_get_current_time_us()); /* Note: Do not free p_rsp, it is used in pf_dcp_responder() */ } diff --git a/src/common/pf_file.c b/src/common/pf_file.c index 028e7da71..c8aaf5bf3 100644 --- a/src/common/pf_file.c +++ b/src/common/pf_file.c @@ -23,9 +23,10 @@ */ #ifdef UNIT_TEST -#define pnal_clear_file mock_pnal_clear_file -#define pnal_load_file mock_pnal_load_file -#define pnal_save_file mock_pnal_save_file +#define pnal_clear_file mock_pnal_clear_file +#define pnal_load_file mock_pnal_load_file +#define pnal_save_file mock_pnal_save_file +#define os_get_current_time_us mock_os_get_current_time_us #endif #include "pf_includes.h" diff --git a/src/common/pf_lldp.c b/src/common/pf_lldp.c index 8d291f530..8b970482a 100644 --- a/src/common/pf_lldp.c +++ b/src/common/pf_lldp.c @@ -16,6 +16,7 @@ #ifdef UNIT_TEST #define pnal_get_system_uptime_10ms mock_pnal_get_system_uptime_10ms #define pnal_get_interface_index mock_pnal_get_interface_index +#define os_get_current_time_us mock_os_get_current_time_us #endif #define STRINGIFY(s) STRINGIFIED (s) @@ -509,7 +510,9 @@ void pf_lldp_restart_peer_timeout ( { pf_port_t * p_port_data = pf_port_get_state (net, loc_port_num); - pf_scheduler_remove_if_running (net, &p_port_data->lldp.rx_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &p_port_data->lldp.rx_timeout); /* * Profinet states that the time to live shall be 20 seconds, @@ -528,11 +531,12 @@ void pf_lldp_restart_peer_timeout ( if ( pf_scheduler_add ( - net, + &net->scheduler_data, timeout_in_secs * 1000000, pf_lldp_receive_timeout, p_port_data, - &p_port_data->lldp.rx_timeout) != 0) + &p_port_data->lldp.rx_timeout, + os_get_current_time_us()) != 0) { LOG_ERROR ( PF_LLDP_LOG, @@ -545,7 +549,9 @@ void pf_lldp_stop_peer_timeout (pnet_t * net, int loc_port_num) { pf_port_t * p_port_data = pf_port_get_state (net, loc_port_num); - pf_scheduler_remove_if_running (net, &p_port_data->lldp.rx_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &p_port_data->lldp.rx_timeout); } /** @@ -1081,7 +1087,7 @@ static void pf_lldp_send (pnet_t * net, int loc_port_num) * * @param net InOut: The p-net stack instance * @param arg In: Reference to port_data - * @param current_time In: Not used. + * @param current_time In: Current time, in microseconds */ static void pf_lldp_trigger_sending ( pnet_t * net, @@ -1094,11 +1100,12 @@ static void pf_lldp_trigger_sending ( if ( pf_scheduler_add ( - net, + &net->scheduler_data, PF_LLDP_SEND_INTERVAL * 1000, pf_lldp_trigger_sending, p_port_data, - &p_port_data->lldp.tx_timeout) != 0) + &p_port_data->lldp.tx_timeout, + current_time) != 0) { LOG_ERROR ( PF_LLDP_LOG, @@ -1122,11 +1129,12 @@ static void pf_lldp_tx_restart (pnet_t * net, int loc_port_num, bool send) if ( pf_scheduler_restart ( - net, + &net->scheduler_data, PF_LLDP_SEND_INTERVAL * 1000, pf_lldp_trigger_sending, p_port_data, - &p_port_data->lldp.tx_timeout) != 0) + &p_port_data->lldp.tx_timeout, + os_get_current_time_us()) != 0) { LOG_ERROR ( PF_ETH_LOG, @@ -1183,7 +1191,9 @@ void pf_lldp_send_disable (pnet_t * net, int loc_port_num) "LLDP(%d): Disabling LLDP transmission for port %d\n", __LINE__, loc_port_num); - pf_scheduler_remove_if_running (net, &p_port_data->lldp.tx_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &p_port_data->lldp.tx_timeout); } /** diff --git a/src/common/pf_ppm_driver_sw.c b/src/common/pf_ppm_driver_sw.c index 89645927e..3e4dc0f52 100644 --- a/src/common/pf_ppm_driver_sw.c +++ b/src/common/pf_ppm_driver_sw.c @@ -18,6 +18,10 @@ #include #include +#ifdef UNIT_TEST +// TODO #define os_get_current_time_us mock_os_get_current_time_us +#endif + /** * @internal * Send the process data frame. @@ -54,11 +58,12 @@ static void pf_ppm_drv_sw_send (pnet_t * net, void * arg, uint32_t current_time) delay = p_arg->ppm.next_exec - current_time; if ( pf_scheduler_add ( - net, + &net->scheduler_data, delay, pf_ppm_drv_sw_send, arg, - &p_arg->ppm.ci_timeout) == 0) + &p_arg->ppm.ci_timeout, + current_time) == 0) { p_arg->ppm.trx_cnt++; if (p_arg->ppm.first_transmit == false) @@ -97,11 +102,12 @@ int pf_ppm_drv_sw_activate_req (pnet_t * net, pf_ar_t * p_ar, uint32_t crep) pf_scheduler_init_handle (&p_ppm->ci_timeout, "ppm"); ret = pf_scheduler_add ( - net, + &net->scheduler_data, p_ppm->control_interval, pf_ppm_drv_sw_send, p_iocr, - &p_ppm->ci_timeout); + &p_ppm->ci_timeout, + os_get_current_time_us()); return ret; } @@ -118,7 +124,7 @@ int pf_ppm_drv_sw_close_req (pnet_t * net, pf_ar_t * p_ar, uint32_t crep) p_ar->arep, crep); - pf_scheduler_remove_if_running (net, &p_ppm->ci_timeout); + pf_scheduler_remove_if_running (&net->scheduler_data, &p_ppm->ci_timeout); return 0; } diff --git a/src/common/pf_scheduler.c b/src/common/pf_scheduler.c index 661e7449e..26838fdaa 100644 --- a/src/common/pf_scheduler.c +++ b/src/common/pf_scheduler.c @@ -19,10 +19,11 @@ * * Use the scheduler to execute callbacks after a known delay time. * + * This file should not use \a pnet_t, except in \a pf_scheduler_tick() where it + * is passed as an argument to the callback. */ #ifdef UNIT_TEST -#define os_get_current_time_us mock_os_get_current_time_us #endif #include "pf_includes.h" @@ -30,34 +31,56 @@ #include #include -static bool pf_scheduler_is_linked (pnet_t * net, uint32_t first, uint32_t ix) +/**************** Utilities for linked lists ************************/ + +/** + * Check if a timeout index is present in a linked list. + * + * @param scheduler_data InOut: Scheduler instance + * @param head In: Head of the list + * @param ix In: Timeout index to look for + * @return true if timeout index is found in the list + */ +static bool pf_scheduler_is_linked ( + volatile pf_scheduler_data_t * scheduler_data, + uint32_t head, + uint32_t ix) { - bool ret = false; + uint32_t current = head; uint32_t cnt = 0; /* Guard against infinite loop */ - if (ix < PF_MAX_TIMEOUTS) + if (ix >= PF_MAX_TIMEOUTS) + { + return false; + } + + while (current < PF_MAX_TIMEOUTS) { - while (first < PF_MAX_TIMEOUTS) + CC_ASSERT (cnt < PF_MAX_TIMEOUTS); + if (current == ix) { - CC_ASSERT (cnt < PF_MAX_TIMEOUTS); - if (first == ix) - { - ret = true; - } - first = net->scheduler_timeouts[first].next; - cnt++; + return true; } + current = scheduler_data->timeouts[current].next; + cnt++; } - return ret; + return false; } +/** + * Unlink a timeout from a linked list + * + * @param scheduler_data InOut: Scheduler instance + * @param p_head InOut: Head of the list + * @param ix In: Timeout index to unlink. Might be invalid if + * the list is empty. + */ static void pf_scheduler_unlink ( - pnet_t * net, - volatile uint32_t * p_q, + volatile pf_scheduler_data_t * scheduler_data, + volatile uint32_t * p_head, uint32_t ix) { - /* Unlink from busy list */ uint32_t prev_ix; uint32_t next_ix; @@ -65,40 +88,51 @@ static void pf_scheduler_unlink ( { LOG_ERROR ( PNET_LOG, - "Sched(%d): ix (%u) is invalid\n", + "Sched(%d): Timeout index %u is invalid.\n", __LINE__, (unsigned)ix); + return; } - else if (pf_scheduler_is_linked (net, *p_q, ix) == false) + + if (pf_scheduler_is_linked (scheduler_data, *p_head, ix) == false) { LOG_ERROR ( PNET_LOG, - "Sched(%d): %s is not in Q\n", + "Sched(%d): Timeout %s is not in this queue\n", __LINE__, - net->scheduler_timeouts[ix].name); + scheduler_data->timeouts[ix].name); + return; } - else + + prev_ix = scheduler_data->timeouts[ix].prev; + next_ix = scheduler_data->timeouts[ix].next; + if (*p_head == ix) { - prev_ix = net->scheduler_timeouts[ix].prev; - next_ix = net->scheduler_timeouts[ix].next; - if (*p_q == ix) - { - *p_q = next_ix; - } - if (next_ix < PF_MAX_TIMEOUTS) - { - net->scheduler_timeouts[next_ix].prev = prev_ix; - } - if (prev_ix < PF_MAX_TIMEOUTS) - { - net->scheduler_timeouts[prev_ix].next = next_ix; - } + *p_head = next_ix; + } + if (next_ix < PF_MAX_TIMEOUTS) + { + scheduler_data->timeouts[next_ix].prev = prev_ix; + } + if (prev_ix < PF_MAX_TIMEOUTS) + { + scheduler_data->timeouts[prev_ix].next = next_ix; } } +/** + * Link a timeout after a position in a linked list + * + * Will be put first in the list if \a pos is PF_MAX_TIMEOUTS or larger. + * + * @param scheduler_data InOut: Scheduler instance + * @param p_head InOut: Head of the list + * @param ix In: Timeout index to link + * @param pos In: Position. + */ static void pf_scheduler_link_after ( - pnet_t * net, - volatile uint32_t * p_q, + volatile pf_scheduler_data_t * scheduler_data, + volatile uint32_t * p_head, uint32_t ix, uint32_t pos) { @@ -111,53 +145,68 @@ static void pf_scheduler_link_after ( "Sched(%d): ix (%u) is invalid\n", __LINE__, (unsigned)ix); + return; } - else if (pf_scheduler_is_linked (net, *p_q, ix) == true) + + if (pf_scheduler_is_linked (scheduler_data, *p_head, ix) == true) { LOG_ERROR ( PNET_LOG, - "Sched(%d): %s is already in Q\n", + "Sched(%d): %s is already in the queue\n", __LINE__, - net->scheduler_timeouts[ix].name); + scheduler_data->timeouts[ix].name); + return; } - else if (pos >= PF_MAX_TIMEOUTS) + + if (pos >= PF_MAX_TIMEOUTS) { - /* Put first in possible non-empty Q */ - net->scheduler_timeouts[ix].prev = PF_MAX_TIMEOUTS; - net->scheduler_timeouts[ix].next = *p_q; - if (*p_q < PF_MAX_TIMEOUTS) + /* Insert it first in possibly non-empty queue */ + scheduler_data->timeouts[ix].prev = PF_MAX_TIMEOUTS; + scheduler_data->timeouts[ix].next = *p_head; + if (*p_head < PF_MAX_TIMEOUTS) { - net->scheduler_timeouts[*p_q].prev = ix; + scheduler_data->timeouts[*p_head].prev = ix; } - *p_q = ix; + *p_head = ix; + return; } - else if (*p_q >= PF_MAX_TIMEOUTS) + + if (*p_head >= PF_MAX_TIMEOUTS) { - /* Q is empty - insert first in Q */ - net->scheduler_timeouts[ix].prev = PF_MAX_TIMEOUTS; - net->scheduler_timeouts[ix].next = PF_MAX_TIMEOUTS; + /* This queue is empty - Insert it first in the queue */ + scheduler_data->timeouts[ix].prev = PF_MAX_TIMEOUTS; + scheduler_data->timeouts[ix].next = PF_MAX_TIMEOUTS; - *p_q = ix; + *p_head = ix; + return; } - else - { - next_ix = net->scheduler_timeouts[pos].next; - if (next_ix < PF_MAX_TIMEOUTS) - { - net->scheduler_timeouts[next_ix].prev = ix; - } - net->scheduler_timeouts[pos].next = ix; + next_ix = scheduler_data->timeouts[pos].next; - net->scheduler_timeouts[ix].prev = pos; - net->scheduler_timeouts[ix].next = next_ix; + if (next_ix < PF_MAX_TIMEOUTS) + { + scheduler_data->timeouts[next_ix].prev = ix; } + scheduler_data->timeouts[pos].next = ix; + + scheduler_data->timeouts[ix].prev = pos; + scheduler_data->timeouts[ix].next = next_ix; } +/** + * Link a timeout before a position in a linked list + * + * Will be put first in the list if \a pos is PF_MAX_TIMEOUTS or larger. + * + * @param scheduler_data InOut: Scheduler instance + * @param p_head InOut: Head of the list + * @param ix In: Timeout index to link + * @param pos In: Position + */ static void pf_scheduler_link_before ( - pnet_t * net, - volatile uint32_t * p_q, + volatile pf_scheduler_data_t * scheduler_data, + volatile uint32_t * p_head, uint32_t ix, uint32_t pos) { @@ -170,56 +219,84 @@ static void pf_scheduler_link_before ( "Sched(%d): ix (%u) is invalid\n", __LINE__, (unsigned)ix); + return; } - else if (pf_scheduler_is_linked (net, *p_q, ix) == true) + + if (pf_scheduler_is_linked (scheduler_data, *p_head, ix) == true) { LOG_ERROR ( PNET_LOG, - "Sched(%d): %s is already in Q\n", + "Sched(%d): %s is already in this queue\n", __LINE__, - net->scheduler_timeouts[ix].name); + scheduler_data->timeouts[ix].name); + return; } - else if (pos >= PF_MAX_TIMEOUTS) + + if (pos >= PF_MAX_TIMEOUTS) { - /* Put first in possible non-empty Q */ - net->scheduler_timeouts[ix].prev = PF_MAX_TIMEOUTS; - net->scheduler_timeouts[ix].next = *p_q; - if (*p_q < PF_MAX_TIMEOUTS) + /* Insert it first in possibly non-empty queue */ + scheduler_data->timeouts[ix].prev = PF_MAX_TIMEOUTS; + scheduler_data->timeouts[ix].next = *p_head; + if (*p_head < PF_MAX_TIMEOUTS) { - net->scheduler_timeouts[*p_q].prev = ix; + scheduler_data->timeouts[*p_head].prev = ix; } - *p_q = ix; + *p_head = ix; + return; } - else if (*p_q >= PF_MAX_TIMEOUTS) + + if (*p_head >= PF_MAX_TIMEOUTS) { - /* Q is empty - insert first in Q */ - net->scheduler_timeouts[ix].prev = PF_MAX_TIMEOUTS; - net->scheduler_timeouts[ix].next = PF_MAX_TIMEOUTS; + /* This queue is empty - Insert it first in the queue */ + scheduler_data->timeouts[ix].prev = PF_MAX_TIMEOUTS; + scheduler_data->timeouts[ix].next = PF_MAX_TIMEOUTS; - *p_q = ix; + *p_head = ix; + return; } - else - { - prev_ix = net->scheduler_timeouts[pos].prev; - if (prev_ix < PF_MAX_TIMEOUTS) - { - net->scheduler_timeouts[prev_ix].next = ix; - } - net->scheduler_timeouts[pos].prev = ix; + prev_ix = scheduler_data->timeouts[pos].prev; - net->scheduler_timeouts[ix].next = pos; - net->scheduler_timeouts[ix].prev = prev_ix; + if (prev_ix < PF_MAX_TIMEOUTS) + { + scheduler_data->timeouts[prev_ix].next = ix; + } + scheduler_data->timeouts[pos].prev = ix; - if (*p_q == pos) - { - /* ix is now first in the Q */ - *p_q = ix; - } + scheduler_data->timeouts[ix].next = pos; + scheduler_data->timeouts[ix].prev = prev_ix; + + if (*p_head == pos) + { + /* ix is now first in the queue */ + *p_head = ix; } } +/** + * Check if timeout \a a is scheduled before timeout \a b. + * + * + * @param scheduler_data InOut: Scheduler instance + * @param ix_a In: Index for timeout a + * @param ix_b In: Index for timeout b + * @return true if timeout a is scheduled before timeout b, + * or if they are simultaneous. + */ +bool pf_scheduler_is_time_before ( + volatile pf_scheduler_data_t * scheduler_data, + uint32_t ix_a, + uint32_t ix_b) +{ + + return ((int32_t) ( + scheduler_data->timeouts[ix_a].when - + scheduler_data->timeouts[ix_b].when)) <= 0; +} + +/****************************** Public functions ***************************/ + void pf_scheduler_reset_handle (pf_scheduler_handle_t * handle) { handle->timer_index = UINT32_MAX; @@ -247,75 +324,83 @@ bool pf_scheduler_is_running (const pf_scheduler_handle_t * handle) } void pf_scheduler_remove_if_running ( - pnet_t * net, + volatile pf_scheduler_data_t * scheduler_data, pf_scheduler_handle_t * handle) { if (pf_scheduler_is_running (handle)) { - pf_scheduler_remove (net, handle); + pf_scheduler_remove (scheduler_data, handle); } } int pf_scheduler_restart ( - pnet_t * net, + volatile pf_scheduler_data_t * scheduler_data, uint32_t delay, pf_scheduler_timeout_ftn_t cb, void * arg, - pf_scheduler_handle_t * handle) + pf_scheduler_handle_t * handle, + uint32_t current_time) { - pf_scheduler_remove_if_running (net, handle); - return pf_scheduler_add (net, delay, cb, arg, handle); + pf_scheduler_remove_if_running (scheduler_data, handle); + return pf_scheduler_add (scheduler_data, delay, cb, arg, handle, current_time); } -void pf_scheduler_init (pnet_t * net, uint32_t tick_interval) +void pf_scheduler_init ( + volatile pf_scheduler_data_t * scheduler_data, + uint32_t tick_interval) { uint32_t ix; + uint32_t j; - net->scheduler_timeout_first = PF_MAX_TIMEOUTS; /* Nothing in queue */ - net->scheduler_timeout_free = PF_MAX_TIMEOUTS; /* Nothing in queue. */ + scheduler_data->busylist_head = PF_MAX_TIMEOUTS; /* Nothing in queue */ + scheduler_data->freelist_head = PF_MAX_TIMEOUTS; /* Nothing in queue. */ - if (net->scheduler_timeout_mutex == NULL) + if (scheduler_data->mutex == NULL) { - net->scheduler_timeout_mutex = os_mutex_create(); + scheduler_data->mutex = os_mutex_create(); } - memset ((void *)net->scheduler_timeouts, 0, sizeof (net->scheduler_timeouts)); + memset ( + (void *)scheduler_data->timeouts, + 0, + sizeof (scheduler_data->timeouts)); - net->scheduler_tick_interval = tick_interval; - CC_ASSERT (net->scheduler_tick_interval > 0); + scheduler_data->tick_interval = tick_interval; + CC_ASSERT (scheduler_data->tick_interval > 0); /* Link all entries into a list and put them into the free queue. */ - for (ix = PF_MAX_TIMEOUTS; ix > 0; ix--) + for (j = PF_MAX_TIMEOUTS; j > 0; j--) { - net->scheduler_timeouts[ix - 1].name = ""; - net->scheduler_timeouts[ix - 1].in_use = false; + ix = j - 1; + scheduler_data->timeouts[ix].name = ""; + scheduler_data->timeouts[ix].in_use = false; pf_scheduler_link_before ( - net, - &net->scheduler_timeout_free, - ix - 1, - net->scheduler_timeout_free); + scheduler_data, + &scheduler_data->freelist_head, + ix, + scheduler_data->freelist_head); } } int pf_scheduler_add ( - pnet_t * net, + volatile pf_scheduler_data_t * scheduler_data, uint32_t delay, pf_scheduler_timeout_ftn_t cb, void * arg, - pf_scheduler_handle_t * handle) + pf_scheduler_handle_t * handle, + uint32_t current_time) { uint32_t ix_this; uint32_t ix_prev; uint32_t ix_free; - uint32_t now = os_get_current_time_us(); delay = - pf_scheduler_sanitize_delay (delay, net->scheduler_tick_interval, true); + pf_scheduler_sanitize_delay (delay, scheduler_data->tick_interval, true); - os_mutex_lock (net->scheduler_timeout_mutex); /* Unlink from the free list */ - ix_free = net->scheduler_timeout_free; - pf_scheduler_unlink (net, &net->scheduler_timeout_free, ix_free); - os_mutex_unlock (net->scheduler_timeout_mutex); + os_mutex_lock (scheduler_data->mutex); + ix_free = scheduler_data->freelist_head; + pf_scheduler_unlink (scheduler_data, &scheduler_data->freelist_head, ix_free); + os_mutex_unlock (scheduler_data->mutex); if (ix_free >= PF_MAX_TIMEOUTS) { @@ -327,63 +412,64 @@ int pf_scheduler_add ( return -1; } - net->scheduler_timeouts[ix_free].in_use = true; - net->scheduler_timeouts[ix_free].name = handle->name; - net->scheduler_timeouts[ix_free].cb = cb; - net->scheduler_timeouts[ix_free].arg = arg; - net->scheduler_timeouts[ix_free].when = now + delay; + scheduler_data->timeouts[ix_free].in_use = true; + scheduler_data->timeouts[ix_free].name = handle->name; + scheduler_data->timeouts[ix_free].cb = cb; + scheduler_data->timeouts[ix_free].arg = arg; + scheduler_data->timeouts[ix_free].when = current_time + delay; - os_mutex_lock (net->scheduler_timeout_mutex); - if (net->scheduler_timeout_first >= PF_MAX_TIMEOUTS) + os_mutex_lock (scheduler_data->mutex); + if (scheduler_data->busylist_head >= PF_MAX_TIMEOUTS) { - /* Put into empty q */ + /* Put into empty queue */ pf_scheduler_link_before ( - net, - &net->scheduler_timeout_first, + scheduler_data, + &scheduler_data->busylist_head, ix_free, PF_MAX_TIMEOUTS); } - else if ( - ((int32_t) ( - net->scheduler_timeouts[ix_free].when - - net->scheduler_timeouts[net->scheduler_timeout_first].when)) <= 0) + else if (pf_scheduler_is_time_before ( + scheduler_data, + ix_free, + scheduler_data->busylist_head)) + { - /* Put first in non-empty q */ + /* Put first in non-empty queue */ pf_scheduler_link_before ( - net, - &net->scheduler_timeout_first, + scheduler_data, + &scheduler_data->busylist_head, ix_free, - net->scheduler_timeout_first); + scheduler_data->busylist_head); } else { - /* Find pos in non-empty q */ - ix_prev = net->scheduler_timeout_first; - ix_this = net->scheduler_timeouts[net->scheduler_timeout_first].next; + /* Find correct position in non-empty queue */ + ix_prev = scheduler_data->busylist_head; + ix_this = scheduler_data->timeouts[scheduler_data->busylist_head].next; while ((ix_this < PF_MAX_TIMEOUTS) && - (((int32_t) ( - net->scheduler_timeouts[ix_free].when - - net->scheduler_timeouts[ix_this].when)) > 0)) + pf_scheduler_is_time_before (scheduler_data, ix_this, ix_free)) { ix_prev = ix_this; - ix_this = net->scheduler_timeouts[ix_this].next; + ix_this = scheduler_data->timeouts[ix_this].next; } /* Put after ix_prev */ pf_scheduler_link_after ( - net, - &net->scheduler_timeout_first, + scheduler_data, + &scheduler_data->busylist_head, ix_free, ix_prev); } - os_mutex_unlock (net->scheduler_timeout_mutex); + os_mutex_unlock (scheduler_data->mutex); handle->timer_index = ix_free + 1; /* Make sure 0 is invalid. */ return 0; } -void pf_scheduler_remove (pnet_t * net, pf_scheduler_handle_t * handle) +void pf_scheduler_remove ( + volatile pf_scheduler_data_t * scheduler_data, + pf_scheduler_handle_t * handle) { uint16_t ix; @@ -396,101 +482,103 @@ void pf_scheduler_remove (pnet_t * net, pf_scheduler_handle_t * handle) __LINE__, handle->timer_index, handle->name); + return; + } + + /* See pf_scheduler_add() for handle->timer_index details */ + ix = handle->timer_index - 1; + os_mutex_lock (scheduler_data->mutex); + + if (scheduler_data->timeouts[ix].name != handle->name) + { + LOG_ERROR ( + PNET_LOG, + "SCHEDULER(%d): Expected \"%s\" but got \"%s\". No removal.\n", + __LINE__, + scheduler_data->timeouts[ix].name, + handle->name); + } + else if (scheduler_data->timeouts[ix].in_use == false) + { + LOG_DEBUG ( + PNET_LOG, + "SCHEDULER(%d): Tried to remove timeout \"%s\", but it has already " + "been triggered.\n", + __LINE__, + handle->name); } else { - /* See pf_scheduler_add() for handle->timer_index details */ - ix = handle->timer_index - 1; - os_mutex_lock (net->scheduler_timeout_mutex); + /* Unlink from busy list */ + pf_scheduler_unlink (scheduler_data, &scheduler_data->busylist_head, ix); - if (net->scheduler_timeouts[ix].name != handle->name) - { - LOG_ERROR ( - PNET_LOG, - "SCHEDULER(%d): Expected %s but got %s. No removal.\n", - __LINE__, - net->scheduler_timeouts[ix].name, - handle->name); - } - else if (net->scheduler_timeouts[ix].in_use == false) - { - LOG_DEBUG ( - PNET_LOG, - "SCHEDULER(%d): Tried to remove timeout \"%s\", but it has already " - "been triggered.\n", - __LINE__, - handle->name); - } - else - { - /* Unlink from busy list */ - pf_scheduler_unlink (net, &net->scheduler_timeout_first, ix); - - /* Insert into free list. */ - net->scheduler_timeouts[ix].in_use = false; - pf_scheduler_link_before ( - net, - &net->scheduler_timeout_free, - ix, - net->scheduler_timeout_free); - - handle->timer_index = UINT32_MAX; - } + /* Insert into free list. */ + scheduler_data->timeouts[ix].in_use = false; + scheduler_data->timeouts[ix].name = ""; + pf_scheduler_link_before ( + scheduler_data, + &scheduler_data->freelist_head, + ix, + scheduler_data->freelist_head); - os_mutex_unlock (net->scheduler_timeout_mutex); + handle->timer_index = UINT32_MAX; } + + os_mutex_unlock (scheduler_data->mutex); } -void pf_scheduler_tick (pnet_t * net) +void pf_scheduler_tick ( + pnet_t * net, + volatile pf_scheduler_data_t * scheduler_data, + uint32_t current_time) { uint32_t ix; pf_scheduler_timeout_ftn_t ftn; void * arg; - uint32_t pf_current_time = os_get_current_time_us(); - os_mutex_lock (net->scheduler_timeout_mutex); + os_mutex_lock (scheduler_data->mutex); /* Send event to all expired delay entries. */ - while ((net->scheduler_timeout_first < PF_MAX_TIMEOUTS) && + while ((scheduler_data->busylist_head < PF_MAX_TIMEOUTS) && ((int32_t) ( - pf_current_time - - net->scheduler_timeouts[net->scheduler_timeout_first].when) >= 0)) + current_time - + scheduler_data->timeouts[scheduler_data->busylist_head].when) >= 0)) { /* Unlink from busy list */ - ix = net->scheduler_timeout_first; - pf_scheduler_unlink (net, &net->scheduler_timeout_first, ix); + ix = scheduler_data->busylist_head; + pf_scheduler_unlink (scheduler_data, &scheduler_data->busylist_head, ix); - ftn = net->scheduler_timeouts[ix].cb; - arg = net->scheduler_timeouts[ix].arg; + ftn = scheduler_data->timeouts[ix].cb; + arg = scheduler_data->timeouts[ix].arg; /* Insert into free list. */ - net->scheduler_timeouts[ix].in_use = false; + scheduler_data->timeouts[ix].in_use = false; pf_scheduler_link_before ( - net, - &net->scheduler_timeout_free, + scheduler_data, + &scheduler_data->freelist_head, ix, - net->scheduler_timeout_free); + scheduler_data->freelist_head); /* Send event without holding the mutex. */ - os_mutex_unlock (net->scheduler_timeout_mutex); - ftn (net, arg, pf_current_time); - os_mutex_lock (net->scheduler_timeout_mutex); + os_mutex_unlock (scheduler_data->mutex); + ftn (net, arg, current_time); + os_mutex_lock (scheduler_data->mutex); } - os_mutex_unlock (net->scheduler_timeout_mutex); + os_mutex_unlock (scheduler_data->mutex); } -void pf_scheduler_show (pnet_t * net) +void pf_scheduler_show ( + volatile pf_scheduler_data_t * scheduler_data, + uint32_t current_time) { uint32_t ix; - printf ( - "Scheduler (time now=%u microseconds):\n", - (unsigned)os_get_current_time_us()); + printf ("Scheduler (time now=%" PRIu32 " microseconds):\n", current_time); - if (net->scheduler_timeout_mutex != NULL) + if (scheduler_data->mutex != NULL) { - os_mutex_lock (net->scheduler_timeout_mutex); + os_mutex_lock (scheduler_data->mutex); } printf ( @@ -506,42 +594,41 @@ void pf_scheduler_show (pnet_t * net) printf ( "[%02u] %-14s %-6s %-6u %-6u %u\n", (unsigned)ix, - net->scheduler_timeouts[ix].name, - net->scheduler_timeouts[ix].in_use ? "true" : "false", - (unsigned)net->scheduler_timeouts[ix].next, - (unsigned)net->scheduler_timeouts[ix].prev, - (unsigned)net->scheduler_timeouts[ix].when); + scheduler_data->timeouts[ix].name, + scheduler_data->timeouts[ix].in_use ? "true" : "false", + (unsigned)scheduler_data->timeouts[ix].next, + (unsigned)scheduler_data->timeouts[ix].prev, + (unsigned)scheduler_data->timeouts[ix].when); } - if (net->scheduler_timeout_mutex != NULL) + if (scheduler_data->mutex != NULL) { printf ("Free list:\n"); - ix = net->scheduler_timeout_free; + ix = scheduler_data->freelist_head; while (ix < PF_MAX_TIMEOUTS) { printf ("%u ", (unsigned)ix); - ix = net->scheduler_timeouts[ix].next; + ix = scheduler_data->timeouts[ix].next; } printf ("\nBusy list:\n"); - ix = net->scheduler_timeout_first; + ix = scheduler_data->busylist_head; while (ix < PF_MAX_TIMEOUTS) { printf ( "%u (%u) ", (unsigned)ix, - (unsigned)net->scheduler_timeouts[ix].when); - ix = net->scheduler_timeouts[ix].next; + (unsigned)scheduler_data->timeouts[ix].when); + ix = scheduler_data->timeouts[ix].next; } - os_mutex_unlock (net->scheduler_timeout_mutex); + os_mutex_unlock (scheduler_data->mutex); } printf ("\n"); - printf ( - "Uptime (in quanta of 10 ms): %" PRIu32 " \n", - pnal_get_system_uptime_10ms()); } +/********************** Numerical utilities *****************************/ + /** * @internal * Sanitize the delay to use with the scheduler, by taking the stack cycle time @@ -595,11 +682,14 @@ void pf_scheduler_show (pnet_t * net) * firing is largely dependent on the underlying operating system's ablitity to * trigger the stack execution with a high time precision. * + * Will assert for stack_cycle_time == 0. + * * @param wanted_delay In: Delay in microseconds. * @param stack_cycle_time In: Stack cycle time in - * microseconds. Must be larger than 0. + * microseconds. + * Must be larger than 0. * @param schedule_half_tick_in_advance In: Schedule event slightly - * earlier, to not be missed. + * earlier, to not be missed. * @return Number of microseconds of delay to use with the scheduler. */ uint32_t pf_scheduler_sanitize_delay ( diff --git a/src/common/pf_scheduler.h b/src/common/pf_scheduler.h index bfd7b1e61..e8095a86c 100644 --- a/src/common/pf_scheduler.h +++ b/src/common/pf_scheduler.h @@ -20,15 +20,23 @@ extern "C" { #endif +#include +#include + #define PF_SCHEDULER_MAX_DELAY_US 100000000U /* 100 seconds */ /** * Initialize the scheduler. - * @param net InOut: The p-net stack instance + * + * Will assert for tick_interval == 0 + * + * @param scheduler_data InOut: Scheduler instance * @param tick_interval In: System calls the tick function at these - * intervals, in microseconds. + * intervals, in microseconds. */ -void pf_scheduler_init (pnet_t * net, uint32_t tick_interval); +void pf_scheduler_init ( + volatile pf_scheduler_data_t * scheduler_data, + uint32_t tick_interval); /** * Initialize a timeout handle. @@ -90,22 +98,24 @@ const char * pf_scheduler_get_name (const pf_scheduler_handle_t * handle); * done by calling \a pf_scheduler_reset_handle() or again calling * \ pf_scheduler_add(). * - * @param net InOut: The p-net stack instance + * @param scheduler_data InOut: Scheduler instance * @param delay In: The delay until the function shall be called, * in microseconds. Max * PF_SCHEDULER_MAX_DELAY_US. * @param cb In: The call-back. * @param arg In: Argument to the call-back. * @param handle InOut: Timeout handle. + * @param current_time In: Current time in microseconds * @return 0 if the call-back was scheduled. * -1 if an error occurred. */ int pf_scheduler_add ( - pnet_t * net, + volatile pf_scheduler_data_t * scheduler_data, uint32_t delay, pf_scheduler_timeout_ftn_t cb, void * arg, - pf_scheduler_handle_t * handle); + pf_scheduler_handle_t * handle, + uint32_t current_time); /** * Re-schedule a call-back at a specific time. @@ -114,57 +124,70 @@ int pf_scheduler_add ( * * Do not use in a scheduler callback. * - * @param net InOut: The p-net stack instance + * @param scheduler_data InOut: Scheduler instance * @param delay In: The delay until the function shall be called, * in microseconds. Max * PF_SCHEDULER_MAX_DELAY_US. * @param cb In: The call-back. * @param arg In: Argument to the call-back. * @param handle InOut: Timeout handle. + * @param current_time In: Current time in microseconds * @return 0 if the call-back was scheduled. * -1 if an error occurred. */ int pf_scheduler_restart ( - pnet_t * net, + volatile pf_scheduler_data_t * scheduler_data, uint32_t delay, pf_scheduler_timeout_ftn_t cb, void * arg, - pf_scheduler_handle_t * handle); + pf_scheduler_handle_t * handle, + uint32_t current_time); /** * Stop a timeout, if it is running. * * Silently ignoring the timeout if not running. * - * @param net InOut: The p-net stack instance + * @param scheduler_data InOut: Scheduler instance * @param handle InOut: Timeout handle. */ void pf_scheduler_remove_if_running ( - pnet_t * net, + volatile pf_scheduler_data_t * scheduler_data, pf_scheduler_handle_t * handle); /** * Stop a timeout. - * @param net InOut: The p-net stack instance + * @param scheduler_data InOut: Scheduler instance * @param handle InOut: Timeout handle. */ -void pf_scheduler_remove (pnet_t * net, pf_scheduler_handle_t * handle); +void pf_scheduler_remove ( + volatile pf_scheduler_data_t * scheduler_data, + pf_scheduler_handle_t * handle); /** * Check if it is time to call a scheduled call-back. * Run scheduled call-backs - if any. * @param net InOut: The p-net stack instance + * Will be passed to the callback + * @param scheduler_data InOut: Scheduler instance + * @param current_time In: Current time in microseconds */ -void pf_scheduler_tick (pnet_t * net); +void pf_scheduler_tick ( + pnet_t * net, + volatile pf_scheduler_data_t * scheduler_data, + uint32_t current_time); /** * Show scheduler (busy and free) instances. * * Locks the mutex temporarily. * - * @param net InOut: The p-net stack instance + * @param scheduler_data InOut: Scheduler instance + * @param current_time In: Current time in microseconds */ -void pf_scheduler_show (pnet_t * net); +void pf_scheduler_show ( + volatile pf_scheduler_data_t * scheduler_data, + uint32_t current_time); /************ Internal functions, made available for unit testing ************/ @@ -173,6 +196,11 @@ uint32_t pf_scheduler_sanitize_delay ( uint32_t stack_cycle_time, bool schedule_half_tick_in_advance); +bool pf_scheduler_is_time_before ( + volatile pf_scheduler_data_t * scheduler_data, + uint32_t ix_a, + uint32_t ix_b); + #ifdef __cplusplus } #endif diff --git a/src/device/pf_cmina.c b/src/device/pf_cmina.c index b287a4b99..ae8ce1e4c 100644 --- a/src/device/pf_cmina.c +++ b/src/device/pf_cmina.c @@ -34,6 +34,7 @@ #define pnal_get_port_statistics mock_pnal_get_port_statistics #define pnal_set_ip_suite mock_pnal_set_ip_suite #define pf_bg_worker_start_job mock_pf_bg_worker_start_job +#define os_get_current_time_us mock_os_get_current_time_us #endif #include @@ -54,7 +55,7 @@ * * @param net InOut: The p-net stack instance * @param arg In: Not used. - * @param current_time In: Not used. + * @param current_time In: Current time, in microseconds. */ static void pf_cmina_send_hello (pnet_t * net, void * arg, uint32_t current_time) { @@ -67,11 +68,12 @@ static void pf_cmina_send_hello (pnet_t * net, void * arg, uint32_t current_time /* Reschedule */ net->cmina_hello_count--; (void)pf_scheduler_add ( - net, + &net->scheduler_data, PF_CMINA_FS_HELLO_INTERVAL * 1000, pf_cmina_send_hello, NULL, - &net->cmina_hello_timeout); + &net->cmina_hello_timeout, + current_time); } else { @@ -543,11 +545,12 @@ int pf_cmina_init (pnet_t * net) /* Send first HELLO now! */ (void)pf_scheduler_add ( - net, + &net->scheduler_data, 0, pf_cmina_send_hello, NULL, - &net->cmina_hello_timeout); + &net->cmina_hello_timeout, + os_get_current_time_us()); } else { @@ -644,7 +647,9 @@ int pf_cmina_dcp_set_ind ( uint16_t reset_mode = 0; /* Stop sending Hello packets */ - pf_scheduler_remove_if_running (net, &net->cmina_hello_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &net->cmina_hello_timeout); /* Parse incoming DCP SET, without caring about actual CMINA state. Update cmina_current_dcp_ase and cmina_nonvolatile_dcp_ase*/ diff --git a/src/device/pf_cmio.c b/src/device/pf_cmio.c index 494a169d1..807c81c7a 100644 --- a/src/device/pf_cmio.c +++ b/src/device/pf_cmio.c @@ -28,7 +28,7 @@ */ #ifdef UNIT_TEST - +#define os_get_current_time_us mock_os_get_current_time_us #endif #include "pf_includes.h" @@ -117,7 +117,7 @@ static void pf_cmio_set_state (pf_ar_t * p_ar, pf_cmio_state_values_t state) * @param arg In: The AR instance. pf_ar_t * @param current_time In: The current system time, in microseconds, * when the scheduler is started to execute - * stored tasks. Not used here. + * stored tasks. */ static void pf_cmio_timer_expired ( pnet_t * net, @@ -148,11 +148,12 @@ static void pf_cmio_timer_expired ( pf_cmdev_cmio_info_ind (net, p_ar, data_possible); (void)pf_scheduler_add ( - net, + &net->scheduler_data, PF_CMIO_TIMER_PERIOD, pf_cmio_timer_expired, p_ar, - &p_ar->cmio_timeout); + &p_ar->cmio_timeout, + current_time); } else { @@ -235,11 +236,12 @@ int pf_cmio_cmdev_state_ind ( /* StartTimer() */ p_ar->cmio_timer_should_reschedule = true; (void)pf_scheduler_add ( - net, + &net->scheduler_data, PF_CMIO_TIMER_PERIOD, pf_cmio_timer_expired, p_ar, - &p_ar->cmio_timeout); + &p_ar->cmio_timeout, + os_get_current_time_us()); pf_cmio_set_state (p_ar, PF_CMIO_STATE_WDATA); } @@ -255,7 +257,9 @@ int pf_cmio_cmdev_state_ind ( { /* StopTimer() */ p_ar->cmio_timer_should_reschedule = false; - pf_scheduler_remove_if_running (net, &p_ar->cmio_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &p_ar->cmio_timeout); pf_cmio_set_state (p_ar, PF_CMIO_STATE_DATA); } diff --git a/src/device/pf_cmrpc.c b/src/device/pf_cmrpc.c index cb7ad3417..f9eba8f9d 100644 --- a/src/device/pf_cmrpc.c +++ b/src/device/pf_cmrpc.c @@ -559,7 +559,9 @@ static void pf_session_release (pnet_t * net, pf_session_info_t * p_sess) } } - pf_scheduler_remove_if_running (net, &p_sess->resend_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &p_sess->resend_timeout); LOG_DEBUG ( PF_RPC_LOG, @@ -1034,11 +1036,12 @@ static void pf_cmrpc_send_with_timeout ( { if ( pf_scheduler_add ( - p_net, + &p_net->scheduler_data, delay, pf_cmrpc_send_with_timeout, arg, - &p_sess->resend_timeout) != 0) + &p_sess->resend_timeout, + current_time) != 0) { LOG_ERROR ( PF_RPC_LOG, @@ -4350,7 +4353,9 @@ static int pf_cmrpc_dce_packet ( } /* Any incoming message stops resending */ - pf_scheduler_remove_if_running (net, &p_sess->resend_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &p_sess->resend_timeout); /* Decide what to do with incoming message */ /* Enter here _even_if_ an error is already detected because we may need diff --git a/src/device/pf_cmrpc_epm.c b/src/device/pf_cmrpc_epm.c index c60dab090..3f7ed5337 100644 --- a/src/device/pf_cmrpc_epm.c +++ b/src/device/pf_cmrpc_epm.c @@ -19,6 +19,10 @@ * */ +#ifdef UNIT_TEST +#define os_get_current_time_us mock_os_get_current_time_us +#endif + #include #include "pf_includes.h" #include "pf_block_writer.h" diff --git a/src/device/pf_cmsm.c b/src/device/pf_cmsm.c index b116a20a5..7377d9c55 100644 --- a/src/device/pf_cmsm.c +++ b/src/device/pf_cmsm.c @@ -34,7 +34,7 @@ */ #ifdef UNIT_TEST - +#define os_get_current_time_us mock_os_get_current_time_us #endif #include "pf_includes.h" @@ -188,12 +188,13 @@ int pf_cmsm_cmdev_state_ind ( __LINE__, (unsigned)p_ar->ar_param.cm_initiator_activity_timeout_factor); (void)pf_scheduler_restart ( - net, + &net->scheduler_data, p_ar->ar_param.cm_initiator_activity_timeout_factor * 100 * 1000, /* time in us */ pf_cmsm_timeout, (void *)p_ar, - &p_ar->cmsm_timeout); + &p_ar->cmsm_timeout, + os_get_current_time_us()); ret = 0; break; default: @@ -215,7 +216,9 @@ int pf_cmsm_cmdev_state_ind ( __LINE__, p_ar->arep); } - pf_scheduler_remove_if_running (net, &p_ar->cmsm_timeout); + pf_scheduler_remove_if_running ( + &net->scheduler_data, + &p_ar->cmsm_timeout); pf_cmsm_set_state (p_ar, PF_CMSM_STATE_IDLE); ret = 0; break; @@ -253,12 +256,13 @@ int pf_cmsm_rm_read_ind ( { /* Restart timeout period */ (void)pf_scheduler_restart ( - net, + &net->scheduler_data, p_ar->ar_param.cm_initiator_activity_timeout_factor * 100 * 1000, /* time in us */ pf_cmsm_timeout, (void *)p_ar, - &p_ar->cmsm_timeout); + &p_ar->cmsm_timeout, + os_get_current_time_us()); } ret = 0; break; @@ -288,11 +292,12 @@ int pf_cmsm_cm_read_ind ( case PF_CMSM_STATE_RUN: /* Restart timeout period */ (void)pf_scheduler_restart ( - net, + &net->scheduler_data, p_ar->ar_param.cm_initiator_activity_timeout_factor * 100 * 1000, pf_cmsm_timeout, (void *)p_ar, - &p_ar->cmsm_timeout); + &p_ar->cmsm_timeout, + os_get_current_time_us()); ret = 0; break; } @@ -321,11 +326,12 @@ int pf_cmsm_cm_write_ind ( case PF_CMSM_STATE_RUN: /* Restart timeout period */ (void)pf_scheduler_restart ( - net, + &net->scheduler_data, p_ar->ar_param.cm_initiator_activity_timeout_factor * 100 * 1000, pf_cmsm_timeout, (void *)p_ar, - &p_ar->cmsm_timeout); + &p_ar->cmsm_timeout, + os_get_current_time_us()); ret = 0; break; } diff --git a/src/device/pf_pdport.c b/src/device/pf_pdport.c index ea4df85fe..24e1092f9 100644 --- a/src/device/pf_pdport.c +++ b/src/device/pf_pdport.c @@ -17,6 +17,7 @@ #define pnal_eth_get_status mock_pnal_eth_get_status #define pnal_get_port_statistics mock_pnal_get_port_statistics #define pf_bg_worker_start_job mock_pf_bg_worker_start_job +#define os_get_current_time_us mock_os_get_current_time_us #endif #include "pf_includes.h" @@ -1070,7 +1071,7 @@ static void pf_pdport_monitor_link (pnet_t * net, int loc_port_num) * * @param net InOut: The p-net stack instance * @param arg In: Not used - * @param current_time In: Not used. + * @param current_time In: Current time, in microseconds */ static void pf_lldp_trigger_linkmonitor ( pnet_t * net, @@ -1087,11 +1088,12 @@ static void pf_lldp_trigger_linkmonitor ( if ( pf_scheduler_add ( - net, + &net->scheduler_data, PF_LINK_MONITOR_INTERVAL, pf_lldp_trigger_linkmonitor, NULL, - &net->pf_interface.link_monitor_timeout) != 0) + &net->pf_interface.link_monitor_timeout, + current_time) != 0) { LOG_ERROR ( PF_LLDP_LOG, @@ -1104,11 +1106,12 @@ void pf_pdport_start_linkmonitor (pnet_t * net) { if ( pf_scheduler_add ( - net, + &net->scheduler_data, PF_LINK_MONITOR_INTERVAL, pf_lldp_trigger_linkmonitor, NULL, - &net->pf_interface.link_monitor_timeout) != 0) + &net->pf_interface.link_monitor_timeout, + os_get_current_time_us()) != 0) { LOG_ERROR ( PF_LLDP_LOG, diff --git a/src/device/pnet_api.c b/src/device/pnet_api.c index bbd7c5a36..662943603 100644 --- a/src/device/pnet_api.c +++ b/src/device/pnet_api.c @@ -14,8 +14,9 @@ ********************************************************************/ #ifdef UNIT_TEST -#define pnal_snmp_init mock_pnal_snmp_init -#define pf_bg_worker_init mock_pf_bg_worker_init +#define pnal_snmp_init mock_pnal_snmp_init +#define pf_bg_worker_init mock_pf_bg_worker_init +#define os_get_current_time_us mock_os_get_current_time_us #endif #include @@ -48,7 +49,7 @@ int pnet_init_only (pnet_t * net, const pnet_cfg_t * p_cfg) net->cmdev_initialized = false; /* TODO How to handle that pf_cmdev_exit() is used before pf_cmdev_init()? */ - pf_scheduler_init (net, p_cfg->tick_us); + pf_scheduler_init (&net->scheduler_data, p_cfg->tick_us); #if PNET_OPTION_DRIVER_ENABLE if (net->fspm_cfg.driver_enable) @@ -151,7 +152,7 @@ void pnet_handle_periodic (pnet_t * net) pf_alarm_periodic (net); /* Handle expired timeout events */ - pf_scheduler_tick (net); + pf_scheduler_tick (net, &net->scheduler_data, os_get_current_time_us()); pf_pdport_periodic (net); @@ -208,7 +209,7 @@ void pnet_show (pnet_t * net, unsigned level) if (level & 0x4000) { printf ("\n\n"); - pf_scheduler_show (net); + pf_scheduler_show (&net->scheduler_data, os_get_current_time_us()); } if (level & 0x8000) { diff --git a/src/pf_types.h b/src/pf_types.h index 110f0fcbc..4a38965d3 100644 --- a/src/pf_types.h +++ b/src/pf_types.h @@ -1081,7 +1081,7 @@ typedef void (*pf_scheduler_timeout_ftn_t) ( void * arg, uint32_t current_time); -typedef struct pf_scheduler_timeouts +typedef struct pf_scheduler_timeout { /** For debugging only */ const char * name; @@ -1096,7 +1096,23 @@ typedef struct pf_scheduler_timeouts pf_scheduler_timeout_ftn_t cb; /* Call-back to call on timeout */ void * arg; /* call-back argument */ -} pf_scheduler_timeouts_t; +} pf_scheduler_timeout_t; + +typedef struct pf_scheduler_data +{ + pf_scheduler_timeout_t timeouts[PF_MAX_TIMEOUTS]; + + /** Will be PF_MAX_TIMEOUTS at empty queue */ + uint32_t busylist_head; + + /** Will be PF_MAX_TIMEOUTS at empty queue */ + uint32_t freelist_head; + + os_mutex_t * mutex; + + /** Time between stack executions, in microseconds */ + uint32_t tick_interval; +} pf_scheduler_data_t; typedef struct pf_scheduler_handle { @@ -2886,11 +2902,11 @@ struct pnet pf_scheduler_handle_t dcp_sam_timeout; pf_scheduler_handle_t dcp_identresp_timeout; pf_eth_frame_id_map_t eth_id_map[PF_ETH_MAX_MAP]; - volatile pf_scheduler_timeouts_t scheduler_timeouts[PF_MAX_TIMEOUTS]; - volatile uint32_t scheduler_timeout_first; - volatile uint32_t scheduler_timeout_free; - os_mutex_t * scheduler_timeout_mutex; - uint32_t scheduler_tick_interval; /* microseconds */ + + /**************** Scheduler *******************************/ + + volatile pf_scheduler_data_t scheduler_data; + bool cmdev_initialized; pf_device_t cmdev_device; /* APIs and diag items */ pf_cmina_dcp_ase_t cmina_nonvolatile_dcp_ase; /* Reflects what is/should be diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 31c2400df..4ff99073a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -75,6 +75,7 @@ target_sources(pf_test PRIVATE ${PROFINET_SOURCE_DIR}/src/device/pf_cmpbe.c ${PROFINET_SOURCE_DIR}/src/device/pf_cmrdr.c ${PROFINET_SOURCE_DIR}/src/device/pf_cmrpc.c + ${PROFINET_SOURCE_DIR}/src/device/pf_cmrpc_epm.c ${PROFINET_SOURCE_DIR}/src/device/pf_cmrpc_helpers.c ${PROFINET_SOURCE_DIR}/src/device/pf_cmrs.c ${PROFINET_SOURCE_DIR}/src/device/pf_cmsm.c diff --git a/test/test_scheduler.cpp b/test/test_scheduler.cpp index d21fccc4d..234e484fb 100644 --- a/test/test_scheduler.cpp +++ b/test/test_scheduler.cpp @@ -135,7 +135,70 @@ TEST_F (SchedulerUnitTest, SchedulerSanitizeDelayTest) ASSERT_NEAR (result, 1000, margin); } -TEST_F (SchedulerTest, SchedulerAddRemove) +TEST_F (SchedulerUnitTest, SchedulerIsTimeBeforeTest) +{ + /* Validate that the test case is compiled with enough timeouts */ + ASSERT_GE (PF_MAX_TIMEOUTS, 6); + + volatile pf_scheduler_data_t scheduler_data; + scheduler_data.mutex = NULL; + + pf_scheduler_init (&scheduler_data, TEST_TICK_INTERVAL_US); + + scheduler_data.timeouts[0].in_use = true; + scheduler_data.timeouts[0].name = "No_0"; + scheduler_data.timeouts[0].when = 100; + scheduler_data.timeouts[0].prev = PF_MAX_TIMEOUTS; + scheduler_data.timeouts[0].next = 1; + + scheduler_data.timeouts[1].in_use = true; + scheduler_data.timeouts[1].name = "No_1"; + scheduler_data.timeouts[1].when = 200; + scheduler_data.timeouts[1].prev = 0; + scheduler_data.timeouts[1].next = 2; + + scheduler_data.timeouts[2].in_use = true; + scheduler_data.timeouts[2].name = "No_2"; + scheduler_data.timeouts[2].when = 300; + scheduler_data.timeouts[2].prev = 1; + scheduler_data.timeouts[2].next = 3; + + scheduler_data.timeouts[3].in_use = true; + scheduler_data.timeouts[3].name = "No_3"; + scheduler_data.timeouts[3].when = 300; + scheduler_data.timeouts[3].prev = 2; + scheduler_data.timeouts[3].next = 4; + + scheduler_data.timeouts[4].in_use = true; + scheduler_data.timeouts[4].name = "No_4"; + scheduler_data.timeouts[4].when = 400; + scheduler_data.timeouts[4].prev = 3; + scheduler_data.timeouts[4].next = 5; + + scheduler_data.timeouts[5].in_use = true; + scheduler_data.timeouts[5].name = "No_5"; + scheduler_data.timeouts[5].when = 500; + scheduler_data.timeouts[5].prev = 4; + scheduler_data.timeouts[5].next = PF_MAX_TIMEOUTS; + + scheduler_data.busylist_head = 0; + scheduler_data.freelist_head = 6; + + pf_scheduler_show (&scheduler_data, 50); + + ASSERT_TRUE (pf_scheduler_is_time_before (&scheduler_data, 0, 1)); + ASSERT_TRUE (pf_scheduler_is_time_before (&scheduler_data, 1, 2)); + ASSERT_TRUE (pf_scheduler_is_time_before (&scheduler_data, 2, 3)); + ASSERT_TRUE (pf_scheduler_is_time_before (&scheduler_data, 3, 4)); + ASSERT_TRUE (pf_scheduler_is_time_before (&scheduler_data, 4, 5)); + + ASSERT_TRUE (pf_scheduler_is_time_before (&scheduler_data, 0, 0)); + + ASSERT_FALSE (pf_scheduler_is_time_before (&scheduler_data, 1, 0)); + ASSERT_FALSE (pf_scheduler_is_time_before (&scheduler_data, 2, 1)); +} + +TEST_F (SchedulerTest, SchedulerAddRemoveInStack) { int ret; pf_scheduler_handle_t * p_a = &appdata.scheduler_handle_a; @@ -146,7 +209,7 @@ TEST_F (SchedulerTest, SchedulerAddRemove) uint32_t value; bool is_scheduled; - pf_scheduler_init (net, TEST_TICK_INTERVAL_US); + pf_scheduler_init (&net->scheduler_data, TEST_TICK_INTERVAL_US); run_stack (TEST_SCHEDULER_RUNTIME); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 0); @@ -183,11 +246,12 @@ TEST_F (SchedulerTest, SchedulerAddRemove) /* Schedule callback A */ ret = pf_scheduler_add ( - net, + &net->scheduler_data, TEST_SCHEDULER_CALLBACK_DELAY, test_scheduler_callback_a, &appdata, - p_a); + p_a, + mock_os_data.current_time_us); EXPECT_EQ (ret, 0); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 0); @@ -242,18 +306,20 @@ TEST_F (SchedulerTest, SchedulerAddRemove) /* Schedule both callbacks */ ret = pf_scheduler_add ( - net, + &net->scheduler_data, TEST_SCHEDULER_CALLBACK_DELAY, test_scheduler_callback_a, &appdata, - p_a); + p_a, + mock_os_data.current_time_us); EXPECT_EQ (ret, 0); ret = pf_scheduler_add ( - net, + &net->scheduler_data, TEST_SCHEDULER_RUNTIME + TEST_SCHEDULER_CALLBACK_DELAY, test_scheduler_callback_b, &appdata, - p_b); + p_b, + mock_os_data.current_time_us); EXPECT_EQ (ret, 0); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 1); @@ -326,14 +392,14 @@ TEST_F (SchedulerTest, SchedulerAddRemove) /* Remove non-scheduled event */ run_stack (TEST_SCHEDULER_RUNTIME); - pf_scheduler_remove_if_running (net, p_a); + pf_scheduler_remove_if_running (&net->scheduler_data, p_a); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 2); value = pf_scheduler_get_value (p_a); EXPECT_EQ (value, UINT32_MAX); /* Implementation detail */ is_scheduled = pf_scheduler_is_running (p_a); EXPECT_FALSE (is_scheduled); - pf_scheduler_remove (net, p_a); /* Will log error message */ + pf_scheduler_remove (&net->scheduler_data, p_a); /* Will log error message */ EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 2); value = pf_scheduler_get_value (p_a); EXPECT_EQ (value, UINT32_MAX); /* Implementation detail */ @@ -344,11 +410,12 @@ TEST_F (SchedulerTest, SchedulerAddRemove) run_stack (TEST_SCHEDULER_RUNTIME); ret = pf_scheduler_add ( - net, + &net->scheduler_data, TEST_SCHEDULER_CALLBACK_DELAY, test_scheduler_callback_a, &appdata, - p_a); + p_a, + mock_os_data.current_time_us); EXPECT_EQ (ret, 0); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 2); @@ -377,7 +444,7 @@ TEST_F (SchedulerTest, SchedulerAddRemove) is_scheduled = pf_scheduler_is_running (p_b); EXPECT_FALSE (is_scheduled); - pf_scheduler_remove (net, p_a); + pf_scheduler_remove (&net->scheduler_data, p_a); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 2); value = pf_scheduler_get_value (p_a); @@ -409,11 +476,12 @@ TEST_F (SchedulerTest, SchedulerAddRemove) run_stack (TEST_SCHEDULER_RUNTIME); ret = pf_scheduler_add ( - net, + &net->scheduler_data, TEST_SCHEDULER_CALLBACK_DELAY, test_scheduler_callback_a, &appdata, - p_a); + p_a, + mock_os_data.current_time_us); EXPECT_EQ (ret, 0); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 2); @@ -442,7 +510,7 @@ TEST_F (SchedulerTest, SchedulerAddRemove) is_scheduled = pf_scheduler_is_running (p_b); EXPECT_FALSE (is_scheduled); - pf_scheduler_remove_if_running (net, p_a); + pf_scheduler_remove_if_running (&net->scheduler_data, p_a); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 2); value = pf_scheduler_get_value (p_a); @@ -472,11 +540,12 @@ TEST_F (SchedulerTest, SchedulerAddRemove) /* Schedule and restart */ ret = pf_scheduler_add ( - net, + &net->scheduler_data, TEST_SCHEDULER_CALLBACK_DELAY, test_scheduler_callback_a, &appdata, - p_a); + p_a, + mock_os_data.current_time_us); EXPECT_EQ (ret, 0); EXPECT_EQ (appdata.call_counters.scheduler_callback_a_calls, 2); @@ -502,11 +571,12 @@ TEST_F (SchedulerTest, SchedulerAddRemove) EXPECT_FALSE (is_scheduled); ret = pf_scheduler_restart ( - net, + &net->scheduler_data, TEST_SCHEDULER_RUNTIME + TEST_SCHEDULER_CALLBACK_DELAY, test_scheduler_callback_a, &appdata, - p_a); + p_a, + mock_os_data.current_time_us); run_stack (TEST_SCHEDULER_RUNTIME);