Reset expired timer vector length after callback
[vpp.git] / src / vppinfra / tw_timer_template.c
index 9253488..a6d26d7 100644 (file)
  *
  *
  */
+#if TW_START_STOP_TRACE_SIZE > 0
+
+void TW (tw_timer_trace) (TWT (tw_timer_wheel) * tw, u32 timer_id,
+                         u32 pool_index, u32 handle)
+{
+  TWT (trace) * t = &tw->traces[tw->trace_index];
+
+  t->timer_id = timer_id;
+  t->pool_index = pool_index;
+  t->handle = handle;
+
+  tw->trace_index++;
+  if (tw->trace_index == TW_START_STOP_TRACE_SIZE)
+    {
+      tw->trace_index = 0;
+      tw->trace_wrapped++;
+    }
+}
+
+void TW (tw_search_trace) (TWT (tw_timer_wheel) * tw, u32 handle)
+{
+  u32 i, start_pos;
+  TWT (trace) * t;
+  char *s = "bogus!";
+
+  /* reverse search for the supplied handle */
+
+  start_pos = tw->trace_index;
+  if (start_pos == 0)
+    start_pos = TW_START_STOP_TRACE_SIZE - 1;
+  else
+    start_pos--;
+
+  for (i = start_pos; i > 0; i--)
+    {
+      t = &tw->traces[i];
+      if (t->handle == handle)
+       {
+         switch (t->timer_id)
+           {
+           case 0xFF:
+             s = "stopped";
+             break;
+           case 0xFE:
+             s = "expired";
+             break;
+           default:
+             s = "started";
+             break;
+           }
+         fformat (stderr, "handle 0x%x (%d) %s at trace %d\n",
+                  handle, handle, s, i);
+       }
+    }
+  if (tw->trace_wrapped > 0)
+    {
+      for (i = TW_START_STOP_TRACE_SIZE; i >= tw->trace_index; i--)
+       {
+         t = &tw->traces[i];
+         if (t->handle == handle)
+           {
+             switch (t->timer_id)
+               {
+               case 0xFF:
+                 s = "stopped";
+                 break;
+               case 0xFE:
+                 s = "expired";
+                 break;
+               default:
+                 s = "started";
+                 break;
+               }
+             fformat (stderr, "handle 0x%x (%d) %s at trace %d\n",
+                      handle, handle, s, i);
+           }
+       }
+    }
+}
+#endif /* TW_START_STOP_TRACE_SIZE > 0 */
+
 static inline u32
 TW (make_internal_timer_handle) (u32 pool_index, u32 timer_id)
 {
@@ -127,6 +208,9 @@ TW (tw_timer_start) (TWT (tw_timer_wheel) * tw, u32 pool_index, u32 timer_id,
       t->expiration_time = tw->current_tick + interval;
       ts = &tw->overflow;
       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
+#if TW_START_STOP_TRACE_SIZE > 0
+      TW (tw_timer_trace) (tw, timer_id, pool_index, t - tw->timers);
+#endif
       return t - tw->timers;
     }
 #endif
@@ -177,7 +261,9 @@ TW (tw_timer_start) (TWT (tw_timer_wheel) * tw, u32 pool_index, u32 timer_id,
       ts = &tw->w[TW_TIMER_RING_GLACIER][glacier_ring_offset];
 
       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
-
+#if TW_START_STOP_TRACE_SIZE > 0
+      TW (tw_timer_trace) (tw, timer_id, pool_index, t - tw->timers);
+#endif
       return t - tw->timers;
     }
 #endif
@@ -193,7 +279,9 @@ TW (tw_timer_start) (TWT (tw_timer_wheel) * tw, u32 pool_index, u32 timer_id,
       ts = &tw->w[TW_TIMER_RING_SLOW][slow_ring_offset];
 
       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
-
+#if TW_START_STOP_TRACE_SIZE > 0
+      TW (tw_timer_trace) (tw, timer_id, pool_index, t - tw->timers);
+#endif
       return t - tw->timers;
     }
 #else
@@ -204,6 +292,14 @@ TW (tw_timer_start) (TWT (tw_timer_wheel) * tw, u32 pool_index, u32 timer_id,
   ts = &tw->w[TW_TIMER_RING_FAST][fast_ring_offset];
 
   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
+
+#if TW_FAST_WHEEL_BITMAP
+  tw->fast_slot_bitmap = clib_bitmap_set (tw->fast_slot_bitmap,
+                                         fast_ring_offset, 1);
+#endif
+#if TW_START_STOP_TRACE_SIZE > 0
+  TW (tw_timer_trace) (tw, timer_id, pool_index, t - tw->timers);
+#endif
   return t - tw->timers;
 }
 
@@ -251,6 +347,19 @@ void TW (tw_timer_stop) (TWT (tw_timer_wheel) * tw, u32 handle)
 {
   TWT (tw_timer) * t;
 
+#if TW_TIMER_ALLOW_DUPLICATE_STOP
+  /*
+   * A vlib process may have its timer expire, and receive
+   * an event before the expiration is processed.
+   * That results in a duplicate tw_timer_stop.
+   */
+  if (pool_is_free_index (tw->timers, handle))
+    return;
+#endif
+#if TW_START_STOP_TRACE_SIZE > 0
+  TW (tw_timer_trace) (tw, ~0, ~0, handle);
+#endif
+
   t = pool_elt_at_index (tw->timers, handle);
 
   /* in case of idiotic handle (e.g. passing a listhead index) */
@@ -287,6 +396,7 @@ TW (tw_timer_wheel_init) (TWT (tw_timer_wheel) * tw,
   tw->timer_interval = timer_interval_in_seconds;
   tw->ticks_per_second = 1.0 / timer_interval_in_seconds;
   tw->first_expires_tick = ~0ULL;
+
   vec_validate (tw->expired_timer_handles, 0);
   _vec_len (tw->expired_timer_handles) = 0;
 
@@ -461,6 +571,10 @@ static inline
                                 new_glacier_ring_offset == 0))
                {
                  vec_add1 (callback_vector, t->user_handle);
+#if TW_START_STOP_TRACE_SIZE > 0
+                 TW (tw_timer_trace) (tw, 0xfe, t->user_handle,
+                                      t - tw->timers);
+#endif
                  pool_put (tw->timers, t);
                }
              /* Timer moves to the glacier ring */
@@ -481,6 +595,11 @@ static inline
                {
                  ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
                  timer_addhead (tw->timers, ts->head_index, t - tw->timers);
+#if TW_FAST_WHEEL_BITMAP
+                 tw->fast_slot_bitmap =
+                   clib_bitmap_set (tw->fast_slot_bitmap,
+                                    t->fast_ring_offset, 1);
+#endif
                }
            }
        }
@@ -516,6 +635,10 @@ static inline
                                 t->fast_ring_offset == 0))
                {
                  vec_add1 (callback_vector, t->user_handle);
+#if TW_START_STOP_TRACE_SIZE > 0
+                 TW (tw_timer_trace) (tw, 0xfe, t->user_handle,
+                                      t - tw->timers);
+#endif
                  pool_put (tw->timers, t);
                }
              /* Timer expires during slow-wheel tick 0 */
@@ -523,6 +646,11 @@ static inline
                {
                  ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
                  timer_addhead (tw->timers, ts->head_index, t - tw->timers);
+#if TW_FAST_WHEEL_BITMAP
+                 tw->fast_slot_bitmap =
+                   clib_bitmap_set (tw->fast_slot_bitmap,
+                                    t->fast_ring_offset, 1);
+#endif
                }
              else              /* typical case */
                {
@@ -562,6 +690,10 @@ static inline
              if (PREDICT_FALSE (t->fast_ring_offset == 0))
                {
                  vec_add1 (callback_vector, t->user_handle);
+#if TW_START_STOP_TRACE_SIZE > 0
+                 TW (tw_timer_trace) (tw, 0xfe, t->user_handle,
+                                      t - tw->timers);
+#endif
                  pool_put (tw->timers, t);
                }
              else              /* typical case */
@@ -569,6 +701,11 @@ static inline
                  /* Add to fast ring */
                  ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
                  timer_addhead (tw->timers, ts->head_index, t - tw->timers);
+#if TW_FAST_WHEEL_BITMAP
+                 tw->fast_slot_bitmap =
+                   clib_bitmap_set (tw->fast_slot_bitmap,
+                                    t->fast_ring_offset, 1);
+#endif
                }
            }
        }
@@ -590,6 +727,9 @@ static inline
          t = pool_elt_at_index (tw->timers, next_index);
          next_index = t->next;
          vec_add1 (callback_vector, t->user_handle);
+#if TW_START_STOP_TRACE_SIZE > 0
+         TW (tw_timer_trace) (tw, 0xfe, t->user_handle, t - tw->timers);
+#endif
          pool_put (tw->timers, t);
        }
 
@@ -600,10 +740,16 @@ static inline
          if (tw->expired_timer_callback)
            {
              tw->expired_timer_callback (callback_vector);
-             _vec_len (callback_vector) = 0;
+             vec_reset_length (callback_vector);
            }
          tw->expired_timer_handles = callback_vector;
        }
+
+#if TW_FAST_WHEEL_BITMAP
+      tw->fast_slot_bitmap = clib_bitmap_set (tw->fast_slot_bitmap,
+                                             fast_wheel_index, 0);
+#endif
+
       tw->current_tick++;
       fast_wheel_index++;
       tw->current_index[TW_TIMER_RING_FAST] = fast_wheel_index;
@@ -642,6 +788,44 @@ u32 *TW (tw_timer_expire_timers_vec) (TWT (tw_timer_wheel) * tw, f64 now,
   return TW (tw_timer_expire_timers_internal) (tw, now, vec);
 }
 
+#if TW_FAST_WHEEL_BITMAP
+/** Returns an approximation to the first timer expiration in
+ * timer-ticks from "now". To avoid wasting an unjustifiable
+ * amount of time on the problem, we maintain an approximate fast-wheel slot
+ * occupancy bitmap. We don't worry about clearing fast wheel bits
+ * when timers are removed from fast wheel slots.
+ */
+
+u32 TW (tw_timer_first_expires_in_ticks) (TWT (tw_timer_wheel) * tw)
+{
+  u32 first_expiring_index, fast_ring_index;
+  i32 delta;
+
+  if (clib_bitmap_is_zero (tw->fast_slot_bitmap))
+    return TW_SLOTS_PER_RING;
+
+  fast_ring_index = tw->current_index[TW_TIMER_RING_FAST];
+  if (fast_ring_index == TW_SLOTS_PER_RING)
+    fast_ring_index = 0;
+
+  first_expiring_index = clib_bitmap_next_set (tw->fast_slot_bitmap,
+                                              fast_ring_index);
+  if (first_expiring_index == ~0 && fast_ring_index != 0)
+    first_expiring_index = clib_bitmap_first_set (tw->fast_slot_bitmap);
+
+  ASSERT (first_expiring_index != ~0);
+
+  delta = (i32) first_expiring_index - (i32) fast_ring_index;
+  if (delta < 0)
+    delta += TW_SLOTS_PER_RING;
+
+  ASSERT (delta >= 0);
+
+  return (u32) delta;
+}
+
+#endif
+
 /*
  * fd.io coding-style-patch-verification: ON
  *