ROFL-pipeline  v0.6.0dev
of1x_timers.c
1 #include "of1x_timers.h"
2 #include "of1x_pipeline.h"
3 #include "of1x_flow_table.h"
4 #include "of1x_flow_entry.h"
5 
6 #include "../../../platform/memory.h"
7 #include "../../../platform/lock.h"
8 #include "../../../platform/likely.h"
9 #include "../../../platform/timing.h"
10 #include "../../../util/logging.h"
11 
12 static rofl_result_t __of1x_destroy_all_entries_from_timer_group(of1x_timer_group_t* tg, of1x_pipeline_t *const pipeline, unsigned int id);
13 #if ! OF1X_TIMER_STATIC_ALLOCATION_SLOTS
14 static of1x_timer_group_t* __of1x_dynamic_slot_search(of1x_flow_table_t* const table, uint64_t expiration_time);
15 #endif
16 
21 void __of1x_fill_new_timer_entry_info(of1x_flow_entry_t * entry, uint32_t hard_timeout, uint32_t idle_timeout){
22 
23  entry->timer_info.hard_timeout = hard_timeout;
24  entry->timer_info.idle_timeout = idle_timeout;
25  entry->timer_info.hard_timer_entry = NULL;
26  entry->timer_info.idle_timer_entry = NULL;
27 
28 }
29 
33 bool __of1x_time_is_later(struct timeval *a, struct timeval *b){
34  if(a->tv_sec > b->tv_sec){
35  return true;
36  }
37 
38  if (a->tv_sec == b->tv_sec){
39  if(a->tv_usec > b->tv_usec){
40  return true;
41  }
42  }
43 
44  return false;
45 }
46 
53 
54  of1x_timer_group_t * tg = timer_group;
55  of1x_entry_timer_t * et;
56  if(!tg)
57  {
58  ROFL_PIPELINE_DEBUG("Timer group list is empty\n");
59  return;
60  }
61 #if OF1X_TIMER_STATIC_ALLOCATION_SLOTS
62  int i;
63  for(i=0;i<OF1X_TIMER_GROUPS_MAX;i++)
64  {
65  ROFL_PIPELINE_DEBUG("timer group on position %p\n", &(tg[i]));
66  ROFL_PIPELINE_DEBUG(" [%p] TO:%"PRIu64" Nent:%d h:%p t:%p\n",&(tg[i]), tg[i].timeout,
67  tg[i].list.num_of_timers, tg[i].list.head, tg[i].list.tail);
68  for(et=tg[i].list.head; et; et=et->next)
69  ROFL_PIPELINE_DEBUG(" [%p] fe:%p prev:%p next:%p tg:%p\n", et,et->entry, et->prev, et->next, et->group);
70  }
71 
72 #else
73  if(tg->prev)
74  ROFL_PIPELINE_DEBUG("NOT the first group!!\n");
75  for(;tg!=NULL;tg=tg->next)
76  {
77  ROFL_PIPELINE_DEBUG("timer group on position %p\n", tg);
78  ROFL_PIPELINE_DEBUG(" [%p] TO:%"PRIu64" next:%p prev:%p Nent:%d h:%p t:%p\n",tg, tg->timeout, tg->next, tg->prev,
79  tg->list.num_of_timers, tg->list.head, tg->list.tail);
80  for(et=tg->list.head; et; et=et->next)
81  ROFL_PIPELINE_DEBUG(" [%p] fe:%p prev:%p next:%p tg:%p\n", et,et->entry, et->prev, et->next, et->group);
82  }
83 #endif
84 }
85 
89 inline uint64_t __of1x_get_time_ms(struct timeval *time){
90  //Make sure 32bit compilers don't truncate and do wrong
91  //convesions from 32 bit values (tv_sec) to uint64
92  uint64_t tmp = time->tv_sec;
93  tmp *= 1000;
94  return tmp + (time->tv_usec/1000);
95 }
96 
100 uint64_t __of1x_get_expiration_time_slotted (uint32_t timeout,struct timeval *now){
101 
102  uint64_t expiration_time_ms;
103  // exact expiration time calculation
104  //WARNING it could happen that the calculated value does not fit in a uint64_t
105  // then we might have to use floats.
106  expiration_time_ms = (now->tv_sec+timeout)*1000+now->tv_usec/1000;
107  // now it must be set in one of the slots
108  //NOTE we are selecting the next slot so we round up the expiration time
109  if(expiration_time_ms%OF1X_TIMER_SLOT_MS)
110  return (expiration_time_ms/OF1X_TIMER_SLOT_MS + 1)*OF1X_TIMER_SLOT_MS;
111  else
112  return expiration_time_ms;
113 }
114 
115 #if OF1X_TIMER_STATIC_ALLOCATION_SLOTS
116 
122 
123  int i;
124  struct timeval now;
125  uint64_t time_next_slot;
126  platform_gettimeofday(&now);
127  time_next_slot = __of1x_get_expiration_time_slotted(0,&now);
128 
129  //First allocate the memory
130  table->timers = (struct of1x_timer_group*) platform_malloc_shared(sizeof(struct of1x_timer_group)*OF1X_TIMER_GROUPS_MAX);
131 
132  if(unlikely(table->timers == NULL))
133  return ROFL_FAILURE;
134 
135  for(i=0; i<OF1X_TIMER_GROUPS_MAX;i++)
136  {
137  table->timers[i].timeout=time_next_slot+OF1X_TIMER_SLOT_MS*i;
138  table->timers[i].list.num_of_timers = 0;
139  table->timers[i].list.head = table->timers[i].list.tail = NULL;
140  }
141  table->current_timer_group=0;
142 
143  return ROFL_SUCCESS;
144 }
145 
152  platform_free_shared(table->timers);
153 }
154 
161 static void __of1x_timer_group_rotate(of1x_pipeline_t *const pipeline, of1x_timer_group_t *tg , unsigned int id_table)
162 {
163  if(tg->list.head)
164  {
165  //erase_all_entries;
166  if(__of1x_destroy_all_entries_from_timer_group(tg, pipeline, id_table)!=ROFL_SUCCESS)
167  ROFL_PIPELINE_DEBUG("ERROR in destroying timer group\n");
168  }
169  tg->timeout += OF1X_TIMER_SLOT_MS*OF1X_TIMER_GROUPS_MAX;
170  tg->list.num_of_timers=0;
171  tg->list.head = tg->list.tail = NULL;
172 
173  pipeline->tables[id_table].current_timer_group++;
174  if(pipeline->tables[id_table].current_timer_group>=OF1X_TIMER_GROUPS_MAX)
175  pipeline->tables[id_table].current_timer_group=0;
176 }
177 
178 #else
179 
184 static of1x_timer_group_t* __of1x_timer_group_init(uint64_t timeout, of1x_timer_group_t* tg_next, of1x_timer_group_t* tg_prev, of1x_flow_table_t* table)
185 {
186  // create the new timer_group
187  of1x_timer_group_t* new_group;
188  new_group = platform_malloc_shared(sizeof(of1x_timer_group_t));
189  if(unlikely(new_group == NULL))
190  {
191  ROFL_PIPELINE_DEBUG("<%s:%d> Error allocating memory\n",__func__,__LINE__);
192  return NULL;
193  }
194  new_group->timeout = timeout;
195  new_group->list.num_of_timers=0;
196  new_group->list.head=NULL;
197  new_group->list.tail=NULL;
198 
199  // place the timer group
200  new_group->prev=tg_prev;
201  new_group->next=tg_next;
202 
203  //if there is a node afterwards we place the new one before
204  if (tg_next) tg_next->prev=new_group;
205  //if there is a node forewards we place the new one before
206  if (tg_prev) tg_prev->next=new_group;
207 
208  if(table->timers == tg_next)
209  table->timers = new_group;
210 
211  return new_group;
212 }
213 
218 static void of1x_destroy_timer_group(of1x_timer_group_t* tg, of1x_flow_table_t* table)
219 {
220  if(tg->prev)
221  (tg->prev)->next=tg->next;
222  else
223  if(table->timers == tg)
224  table->timers = tg->next;
225  if(tg->next)
226  (tg->next)->prev=tg->prev;
228  tg = NULL;
229 }
230 #endif
231 
236 static of1x_entry_timer_t* __of1x_entry_timer_init(of1x_timer_group_t* tg, of1x_flow_entry_t* entry, of1x_timer_timeout_type_t is_idle)
237 {
238  of1x_entry_timer_t* new_entry;
239  new_entry = platform_malloc_shared(sizeof(of1x_entry_timer_t));
240  if(unlikely(new_entry==NULL))
241  {
242  ROFL_PIPELINE_DEBUG("<%s:%d> Error allocating memory\n",__func__,__LINE__);
243  return NULL;
244  }
245  new_entry->entry = entry;
246  new_entry->group = tg;
247 
248  // we add the new entries at the end
249  new_entry->next=NULL;
250 
251  // we check if it is the first entry.
252  if(tg->list.tail) //if it is not
253  {
254  new_entry->prev=tg->list.tail;
255  //update the pointer OF the last entry on the list
256  tg->list.tail->next=new_entry;
257  }
258  else //if it is the first one
259  {
260  new_entry->prev=NULL;
261  //normally this should also apply:
262  if(!tg->list.head)
263  tg->list.head = new_entry;
264  }
265 
266  // update the pointer TO the last entry of the list
267  tg->list.tail = new_entry;
268  // update the entries counter
269  tg->list.num_of_timers++;
270 
271  new_entry->type=is_idle;
272 
273  if(is_idle)
274  entry->timer_info.idle_timer_entry=new_entry;
275  else
276  entry->timer_info.hard_timer_entry=new_entry;
277 
278  return new_entry;
279 }
280 
286 static rofl_result_t __of1x_destroy_single_timer_entry_clean(of1x_entry_timer_t* entry, of1x_flow_table_t * table)
287 {
288  if(likely(entry!=NULL))
289  {
290  if(!entry->next && !entry->prev) // this is the only entry
291  {
292  entry->group->list.head=NULL;
293  entry->group->list.tail=NULL;
294  }
295  else if(!entry->prev) //is the first entry
296  {
297  entry->group->list.head=entry->next;
298  entry->next->prev = NULL;
299  }
300  else if(!entry->next) //last entry
301  {
302  entry->group->list.tail=entry->prev;
303  entry->prev->next = NULL;
304  }
305  else
306  {
307  entry->next->prev = entry->prev;
308  entry->prev->next = entry->next;
309  }
310 
311  entry->group->list.num_of_timers--;
312 #if ! OF1X_TIMER_STATIC_ALLOCATION_SLOTS
313  //we need to check if this entry was the last one and delete the timer group
314  if(entry->group->list.num_of_timers == 0)
315  __of1x_destroy_timer_group(entry->group,table);
316 #endif
317  platform_free_shared(entry);
318  entry = NULL;
319 
320  return ROFL_SUCCESS;
321 
322  }else{
323  ROFL_PIPELINE_DEBUG("<%s:%d> Not a valid timer entry %p\n",__func__,__LINE__,entry);
324  return ROFL_FAILURE;
325  }
326 }
327 
336  // We need to erase both hard and idle timer_entries
337 
338  if(unlikely(entry->table==NULL))
339  return ROFL_FAILURE;
340 
341  if(entry->timer_info.hard_timeout){
342  if(__of1x_destroy_single_timer_entry_clean(entry->timer_info.hard_timer_entry, entry->table)!=ROFL_SUCCESS)
343  return ROFL_FAILURE;
344  entry->timer_info.hard_timer_entry = NULL;
345  }
346 
347  if(entry->timer_info.idle_timeout){
348  if(__of1x_destroy_single_timer_entry_clean(entry->timer_info.idle_timer_entry, entry->table)!=ROFL_SUCCESS)
349  return ROFL_FAILURE;
350  entry->timer_info.idle_timer_entry = NULL;
351  }
352 
353 #if DEBUG_NO_REAL_PIPE
355 #endif
356 
357  return ROFL_SUCCESS;
358 }
359 
364 static rofl_result_t __of1x_reschedule_idle_timer(of1x_entry_timer_t * entry_timer, of1x_pipeline_t *const pipeline, unsigned int id_table)
365 {
366  struct timeval system_time;
367  uint64_t expiration_time;
368  __of1x_stats_flow_tid_t consolidated_stats;
369 
370  //Consolidate entry
371  __of1x_stats_flow_consolidate(&entry_timer->entry->stats, &consolidated_stats);
372 
373  if(consolidated_stats.packet_count == entry_timer->entry->timer_info.last_packet_count)
374  {
375  // timeout expired so no need to reschedule !!! we have to delete the entry
376 #ifdef DEBUG_NO_REAL_PIPE
377  ROFL_PIPELINE_DEBUG("NOT erasing real entries of table \n");
378  __of1x_fill_new_timer_entry_info(entry_timer->entry,0,0);
379  //we need to destroy the entries
380  __of1x_destroy_timer_entries(entry_timer->entry);
381 #else
382  __of1x_remove_specific_flow_entry_table(pipeline, id_table, entry_timer->entry, OF1X_FLOW_REMOVE_IDLE_TIMEOUT, MUTEX_ALREADY_ACQUIRED_BY_TIMER_EXPIRATION);
383 #endif
384  return ROFL_SUCCESS;
385  }
386 
387  entry_timer->entry->timer_info.last_packet_count = consolidated_stats.packet_count;
388 
389  //NOTE we calculate the new time of expiration from the checking time and not from the last time it was used (less accurate and more efficient)
390  platform_gettimeofday(&system_time);
391  expiration_time = __of1x_get_expiration_time_slotted(entry_timer->entry->timer_info.idle_timeout, &system_time);
392 
393 #if OF1X_TIMER_STATIC_ALLOCATION_SLOTS
394 
395  int slot_delta = expiration_time - pipeline->tables[id_table].timers[pipeline->tables[id_table].current_timer_group].timeout; //ms
396  int slot_position = (pipeline->tables[id_table].current_timer_group + slot_delta/OF1X_TIMER_SLOT_MS) % OF1X_TIMER_GROUPS_MAX;
397  if(__of1x_entry_timer_init(&(pipeline->tables[id_table].timers[slot_position]), entry_timer->entry, IDLE_TO)==NULL)
398  return ROFL_FAILURE;
399 #else
400 
401  of1x_timer_group_t * tg_iterator = __of1x_dynamic_slot_search(pipeline->tables[id_table], expiration_time);
402  if(tg_iterator==NULL)
403  return ROFL_FAILURE;
404  // add entry to this group. new_group.list->num_of_timers++; ...
405  if(__of1x_entry_timer_init(tg_iterator, entry_timer->entry, IDLE_TO) == NULL)
406  return ROFL_FAILURE;
407 #endif
408 
409  //TODO delete timer_entry that has been rescheduled:
410  __of1x_destroy_single_timer_entry_clean(entry_timer, &pipeline->tables[id_table]);
411 
412  return ROFL_SUCCESS;
413 }
414 
421 static rofl_result_t __of1x_destroy_all_entries_from_timer_group(of1x_timer_group_t* tg, of1x_pipeline_t *const pipeline, unsigned int id_table)
422 {
423  of1x_entry_timer_t* entry_iterator;
424 
425  if(tg->list.num_of_timers>0 && tg->list.head){
426  while( (entry_iterator = tg->list.head) != NULL){
427  //NOTE actual removal of timer_entries is done in the destruction of the entry
428 
429  if(entry_iterator->type == IDLE_TO){
430  if(__of1x_reschedule_idle_timer(entry_iterator, pipeline, id_table)!=ROFL_SUCCESS)
431  return ROFL_FAILURE;
432  }else{
433 #ifdef DEBUG_NO_REAL_PIPE
434  ROFL_PIPELINE_DEBUG("NOT erasing real entries of table \n");
435  __of1x_fill_new_timer_entry_info(entry_iterator->entry,0,0);
436  //we delete the enrty_timer form outside
437  __of1x_destroy_timer_entries(entry_iterator->entry);
438 #else
439  //ROFL_PIPELINE_DEBUG("Erasing real entries of table \n"); //NOTE DELETE
440  __of1x_remove_specific_flow_entry_table(pipeline, id_table,entry_iterator->entry, OF1X_FLOW_REMOVE_HARD_TIMEOUT, MUTEX_ALREADY_ACQUIRED_BY_TIMER_EXPIRATION);
441 #endif
442  }
443  }
444  }
445  return ROFL_SUCCESS;
446 }
447 
448 #if ! OF1X_TIMER_STATIC_ALLOCATION_SLOTS
449 
454 static of1x_timer_group_t * of1x_dynamic_slot_search(of1x_flow_table_t* const table, uint64_t expiration_time)
455 {
456  of1x_timer_group_t* tg_iterator;
457  //Determine the slot, check if is already defined, if not allocate
458  //We search the timer group with the timeout corresponding to the expiration_time calculated
459  for(tg_iterator = table->timers; tg_iterator && tg_iterator->timeout<expiration_time && tg_iterator->next; tg_iterator=tg_iterator->next);
460  // After that we expect 3 different situations:
461 
462  if(!tg_iterator)
463  {
464  //the list is empty, we sould create the first group
465  table->timers = __of1x_timer_group_init(expiration_time,NULL,NULL, table);
466  if(table->timers == NULL)
467  return NULL;
468 
469  tg_iterator = table->timers;
470  }
471  else if(tg_iterator->timeout>expiration_time)
472  {
473  // Create new group and allocate it before the current group
474  tg_iterator = __of1x_timer_group_init(expiration_time,tg_iterator,tg_iterator->prev, table);
475  if(tg_iterator == NULL)
476  return NULL;
477  }
478  else if(tg_iterator->timeout < expiration_time && !tg_iterator->next)
479  {
480  // Create new group and allocate it after the current group
481  tg_iterator = __of1x_timer_group_init(expiration_time,NULL, tg_iterator, table);
482  if(tg_iterator == NULL)
483  return NULL;
484  }
485  else
486  {
487  if (!(tg_iterator->timeout==expiration_time))
488  {
489  // Unexpected outcome ...
490  ROFL_PIPELINE_DEBUG("<%s:%d> No proper position for this entry found\n",__func__, __LINE__);
491  return NULL;
492  }
493  }
494 
495  return tg_iterator;
496 }
497 #endif
498 
499 static rofl_result_t __of1x_add_single_timer(of1x_flow_table_t* const table, const uint32_t timeout, of1x_flow_entry_t* entry, of1x_timer_timeout_type_t is_idle)
500 {
501  uint64_t expiration_time;
502  struct timeval now;
503  platform_gettimeofday(&now);
504  expiration_time = __of1x_get_expiration_time_slotted(timeout, &now);
505 #if OF1X_TIMER_STATIC_ALLOCATION_SLOTS
506  // add entries to the position tc + hard_timeout_s
507  if(timeout > OF1X_TIMER_GROUPS_MAX)
508  {
509  ROFL_PIPELINE_DEBUG("Timeout value excedded maximum value (hto=%d, MAX=%d)\n", timeout,OF1X_TIMER_GROUPS_MAX);
510  return ROFL_FAILURE;
511  }
512 
513 
514  int slot_delta = expiration_time - table->timers[table->current_timer_group].timeout; //ms
515 
516  //NOTE we allocate the timer in the next slot rounding up:
517  //so the actual expiration time will be a value in [hard_timeout,hard_timeout+OF1X_TIMER_SLOT_MS)
518  int slot_position = (table->current_timer_group+(slot_delta/OF1X_TIMER_SLOT_MS))%(OF1X_TIMER_GROUPS_MAX);
519  if(__of1x_entry_timer_init(&(table->timers[slot_position]), entry, is_idle)==NULL)
520  return ROFL_FAILURE;
521 
522 #else
523 
524  of1x_timer_group_t* tg_iterator = __of1x_dynamic_slot_search(table, expiration_time);
525  if (tg_iterator==NULL)
526  return ROFL_FAILURE;
527 
528  // add entry to this group. new_group.list->num_of_timers++; ...
529  if(__of1x_entry_timer_init(tg_iterator, entry, is_idle, NULL) == NULL)
530  return ROFL_FAILURE;
531 
532 #endif
533  return ROFL_SUCCESS;
534 }
535 
536 //Add timer to a table
537 rofl_result_t __of1x_add_timer(of1x_flow_table_t* const table, of1x_flow_entry_t* const entry){
538  rofl_result_t res;
539  //NOTE we don't use that lock because this is only called from of1x_add_flow_entry...()
540 
541  if(entry->timer_info.idle_timeout)
542  {
543  res = __of1x_add_single_timer(table, entry->timer_info.idle_timeout, entry, IDLE_TO); //is_idle = 1
544  if(res == ROFL_FAILURE)
545  {
546  return ROFL_FAILURE;
547  }
548  }
549  if(entry->timer_info.hard_timeout)
550  {
551  res = __of1x_add_single_timer(table, entry->timer_info.hard_timeout, entry, HARD_TO); //is_idle = 0
552  if(res == ROFL_FAILURE)
553  {
554  return ROFL_FAILURE;
555  }
556  }
557 
558  return ROFL_SUCCESS;
559 }
560 
561 void __of1x_process_pipeline_tables_timeout_expirations(of1x_pipeline_t *const pipeline){
562 
563  unsigned int i;
564 
565  struct timeval system_time;
566  platform_gettimeofday(&system_time);
567  uint64_t now = __of1x_get_time_ms(&system_time);
568 
569  for(i=0;i<pipeline->num_of_tables;i++)
570  {
571  of1x_flow_table_t* table = &pipeline->tables[i];
572  platform_mutex_lock(table->mutex);
573 #if OF1X_TIMER_STATIC_ALLOCATION_SLOTS
574  while(table->timers[table->current_timer_group].timeout<=now)
575  {
576  //rotate the timers
577  __of1x_timer_group_rotate(pipeline,&(table->timers[table->current_timer_group]),i);
578  }
579 #else
580  of1x_timer_group_t* slot_it, *next;
581  for(slot_it=table->timers; slot_it; slot_it=next)
582  {
583  if(now<slot_it->timeout) //Current slot time > time_now. We are done
584  break;
585  //remove all entries and the timer group.
586  if(__of1x_destroy_all_entries_from_timer_group(slot_it, table)!=ROFL_SUCCESS)
587  {
588  ROFL_PIPELINE_DEBUG("Error while destroying timer group\n");
589  platform_mutex_unlock(table->mutex);
590  return;
591  }
592  next = slot_it->next;
593  if(slot_it)
594  __of1x_destroy_timer_group(slot_it, table);
595  }
596 #endif
597  platform_mutex_unlock(table->mutex);
598  }
599  return;
600 }
rofl_result_t __of1x_destroy_timer_entries(struct of1x_flow_entry *entry)
of1x_destroy_timer_entry When a flow entry is removed from the table this function will be called in ...
Definition: of1x_timers.c:335
OpenFlow v1.0, 1.2 and 1.3.2 pipeline abstraction.
void __of1x_timer_group_static_destroy(struct of1x_flow_table *table)
of1x_timer_group_static_destroy Destroys the timers table initializes the values for the entry lists ...
Definition: of1x_timers.c:151
void platform_mutex_lock(platform_mutex_t *mutex)
Locks the platform_mutex_t mutex.
OpenFlow v1.0, 1.2 and 1.3.2 flow table abstraction.
ROFL_BEGIN_DECLS int platform_gettimeofday(struct timeval *tval)
Gets the system time.
OpenFlow v1.0, 1.2 and 1.3.2 flow entry structure.
void platform_mutex_unlock(platform_mutex_t *mutex)
Unlocks the platform_mutex_t mutex.
OpenFlow v1.0, 1.2 and 1.3.2 flow table abstraction.
void __of1x_fill_new_timer_entry_info(struct of1x_flow_entry *entry, uint32_t hard_timeout, uint32_t idle_timeout)
of1x_fill_new_timer_entry_info initialize the values for a new the timer entry
Definition: of1x_timers.c:21
OpenFlow v1.0, 1.2 and 1.3.2 timers subsystem.
void __of1x_dump_timers_structure(of1x_timer_group_t *timer_group)
of1x_dump_timers_structure this function is ment to show the timer groups existing and the entries re...
Definition: of1x_timers.c:52
OpenFlow v1.0, 1.2 and 1.3.2 flow entry abstraction.
void * platform_malloc_shared(size_t length)
Allocates a chunk of dynamic memory of size length, which must be accessible (R/W) for all the thread...
OpenFlow v1.0, 1.2 and 1.3.2 pipeline abstraction data structure.
Definition: of1x_pipeline.h:50
rofl_result_t __of1x_timer_group_static_init(struct of1x_flow_table *table)
of1x_timer_group_static_init initializes the timeout values initializes the values for the entry list...
Definition: of1x_timers.c:121
void platform_free_shared(void *data)
Frees a chunk of dynamic memory previously allocated with platform_malloc_shared().
Definition: of1x_timers.h:48
uint64_t __of1x_get_expiration_time_slotted(uint32_t timeout, struct timeval *now)
of1x_get_expiration_time_slotted
Definition: of1x_timers.c:100
uint64_t __of1x_get_time_ms(struct timeval *time)
transforms the timeval to a single uint64_t unit time in miliseconds
Definition: of1x_timers.c:89