LCOV - code coverage report
Current view: top level - vppinfra - tw_timer_template.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 217 279 77.8 %
Date: 2023-07-05 22:20:52 Functions: 31 74 41.9 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : 
      16             : /** @file
      17             :  *  @brief TW timer implementation TEMPLATE ONLY, do not compile directly
      18             :  *
      19             :  *
      20             :  */
      21             : #if TW_START_STOP_TRACE_SIZE > 0
      22             : 
      23             : void TW (tw_timer_trace) (TWT (tw_timer_wheel) * tw, u32 timer_id,
      24             :                           u32 pool_index, u32 handle)
      25             : {
      26             :   TWT (trace) * t = &tw->traces[tw->trace_index];
      27             : 
      28             :   t->timer_id = timer_id;
      29             :   t->pool_index = pool_index;
      30             :   t->handle = handle;
      31             : 
      32             :   tw->trace_index++;
      33             :   if (tw->trace_index == TW_START_STOP_TRACE_SIZE)
      34             :     {
      35             :       tw->trace_index = 0;
      36             :       tw->trace_wrapped++;
      37             :     }
      38             : }
      39             : 
      40             : void TW (tw_search_trace) (TWT (tw_timer_wheel) * tw, u32 handle)
      41             : {
      42             :   u32 i, start_pos;
      43             :   TWT (trace) * t;
      44             :   char *s = "bogus!";
      45             : 
      46             :   /* reverse search for the supplied handle */
      47             : 
      48             :   start_pos = tw->trace_index;
      49             :   if (start_pos == 0)
      50             :     start_pos = TW_START_STOP_TRACE_SIZE - 1;
      51             :   else
      52             :     start_pos--;
      53             : 
      54             :   for (i = start_pos; i > 0; i--)
      55             :     {
      56             :       t = &tw->traces[i];
      57             :       if (t->handle == handle)
      58             :         {
      59             :           switch (t->timer_id)
      60             :             {
      61             :             case 0xFF:
      62             :               s = "stopped";
      63             :               break;
      64             :             case 0xFE:
      65             :               s = "expired";
      66             :               break;
      67             :             default:
      68             :               s = "started";
      69             :               break;
      70             :             }
      71             :           fformat (stderr, "handle 0x%x (%d) %s at trace %d\n",
      72             :                    handle, handle, s, i);
      73             :         }
      74             :     }
      75             :   if (tw->trace_wrapped > 0)
      76             :     {
      77             :       for (i = TW_START_STOP_TRACE_SIZE; i >= tw->trace_index; i--)
      78             :         {
      79             :           t = &tw->traces[i];
      80             :           if (t->handle == handle)
      81             :             {
      82             :               switch (t->timer_id)
      83             :                 {
      84             :                 case 0xFF:
      85             :                   s = "stopped";
      86             :                   break;
      87             :                 case 0xFE:
      88             :                   s = "expired";
      89             :                   break;
      90             :                 default:
      91             :                   s = "started";
      92             :                   break;
      93             :                 }
      94             :               fformat (stderr, "handle 0x%x (%d) %s at trace %d\n",
      95             :                        handle, handle, s, i);
      96             :             }
      97             :         }
      98             :     }
      99             : }
     100             : #endif /* TW_START_STOP_TRACE_SIZE > 0 */
     101             : 
     102             : static inline u32
     103     1664574 : TW (make_internal_timer_handle) (u32 pool_index, u32 timer_id)
     104             : {
     105             :   u32 handle;
     106             : 
     107     1664574 :   ASSERT (timer_id < TW_TIMERS_PER_OBJECT);
     108             : #if LOG2_TW_TIMERS_PER_OBJECT > 0
     109        3714 :   ASSERT (pool_index < (1 << (32 - LOG2_TW_TIMERS_PER_OBJECT)));
     110             : 
     111        3714 :   handle = (timer_id << (32 - LOG2_TW_TIMERS_PER_OBJECT)) | (pool_index);
     112             : #else
     113     1660860 :   handle = pool_index;
     114             : #endif
     115     1664574 :   return handle;
     116             : }
     117             : 
     118             : static inline void
     119     2057270 : timer_addhead (TWT (tw_timer) * pool, u32 head_index, u32 new_index)
     120             : {
     121     2057270 :   TWT (tw_timer) * head = pool_elt_at_index (pool, head_index);
     122             :   TWT (tw_timer) * old_first;
     123             :   u32 old_first_index;
     124             :   TWT (tw_timer) * new;
     125             : 
     126     2057270 :   new = pool_elt_at_index (pool, new_index);
     127             : 
     128     2057270 :   if (PREDICT_FALSE (head->next == head_index))
     129             :     {
     130     1710383 :       head->next = head->prev = new_index;
     131     1710383 :       new->next = new->prev = head_index;
     132     1710383 :       return;
     133             :     }
     134             : 
     135      346886 :   old_first_index = head->next;
     136      346886 :   old_first = pool_elt_at_index (pool, old_first_index);
     137             : 
     138      346886 :   new->next = old_first_index;
     139      346886 :   new->prev = old_first->prev;
     140      346886 :   old_first->prev = new_index;
     141      346886 :   head->next = new_index;
     142             : }
     143             : 
     144             : static inline void
     145      482284 : timer_remove (TWT (tw_timer) * pool, TWT (tw_timer) * elt)
     146             : {
     147             :   TWT (tw_timer) * next_elt, *prev_elt;
     148             : 
     149      482284 :   ASSERT (elt->user_handle != ~0);
     150             : 
     151      482284 :   next_elt = pool_elt_at_index (pool, elt->next);
     152      482284 :   prev_elt = pool_elt_at_index (pool, elt->prev);
     153             : 
     154      482284 :   next_elt->prev = elt->prev;
     155      482284 :   prev_elt->next = elt->next;
     156             : 
     157      482284 :   elt->prev = elt->next = ~0;
     158      482284 : }
     159             : 
     160             : static inline void
     161     1717102 : timer_add (TWT (tw_timer_wheel) * tw, TWT (tw_timer) * t, u64 interval)
     162             : {
     163             : #if TW_TIMER_WHEELS > 1
     164             :   u16 slow_ring_offset;
     165             :   u32 carry;
     166             : #endif
     167             : #if TW_TIMER_WHEELS > 2
     168             :   u16 glacier_ring_offset;
     169             : #endif
     170             : #if TW_OVERFLOW_VECTOR > 0
     171             :   u64 interval_plus_time_to_wrap, triple_wrap_mask;
     172             : #endif
     173             :   u16 fast_ring_offset;
     174             :   tw_timer_wheel_slot_t *ts;
     175             : 
     176             :   /* Factor interval into 1..3 wheel offsets */
     177             : #if TW_TIMER_WHEELS > 2
     178             : #if TW_OVERFLOW_VECTOR > 0
     179             :   /*
     180             :    * This is tricky. Put a timer onto the overflow
     181             :    * vector if the interval PLUS the time
     182             :    * until the next triple-wrap exceeds one full revolution
     183             :    * of all three wheels.
     184             :    */
     185     1674510 :   triple_wrap_mask = (1 << (3 * TW_RING_SHIFT)) - 1;
     186     1674510 :   interval_plus_time_to_wrap =
     187     1674510 :     interval + (tw->current_tick & triple_wrap_mask);
     188     1674510 :   if ((interval_plus_time_to_wrap >= 1 << (3 * TW_RING_SHIFT)))
     189             :     {
     190        7310 :       t->expiration_time = tw->current_tick + interval;
     191        7310 :       ts = &tw->overflow;
     192        7310 :       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     193             : #if TW_START_STOP_TRACE_SIZE > 0
     194             :       TW (tw_timer_trace) (tw, timer_id, user_id, t - tw->timers);
     195             : #endif
     196        7310 :       return;
     197             :     }
     198             : #endif
     199             : 
     200     1667200 :   glacier_ring_offset = interval >> (2 * TW_RING_SHIFT);
     201     1667200 :   ASSERT ((u64) glacier_ring_offset < TW_SLOTS_PER_RING);
     202     1667200 :   interval -= (((u64) glacier_ring_offset) << (2 * TW_RING_SHIFT));
     203             : #endif
     204             : #if TW_TIMER_WHEELS > 1
     205     1709787 :   slow_ring_offset = interval >> TW_RING_SHIFT;
     206     1709787 :   ASSERT ((u64) slow_ring_offset < TW_SLOTS_PER_RING);
     207     1709787 :   interval -= (((u64) slow_ring_offset) << TW_RING_SHIFT);
     208             : #endif
     209     1709792 :   fast_ring_offset = interval & TW_RING_MASK;
     210             : 
     211             :   /*
     212             :    * Account for the current wheel positions(s)
     213             :    * This is made slightly complicated by the fact that the current
     214             :    * index vector will contain (TW_SLOTS_PER_RING, ...) when
     215             :    * the actual position is (0, ...)
     216             :    */
     217             : 
     218     1709792 :   fast_ring_offset += tw->current_index[TW_TIMER_RING_FAST] & TW_RING_MASK;
     219             : 
     220             : #if TW_TIMER_WHEELS > 1
     221     1709787 :   carry = fast_ring_offset >= TW_SLOTS_PER_RING ? 1 : 0;
     222     1709787 :   fast_ring_offset %= TW_SLOTS_PER_RING;
     223     1709787 :   slow_ring_offset += (tw->current_index[TW_TIMER_RING_SLOW] & TW_RING_MASK)
     224     1709787 :     + carry;
     225     1709787 :   carry = slow_ring_offset >= TW_SLOTS_PER_RING ? 1 : 0;
     226     1709787 :   slow_ring_offset %= TW_SLOTS_PER_RING;
     227             : #endif
     228             : 
     229             : #if TW_TIMER_WHEELS > 2
     230     1667200 :   glacier_ring_offset +=
     231     1667200 :     (tw->current_index[TW_TIMER_RING_GLACIER] & TW_RING_MASK) + carry;
     232     1667200 :   glacier_ring_offset %= TW_SLOTS_PER_RING;
     233             : #endif
     234             : 
     235             : #if TW_TIMER_WHEELS > 2
     236     1667200 :   if (glacier_ring_offset !=
     237     1667200 :       (tw->current_index[TW_TIMER_RING_GLACIER] & TW_RING_MASK))
     238             :     {
     239             :       /* We'll need slow and fast ring offsets later */
     240      420333 :       t->slow_ring_offset = slow_ring_offset;
     241      420333 :       t->fast_ring_offset = fast_ring_offset;
     242             : 
     243      420333 :       ts = &tw->w[TW_TIMER_RING_GLACIER][glacier_ring_offset];
     244             : 
     245      420333 :       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     246             : #if TW_START_STOP_TRACE_SIZE > 0
     247             :       TW (tw_timer_trace) (tw, timer_id, user_id, t - tw->timers);
     248             : #endif
     249      420333 :       return;
     250             :     }
     251             : #endif
     252             : 
     253             : #if TW_TIMER_WHEELS > 1
     254             :   /* Timer expires more than 51.2 seconds from now? */
     255     1289457 :   if (slow_ring_offset !=
     256     1289457 :       (tw->current_index[TW_TIMER_RING_SLOW] & TW_RING_MASK))
     257             :     {
     258             :       /* We'll need the fast ring offset later... */
     259      383359 :       t->fast_ring_offset = fast_ring_offset;
     260             : 
     261      383359 :       ts = &tw->w[TW_TIMER_RING_SLOW][slow_ring_offset];
     262             : 
     263      383359 :       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     264             : #if TW_START_STOP_TRACE_SIZE > 0
     265             :       TW (tw_timer_trace) (tw, timer_id, user_id, t - tw->timers);
     266             : #endif
     267      383359 :       return;
     268             :     }
     269             : #else
     270           5 :   fast_ring_offset %= TW_SLOTS_PER_RING;
     271             : #endif
     272             : 
     273             :   /* Timer expires less than one fast-ring revolution from now */
     274      906100 :   ts = &tw->w[TW_TIMER_RING_FAST][fast_ring_offset];
     275             : 
     276      906100 :   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     277             : 
     278             : #if TW_FAST_WHEEL_BITMAP
     279      905948 :   tw->fast_slot_bitmap = clib_bitmap_set (tw->fast_slot_bitmap,
     280             :                                           fast_ring_offset, 1);
     281             : #endif
     282             : #if TW_START_STOP_TRACE_SIZE > 0
     283             :   TW (tw_timer_trace) (tw, timer_id, user_id, t - tw->timers);
     284             : #endif
     285           5 : }
     286             : 
     287             : /**
     288             :  * @brief Start a Tw Timer
     289             :  * @param tw_timer_wheel_t * tw timer wheel object pointer
     290             :  * @param u32 user_id user defined timer id, presumably for a tw session
     291             :  * @param u32 timer_id app-specific timer ID. 4 bits.
     292             :  * @param u64 interval timer interval in ticks
     293             :  * @returns handle needed to cancel the timer
     294             :  */
     295             : __clib_export u32
     296     1664574 : TW (tw_timer_start) (TWT (tw_timer_wheel) * tw, u32 user_id, u32 timer_id,
     297             :                      u64 interval)
     298             : {
     299             :   TWT (tw_timer) * t;
     300             : 
     301     1664574 :   ASSERT (interval);
     302             : 
     303     1664574 :   pool_get (tw->timers, t);
     304     1664574 :   clib_memset (t, 0xff, sizeof (*t));
     305             : 
     306     1664574 :   t->user_handle = TW (make_internal_timer_handle) (user_id, timer_id);
     307             : 
     308     1664574 :   timer_add (tw, t, interval);
     309     1664574 :   return t - tw->timers;
     310             : }
     311             : 
     312             : #if TW_TIMER_SCAN_FOR_HANDLE > 0
     313             : int TW (scan_for_handle) (TWT (tw_timer_wheel) * tw, u32 handle)
     314             : {
     315             :   int i, j;
     316             :   tw_timer_wheel_slot_t *ts;
     317             :   TWT (tw_timer) * t, *head;
     318             :   u32 next_index;
     319             :   int rv = 0;
     320             : 
     321             :   for (i = 0; i < TW_TIMER_WHEELS; i++)
     322             :     {
     323             :       for (j = 0; j < TW_SLOTS_PER_RING; j++)
     324             :         {
     325             :           ts = &tw->w[i][j];
     326             :           head = pool_elt_at_index (tw->timers, ts->head_index);
     327             :           next_index = head->next;
     328             : 
     329             :           while (next_index != ts->head_index)
     330             :             {
     331             :               t = pool_elt_at_index (tw->timers, next_index);
     332             :               if (next_index == handle)
     333             :                 {
     334             :                   clib_warning ("handle %d found in ring %d slot %d",
     335             :                                 handle, i, j);
     336             :                   clib_warning ("user handle 0x%x", t->user_handle);
     337             :                   rv = 1;
     338             :                 }
     339             :               next_index = t->next;
     340             :             }
     341             :         }
     342             :     }
     343             :   return rv;
     344             : }
     345             : #endif /* TW_TIMER_SCAN_FOR_HANDLE */
     346             : 
     347             : /**
     348             :  * @brief Stop a tw timer
     349             :  * @param tw_timer_wheel_t * tw timer wheel object pointer
     350             :  * @param u32 handle timer cancellation returned by tw_timer_start
     351             :  */
     352      429751 : __clib_export void TW (tw_timer_stop) (TWT (tw_timer_wheel) * tw, u32 handle)
     353             : {
     354             :   TWT (tw_timer) * t;
     355             : 
     356             : #if TW_TIMER_ALLOW_DUPLICATE_STOP
     357             :   /*
     358             :    * A vlib process may have its timer expire, and receive
     359             :    * an event before the expiration is processed.
     360             :    * That results in a duplicate tw_timer_stop.
     361             :    */
     362      429751 :   if (pool_is_free_index (tw->timers, handle))
     363           0 :     return;
     364             : #endif
     365             : #if TW_START_STOP_TRACE_SIZE > 0
     366             :   TW (tw_timer_trace) (tw, ~0, ~0, handle);
     367             : #endif
     368             : 
     369      429751 :   t = pool_elt_at_index (tw->timers, handle);
     370             : 
     371             :   /* in case of idiotic handle (e.g. passing a listhead index) */
     372      429751 :   ASSERT (t->user_handle != ~0);
     373             : 
     374      429751 :   timer_remove (tw->timers, t);
     375             : 
     376      429751 :   pool_put_index (tw->timers, handle);
     377           0 : }
     378             : 
     379             : __clib_export int
     380      438424 : TW (tw_timer_handle_is_free) (TWT (tw_timer_wheel) * tw, u32 handle)
     381             : {
     382      438424 :   return pool_is_free_index (tw->timers, handle);
     383             : }
     384             : 
     385             : /**
     386             :  * @brief Update a tw timer
     387             :  * @param tw_timer_wheel_t * tw timer wheel object pointer
     388             :  * @param u32 handle timer returned by tw_timer_start
     389             :  * @param u32 interval timer interval in ticks
     390             :  */
     391             : __clib_export void
     392       52533 : TW (tw_timer_update) (TWT (tw_timer_wheel) * tw, u32 handle, u64 interval)
     393             : {
     394             :   TWT (tw_timer) * t;
     395       52533 :   t = pool_elt_at_index (tw->timers, handle);
     396       52533 :   timer_remove (tw->timers, t);
     397       52533 :   timer_add (tw, t, interval);
     398       52533 : }
     399             : 
     400             : /**
     401             :  * @brief Initialize a tw timer wheel template instance
     402             :  * @param tw_timer_wheel_t * tw timer wheel object pointer
     403             :  * @param void * expired_timer_callback. Passed a u32 * vector of
     404             :  *   expired timer handles. The callback is optional.
     405             :  * @param f64 timer_interval_in_seconds
     406             :  */
     407             : __clib_export void
     408        2403 : TW (tw_timer_wheel_init) (TWT (tw_timer_wheel) * tw,
     409             :                           void *expired_timer_callback,
     410             :                           f64 timer_interval_in_seconds, u32 max_expirations)
     411             : {
     412             :   int ring, slot;
     413             :   tw_timer_wheel_slot_t *ts;
     414             :   TWT (tw_timer) * t;
     415        2403 :   clib_memset (tw, 0, sizeof (*tw));
     416        2403 :   tw->expired_timer_callback = expired_timer_callback;
     417        2403 :   tw->max_expirations = max_expirations;
     418        2403 :   if (timer_interval_in_seconds == 0.0)
     419             :     {
     420           0 :       clib_warning ("timer interval is zero");
     421           0 :       abort ();
     422             :     }
     423        2403 :   tw->timer_interval = timer_interval_in_seconds;
     424        2403 :   tw->ticks_per_second = 1.0 / timer_interval_in_seconds;
     425             : 
     426        2403 :   vec_validate (tw->expired_timer_handles, 0);
     427        2403 :   vec_set_len (tw->expired_timer_handles, 0);
     428             : 
     429        8881 :   for (ring = 0; ring < TW_TIMER_WHEELS; ring++)
     430             :     {
     431     6119753 :       for (slot = 0; slot < TW_SLOTS_PER_RING; slot++)
     432             :         {
     433     6113284 :           ts = &tw->w[ring][slot];
     434     6113284 :           pool_get (tw->timers, t);
     435     6113284 :           clib_memset (t, 0xff, sizeof (*t));
     436     6113284 :           t->next = t->prev = t - tw->timers;
     437     6113284 :           ts->head_index = t - tw->timers;
     438             :         }
     439             :     }
     440             : 
     441             : #if TW_OVERFLOW_VECTOR > 0
     442        1723 :   ts = &tw->overflow;
     443        1723 :   pool_get (tw->timers, t);
     444        1723 :   clib_memset (t, 0xff, sizeof (*t));
     445        1723 :   t->next = t->prev = t - tw->timers;
     446        1723 :   ts->head_index = t - tw->timers;
     447             : #endif
     448        2403 : }
     449             : 
     450             : /**
     451             :  * @brief Free a tw timer wheel template instance
     452             :  * @param tw_timer_wheel_t * tw timer wheel object pointer
     453             :  */
     454           0 : __clib_export void TW (tw_timer_wheel_free) (TWT (tw_timer_wheel) * tw)
     455             : {
     456             :   int i, j;
     457             :   tw_timer_wheel_slot_t *ts;
     458             :   TWT (tw_timer) * head, *t;
     459             :   u32 next_index;
     460             : 
     461           0 :   for (i = 0; i < TW_TIMER_WHEELS; i++)
     462             :     {
     463           0 :       for (j = 0; j < TW_SLOTS_PER_RING; j++)
     464             :         {
     465           0 :           ts = &tw->w[i][j];
     466           0 :           head = pool_elt_at_index (tw->timers, ts->head_index);
     467           0 :           next_index = head->next;
     468             : 
     469           0 :           while (next_index != ts->head_index)
     470             :             {
     471           0 :               t = pool_elt_at_index (tw->timers, next_index);
     472           0 :               next_index = t->next;
     473           0 :               pool_put (tw->timers, t);
     474             :             }
     475           0 :           pool_put (tw->timers, head);
     476             :         }
     477             :     }
     478             : 
     479             : #if TW_OVERFLOW_VECTOR > 0
     480           0 :   ts = &tw->overflow;
     481           0 :   head = pool_elt_at_index (tw->timers, ts->head_index);
     482           0 :   next_index = head->next;
     483             : 
     484           0 :   while (next_index != ts->head_index)
     485             :     {
     486           0 :       t = pool_elt_at_index (tw->timers, next_index);
     487           0 :       next_index = t->next;
     488           0 :       pool_put (tw->timers, t);
     489             :     }
     490           0 :   pool_put (tw->timers, head);
     491             : #endif
     492             : 
     493           0 :   clib_memset (tw, 0, sizeof (*tw));
     494           0 : }
     495             : 
     496             : /**
     497             :  * @brief Advance a tw timer wheel. Calls the expired timer callback
     498             :  * as needed. This routine should be called once every timer_interval seconds
     499             :  * @param tw_timer_wheel_t * tw timer wheel template instance pointer
     500             :  * @param f64 now the current time, e.g. from vlib_time_now(vm)
     501             :  * @returns u32 * vector of expired user handles
     502             :  */
     503             : static inline
     504   609962690 :   u32 * TW (tw_timer_expire_timers_internal) (TWT (tw_timer_wheel) * tw,
     505             :                                               f64 now,
     506             :                                               u32 * callback_vector_arg)
     507             : {
     508             :   u32 nticks, i;
     509             :   tw_timer_wheel_slot_t *ts;
     510             :   TWT (tw_timer) * t, *head;
     511             :   u32 *callback_vector;
     512             :   u32 fast_wheel_index;
     513             :   u32 next_index;
     514             :   u32 slow_wheel_index __attribute__ ((unused));
     515             :   u32 glacier_wheel_index __attribute__ ((unused));
     516             : 
     517             :   /* Called too soon to process new timer expirations? */
     518   609962690 :   if (PREDICT_FALSE (now < tw->next_run_time))
     519   551672549 :     return callback_vector_arg;
     520             : 
     521             :   /* Number of ticks which have occurred */
     522    58289884 :   nticks = tw->ticks_per_second * (now - tw->last_run_time);
     523    58289884 :   if (nticks == 0)
     524           3 :     return callback_vector_arg;
     525             : 
     526             :   /* Remember when we ran, compute next runtime */
     527    58289881 :   tw->next_run_time = (now + tw->timer_interval);
     528             : 
     529             :   /* First call, or time jumped backwards? */
     530    58289881 :   if (PREDICT_FALSE
     531             :       ((tw->last_run_time == 0.0) || (now <= tw->last_run_time)))
     532             :     {
     533          31 :       tw->last_run_time = now;
     534          31 :       return callback_vector_arg;
     535             :     }
     536             : 
     537    58377636 :   if (callback_vector_arg == 0)
     538             :     {
     539    58374575 :       vec_set_len (tw->expired_timer_handles, 0);
     540    58370749 :       callback_vector = tw->expired_timer_handles;
     541             :     }
     542             :   else
     543        3230 :     callback_vector = callback_vector_arg;
     544             : 
     545   781171179 :   for (i = 0; i < nticks; i++)
     546             :     {
     547   722795702 :       fast_wheel_index = tw->current_index[TW_TIMER_RING_FAST];
     548             :       if (TW_TIMER_WHEELS > 1)
     549   722795626 :         slow_wheel_index = tw->current_index[TW_TIMER_RING_SLOW];
     550             :       if (TW_TIMER_WHEELS > 2)
     551   721669000 :         glacier_wheel_index = tw->current_index[TW_TIMER_RING_GLACIER];
     552             : 
     553             : #if TW_OVERFLOW_VECTOR > 0
     554             :       /* Triple odometer-click? Process the overflow vector... */
     555   721669000 :       if (PREDICT_FALSE (fast_wheel_index == TW_SLOTS_PER_RING
     556             :                          && slow_wheel_index == TW_SLOTS_PER_RING
     557             :                          && glacier_wheel_index == TW_SLOTS_PER_RING))
     558             :         {
     559             :           u64 interval;
     560             :           u32 new_glacier_ring_offset, new_slow_ring_offset;
     561             :           u32 new_fast_ring_offset;
     562             : 
     563           0 :           ts = &tw->overflow;
     564           0 :           head = pool_elt_at_index (tw->timers, ts->head_index);
     565           0 :           next_index = head->next;
     566             : 
     567             :           /* Make slot empty */
     568           0 :           head->next = head->prev = ts->head_index;
     569             : 
     570             :           /* traverse slot, place timers wherever they go */
     571           0 :           while (next_index != head - tw->timers)
     572             :             {
     573           0 :               t = pool_elt_at_index (tw->timers, next_index);
     574           0 :               next_index = t->next;
     575             : 
     576             :               /* Remove from the overflow vector (hammer) */
     577           0 :               t->next = t->prev = ~0;
     578             : 
     579           0 :               ASSERT (t->expiration_time >= tw->current_tick);
     580             : 
     581           0 :               interval = t->expiration_time - tw->current_tick;
     582             : 
     583             :               /* Right back onto the overflow vector? */
     584           0 :               if (interval >= (1 << (3 * TW_RING_SHIFT)))
     585             :                 {
     586           0 :                   ts = &tw->overflow;
     587           0 :                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     588           0 :                   continue;
     589             :                 }
     590             :               /* Compute ring offsets */
     591           0 :               new_glacier_ring_offset = interval >> (2 * TW_RING_SHIFT);
     592             : 
     593           0 :               interval -= (new_glacier_ring_offset << (2 * TW_RING_SHIFT));
     594             : 
     595             :               /* Note: the wheels are at (0,0,0), no add-with-carry needed */
     596           0 :               new_slow_ring_offset = interval >> TW_RING_SHIFT;
     597           0 :               interval -= (new_slow_ring_offset << TW_RING_SHIFT);
     598           0 :               new_fast_ring_offset = interval & TW_RING_MASK;
     599           0 :               t->slow_ring_offset = new_slow_ring_offset;
     600           0 :               t->fast_ring_offset = new_fast_ring_offset;
     601             : 
     602             :               /* Timer expires Right Now */
     603           0 :               if (PREDICT_FALSE (t->slow_ring_offset == 0 &&
     604             :                                  t->fast_ring_offset == 0 &&
     605             :                                  new_glacier_ring_offset == 0))
     606             :                 {
     607           0 :                   vec_add1 (callback_vector, t->user_handle);
     608             : #if TW_START_STOP_TRACE_SIZE > 0
     609             :                   TW (tw_timer_trace) (tw, 0xfe, t->user_handle,
     610             :                                        t - tw->timers);
     611             : #endif
     612           0 :                   pool_put (tw->timers, t);
     613             :                 }
     614             :               /* Timer moves to the glacier ring */
     615           0 :               else if (new_glacier_ring_offset)
     616             :                 {
     617           0 :                   ts = &tw->w[TW_TIMER_RING_GLACIER][new_glacier_ring_offset];
     618           0 :                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     619             :                 }
     620             :               /* Timer moves to the slow ring */
     621           0 :               else if (t->slow_ring_offset)
     622             :                 {
     623             :                   /* Add to slow ring */
     624           0 :                   ts = &tw->w[TW_TIMER_RING_SLOW][t->slow_ring_offset];
     625           0 :                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     626             :                 }
     627             :               /* Timer timer moves to the fast ring */
     628             :               else
     629             :                 {
     630           0 :                   ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
     631           0 :                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     632             : #if TW_FAST_WHEEL_BITMAP
     633           0 :                   tw->fast_slot_bitmap =
     634           0 :                     clib_bitmap_set (tw->fast_slot_bitmap,
     635           0 :                                      t->fast_ring_offset, 1);
     636             : #endif
     637             :                 }
     638             :             }
     639             :         }
     640             : #endif
     641             : 
     642             : #if TW_TIMER_WHEELS > 2
     643             :       /*
     644             :        * Double odometer-click? Process one slot in the glacier ring...
     645             :        */
     646   721669000 :       if (PREDICT_FALSE (fast_wheel_index == TW_SLOTS_PER_RING
     647             :                          && slow_wheel_index == TW_SLOTS_PER_RING))
     648             :         {
     649         466 :           glacier_wheel_index %= TW_SLOTS_PER_RING;
     650         466 :           ts = &tw->w[TW_TIMER_RING_GLACIER][glacier_wheel_index];
     651             : 
     652         466 :           head = pool_elt_at_index (tw->timers, ts->head_index);
     653         466 :           next_index = head->next;
     654             : 
     655             :           /* Make slot empty */
     656         466 :           head->next = head->prev = ts->head_index;
     657             : 
     658             :           /* traverse slot, deal timers into slow ring */
     659        4294 :           while (next_index != head - tw->timers)
     660             :             {
     661        3828 :               t = pool_elt_at_index (tw->timers, next_index);
     662        3828 :               next_index = t->next;
     663             : 
     664             :               /* Remove from glacier ring slot (hammer) */
     665        3828 :               t->next = t->prev = ~0;
     666             : 
     667             :               /* Timer expires Right Now */
     668        3828 :               if (PREDICT_FALSE (t->slow_ring_offset == 0 &&
     669             :                                  t->fast_ring_offset == 0))
     670             :                 {
     671           1 :                   vec_add1 (callback_vector, t->user_handle);
     672             : #if TW_START_STOP_TRACE_SIZE > 0
     673             :                   TW (tw_timer_trace) (tw, 0xfe, t->user_handle,
     674             :                                        t - tw->timers);
     675             : #endif
     676           1 :                   pool_put (tw->timers, t);
     677             :                 }
     678             :               /* Timer expires during slow-wheel tick 0 */
     679        3827 :               else if (PREDICT_FALSE (t->slow_ring_offset == 0))
     680             :                 {
     681         222 :                   ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
     682         222 :                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     683             : #if TW_FAST_WHEEL_BITMAP
     684         222 :                   tw->fast_slot_bitmap =
     685         222 :                     clib_bitmap_set (tw->fast_slot_bitmap,
     686         222 :                                      t->fast_ring_offset, 1);
     687             : #endif
     688             :                 }
     689             :               else              /* typical case */
     690             :                 {
     691             :                   /* Add to slow ring */
     692        3605 :                   ts = &tw->w[TW_TIMER_RING_SLOW][t->slow_ring_offset];
     693        3605 :                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     694             :                 }
     695             :             }
     696             :         }
     697             : #endif
     698             : 
     699             : #if TW_TIMER_WHEELS > 1
     700             :       /*
     701             :        * Single odometer-click? Process a slot in the slow ring,
     702             :        */
     703   722795626 :       if (PREDICT_FALSE (fast_wheel_index == TW_SLOTS_PER_RING))
     704             :         {
     705      705574 :           slow_wheel_index %= TW_SLOTS_PER_RING;
     706      705574 :           ts = &tw->w[TW_TIMER_RING_SLOW][slow_wheel_index];
     707             : 
     708      705574 :           head = pool_elt_at_index (tw->timers, ts->head_index);
     709      705574 :           next_index = head->next;
     710             : 
     711             :           /* Make slot empty */
     712      705574 :           head->next = head->prev = ts->head_index;
     713             : 
     714             :           /* traverse slot, deal timers into fast ring */
     715     1043063 :           while (next_index != head - tw->timers)
     716             :             {
     717      337486 :               t = pool_elt_at_index (tw->timers, next_index);
     718      337486 :               next_index = t->next;
     719             : 
     720             :               /* Remove from sloe ring slot (hammer) */
     721      337486 :               t->next = t->prev = ~0;
     722             : 
     723             :               /* Timer expires Right Now */
     724      337486 :               if (PREDICT_FALSE (t->fast_ring_offset == 0))
     725             :                 {
     726        1144 :                   vec_add1 (callback_vector, t->user_handle);
     727             : #if TW_START_STOP_TRACE_SIZE > 0
     728             :                   TW (tw_timer_trace) (tw, 0xfe, t->user_handle,
     729             :                                        t - tw->timers);
     730             : #endif
     731        1144 :                   pool_put (tw->timers, t);
     732             :                 }
     733             :               else              /* typical case */
     734             :                 {
     735             :                   /* Add to fast ring */
     736      336342 :                   ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
     737      336342 :                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
     738             : #if TW_FAST_WHEEL_BITMAP
     739      336294 :                   tw->fast_slot_bitmap =
     740      336294 :                     clib_bitmap_set (tw->fast_slot_bitmap,
     741      336294 :                                      t->fast_ring_offset, 1);
     742             : #endif
     743             :                 }
     744             :             }
     745             :         }
     746             : #endif
     747             : 
     748             :       /* Handle the fast ring */
     749   722795702 :       fast_wheel_index %= TW_SLOTS_PER_RING;
     750   722795702 :       ts = &tw->w[TW_TIMER_RING_FAST][fast_wheel_index];
     751             : 
     752   722795702 :       head = pool_elt_at_index (tw->timers, ts->head_index);
     753   722798662 :       next_index = head->next;
     754             : 
     755             :       /* Make slot empty */
     756   722798662 :       head->next = head->prev = ts->head_index;
     757             : 
     758             :       /* Construct vector of expired timer handles to give the user */
     759   724019810 :       while (next_index != ts->head_index)
     760             :         {
     761     1220420 :           t = pool_elt_at_index (tw->timers, next_index);
     762     1220420 :           next_index = t->next;
     763     1220420 :           vec_add1 (callback_vector, t->user_handle);
     764             : #if TW_START_STOP_TRACE_SIZE > 0
     765             :           TW (tw_timer_trace) (tw, 0xfe, t->user_handle, t - tw->timers);
     766             : #endif
     767     1220420 :           pool_put (tw->timers, t);
     768             :         }
     769             : 
     770             :       /* If any timers expired, tell the user */
     771   722798632 :       if (callback_vector_arg == 0 && vec_len (callback_vector))
     772             :         {
     773             :           /* The callback is optional. We return the u32 * handle vector */
     774     1071441 :           if (tw->expired_timer_callback)
     775             :             {
     776     1071453 :               tw->expired_timer_callback (callback_vector);
     777     1071453 :               vec_reset_length (callback_vector);
     778             :             }
     779     1071453 :           tw->expired_timer_handles = callback_vector;
     780             :         }
     781             : 
     782             : #if TW_FAST_WHEEL_BITMAP
     783   721669000 :       tw->fast_slot_bitmap = clib_bitmap_set (tw->fast_slot_bitmap,
     784             :                                               fast_wheel_index, 0);
     785             : #endif
     786             : 
     787   722797882 :       tw->current_tick++;
     788   722797882 :       fast_wheel_index++;
     789   722797882 :       tw->current_index[TW_TIMER_RING_FAST] = fast_wheel_index;
     790             : 
     791             : #if TW_TIMER_WHEELS > 1
     792   722797806 :       if (PREDICT_FALSE (fast_wheel_index == TW_SLOTS_PER_RING))
     793      705577 :         slow_wheel_index++;
     794   722797806 :       tw->current_index[TW_TIMER_RING_SLOW] = slow_wheel_index;
     795             : #endif
     796             : 
     797             : #if TW_TIMER_WHEELS > 2
     798   721669000 :       if (PREDICT_FALSE (slow_wheel_index == TW_SLOTS_PER_RING))
     799         466 :         glacier_wheel_index++;
     800   721669000 :       tw->current_index[TW_TIMER_RING_GLACIER] = glacier_wheel_index;
     801             : #endif
     802             : 
     803   722797882 :       if (vec_len (callback_vector) >= tw->max_expirations)
     804           0 :         break;
     805             :     }
     806             : 
     807    58375384 :   if (callback_vector_arg == 0)
     808    58373287 :     tw->expired_timer_handles = callback_vector;
     809             : 
     810    58375384 :   tw->last_run_time += i * tw->timer_interval;
     811    58375384 :   return callback_vector;
     812             : }
     813             : 
     814   609957990 : __clib_export u32 *TW (tw_timer_expire_timers) (TWT (tw_timer_wheel) * tw,
     815             :                                                 f64 now)
     816             : {
     817   609957990 :   return TW (tw_timer_expire_timers_internal) (tw, now, 0 /* no vector */ );
     818             : }
     819             : 
     820        6659 : __clib_export u32 *TW (tw_timer_expire_timers_vec) (TWT (tw_timer_wheel) * tw,
     821             :                                                     f64 now, u32 * vec)
     822             : {
     823        6659 :   return TW (tw_timer_expire_timers_internal) (tw, now, vec);
     824             : }
     825             : 
     826             : #if TW_FAST_WHEEL_BITMAP
     827             : /** Returns an approximation to the first timer expiration in
     828             :  * timer-ticks from "now". To avoid wasting an unjustifiable
     829             :  * amount of time on the problem, we maintain an approximate fast-wheel slot
     830             :  * occupancy bitmap. We don't worry about clearing fast wheel bits
     831             :  * when timers are removed from fast wheel slots.
     832             :  */
     833             : 
     834             : __clib_export u32
     835    95956500 : TW (tw_timer_first_expires_in_ticks) (TWT (tw_timer_wheel) * tw)
     836             : {
     837             :   u32 first_expiring_index, fast_ring_index;
     838             :   i32 delta;
     839             : 
     840             : #if TW_TIMER_WHEELS > 1
     841    95956500 :   fast_ring_index = tw->current_index[TW_TIMER_RING_FAST];
     842    95956500 :   if (fast_ring_index == TW_SLOTS_PER_RING)
     843      756453 :     return 1;
     844             : 
     845    95200100 :   first_expiring_index = clib_bitmap_next_set (tw->fast_slot_bitmap,
     846             :                                                fast_ring_index);
     847    95200100 :   if (first_expiring_index == ~0)
     848    24440200 :     first_expiring_index = TW_SLOTS_PER_RING;
     849             : 
     850             : #else
     851             : 
     852             :   if (clib_bitmap_is_zero (tw->fast_slot_bitmap))
     853             :     return TW_SLOTS_PER_RING;
     854             : 
     855             :   fast_ring_index = tw->current_index[TW_TIMER_RING_FAST];
     856             :   if (fast_ring_index == TW_SLOTS_PER_RING)
     857             :     fast_ring_index = 0;
     858             : 
     859             :   first_expiring_index = clib_bitmap_next_set (tw->fast_slot_bitmap,
     860             :                                                fast_ring_index);
     861             :   if (first_expiring_index == ~0 && fast_ring_index != 0)
     862             :     first_expiring_index = clib_bitmap_first_set (tw->fast_slot_bitmap);
     863             : #endif
     864             : 
     865    95200100 :   ASSERT (first_expiring_index != ~0);
     866             : 
     867    95200100 :   delta = (i32) first_expiring_index - (i32) fast_ring_index;
     868    95200100 :   if (delta < 0)
     869           0 :     delta += TW_SLOTS_PER_RING;
     870             : 
     871    95200100 :   ASSERT (delta >= 0);
     872             : 
     873    95200100 :   return (u32) delta;
     874             : }
     875             : 
     876             : #endif
     877             : 
     878             : /*
     879             :  * fd.io coding-style-patch-verification: ON
     880             :  *
     881             :  * Local Variables:
     882             :  * eval: (c-set-style "gnu")
     883             :  * End:
     884             :  */

Generated by: LCOV version 1.14