fib: Source Address Selection
[vpp.git] / src / plugins / igmp / igmp_timer.c
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
25 static 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  */
37 typedef 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
52 enum
53 {
54   IGMP_PROCESS_EVENT_UPDATE_TIMER = 1,
55 } igmp_process_event_t;
56
57 /**
58  * pool of timers
59  */
60 static igmp_timer_t *timer_pool;
61
62 /**
63  * Vector of pending timers
64  */
65 static u32 *pending_timers;
66
67 u32
68 igmp_timer_type_get (igmp_timer_type_t t)
69 {
70   ASSERT (t < IGMP_N_TIMERS);
71   return (igmp_default_timer_values[t]);
72 }
73
74 void
75 igmp_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
82 static int
83 igmp_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 */
101 u32
102 igmp_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
110 void *
111 igmp_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
120 void
121 igmp_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
130 int
131 igmp_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 */
143 static uword
144 igmp_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* */
185 VLIB_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
194 igmp_timer_id_t
195 igmp_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
220 void
221 igmp_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
234 u8 *
235 format_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  */