1/*
2 *------------------------------------------------------------------
3 * Copyright (c) 2017 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *------------------------------------------------------------------
16 */
17
18#include <igmp/igmp_timer.h>
19#include <igmp/igmp.h>
20
21/**
22 * Default timer values as per RFC
23 */
24
25static igmp_timer_type_t igmp_default_timer_values[] = {
26  [IGMP_TIMER_QUERY] = 60,
27  [IGMP_TIMER_SRC] = (3 * 60),
28  [IGMP_TIMER_LEAVE] = 60,
29  [IGMP_TIMER_REPORT_INTERVAL] = 1,
30};
31
32#define IGMP_N_TIMERS (IGMP_TIMER_REPORT_INTERVAL+1)
33
34/**
35 * Timer
36 */
37typedef struct igmp_timer_t_
38{
39  /** Expiration timer */
40  f64 exp_time;
41
42  /** Call-back function to invoke on expiry */
43  igmp_timer_function_t func;
44
45  /** index of the object that scheduled the timer */
46  u32 obj;
47
48  /** Data registered by the client and passed back when the timer expires */
49  void *data;
50} igmp_timer_t;
51
52enum
53{
54  IGMP_PROCESS_EVENT_UPDATE_TIMER = 1,
55} igmp_process_event_t;
56
57/**
58 * pool of timers
59 */
60static igmp_timer_t *timer_pool;
61
62/**
63 * Vector of pending timers
64 */
65static u32 *pending_timers;
66
67u32
68igmp_timer_type_get (igmp_timer_type_t t)
69{
70  ASSERT (t < IGMP_N_TIMERS);
71  return (igmp_default_timer_values[t]);
72}
73
74void
75igmp_timer_type_set (igmp_timer_type_t t, u32 v)
76{
77  ASSERT (t < IGMP_N_TIMERS);
78  igmp_default_timer_values[t] = v;
79}
80
81
82static int
83igmp_timer_compare (const void *_v1, const void *_v2)
84{
85  const u32 *i1 = _v1, *i2 = _v2;
86  const igmp_timer_t *t1, *t2;
87  f64 dt;
88
89  t1 = pool_elt_at_index (timer_pool, *i1);
90  t2 = pool_elt_at_index (timer_pool, *i2);
91
92  dt = t2->exp_time - t1->exp_time;
93
94  return (dt < 0 ? -1 : (dt > 0 ? +1 : 0));
95}
96
97/** \brief igmp get next timer
98
99    Get next timer.
100*/
101u32
102igmp_get_next_timer (void)
103{
104  if (0 == vec_len (pending_timers))
105    return (IGMP_TIMER_ID_INVALID);
106
107  return (pending_timers[vec_len (pending_timers) - 1]);
108}
109
110void *
111igmp_timer_get_data (igmp_timer_id_t tid)
112{
113  igmp_timer_t *timer;
114
115  timer = pool_elt_at_index (timer_pool, tid);
116
117  return (timer->data);
118}
119
120void
121igmp_timer_set_data (igmp_timer_id_t tid, void *data)
122{
123  igmp_timer_t *timer;
124
125  timer = pool_elt_at_index (timer_pool, tid);
126
127  timer->data = data;
128}
129
130int
131igmp_timer_is_running (igmp_timer_id_t tid)
132{
133  return (IGMP_TIMER_ID_INVALID == tid);
134}
135
136/** \brief igmp timer process
137    @param vm - vlib main
138    @param rt - vlib runtime node
139    @param f - vlib frame
140
141    Handle igmp timers.
142*/
143static uword
144igmp_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
145		    vlib_frame_t * f)
146{
147  uword *event_data = 0, event_type;
148  igmp_timer_id_t tid;
149  igmp_timer_t *timer;
150
151  tid = IGMP_TIMER_ID_INVALID;
152
153  while (1)
154    {
155      /* suspend util timer expires */
156      if (IGMP_TIMER_ID_INVALID != tid)
157	{
158	  timer = pool_elt_at_index (timer_pool, tid);
159	  vlib_process_wait_for_event_or_clock
160	    (vm, timer->exp_time - vlib_time_now (vm));
161	}
162      else
163	vlib_process_wait_for_event (vm);
164
165      event_type = vlib_process_get_events (vm, &event_data);
166      vec_reset_length (event_data);
167
168      if (event_type == IGMP_PROCESS_EVENT_UPDATE_TIMER)
169	goto next_timer;
170
171      /* timer expired */
172      ASSERT (tid != IGMP_TIMER_ID_INVALID);
173
174      timer = pool_elt_at_index (timer_pool, tid);
175      ASSERT (timer->func != NULL);
176      timer->func (timer->obj, timer->data);
177
178    next_timer:
179      tid = igmp_get_next_timer ();
180    }
181  return 0;
182}
183
184/* *INDENT-OFF* */
185VLIB_REGISTER_NODE (igmp_timer_process_node) =
186{
187  .function = igmp_timer_process,
188  .type = VLIB_NODE_TYPE_PROCESS,
189  .name = "igmp-timer-process",
190  .n_next_nodes = 0,
191};
192/* *INDENT-ON* */
193
194igmp_timer_id_t
195igmp_timer_schedule (f64 when, u32 obj, igmp_timer_function_t fn, void *data)
196{
197  igmp_timer_t *timer;
198  vlib_main_t *vm;
199
200  ASSERT (fn);
201
202  vm = vlib_get_main ();
203  pool_get (timer_pool, timer);
204
205  timer->exp_time = vlib_time_now (vm) + when;
206  timer->obj = obj;
207  timer->func = fn;
208  timer->data = data;
209
210  vec_add1 (pending_timers, timer - timer_pool);
211
212  vec_sort_with_function (pending_timers, igmp_timer_compare);
213
214  vlib_process_signal_event (vm, igmp_timer_process_node.index,
215			     IGMP_PROCESS_EVENT_UPDATE_TIMER, 0);
216
217  return (timer - timer_pool);
218}
219
220void
221igmp_timer_retire (igmp_timer_id_t * tid)
222{
223  if (IGMP_TIMER_ID_INVALID == *tid)
224    return;
225  vec_del1 (pending_timers, vec_search (pending_timers, *tid));
226  pool_put_index (timer_pool, *tid);
227  *tid = IGMP_TIMER_ID_INVALID;
228
229  vlib_process_signal_event (vlib_get_main (),
230			     igmp_timer_process_node.index,
231			     IGMP_PROCESS_EVENT_UPDATE_TIMER, 0);
232}
233
234u8 *
235format_igmp_timer_id (u8 * s, va_list * args)
236{
237  igmp_timer_id_t tid = va_arg (*args, igmp_timer_id_t);
238  igmp_timer_t *timer;
239
240  if (IGMP_TIMER_ID_INVALID == tid)
241    {
242      s = format (s, "not-running");
243    }
244  else
245    {
246      timer = pool_elt_at_index (timer_pool, tid);
247
248      s =
249	format (s, "[expires-in:%f]",
250		timer->exp_time - vlib_time_now (vlib_get_main ()));
251    }
252
253  return (s);
254}
255
256/*
257 * fd.io coding-style-patch-verification: ON
258 *
259 * Local Variables:
260 * eval: (c-set-style "gnu")
261 * End:
262 */
263