Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


res_timing_timerfd.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*!
20  * \file
21  * \author Mark Michelson <mmichelson@digium.com>
22  *
23  * \brief timerfd timing interface
24  */
25 
26 /*** MODULEINFO
27  <depend>timerfd</depend>
28  <support_level>core</support_level>
29  ***/
30 
31 #include "asterisk.h"
32 
33 #include <sys/timerfd.h>
34 
35 #include "asterisk/module.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/timing.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/utils.h"
40 #include "asterisk/time.h"
41 
42 static void *timing_funcs_handle;
43 
44 static int timerfd_timer_open(void);
45 static void timerfd_timer_close(int handle);
46 static int timerfd_timer_set_rate(int handle, unsigned int rate);
47 static int timerfd_timer_ack(int handle, unsigned int quantity);
48 static int timerfd_timer_enable_continuous(int handle);
49 static int timerfd_timer_disable_continuous(int handle);
50 static enum ast_timer_event timerfd_timer_get_event(int handle);
51 static unsigned int timerfd_timer_get_max_rate(int handle);
52 
54  .name = "timerfd",
55  .priority = 200,
56  .timer_open = timerfd_timer_open,
57  .timer_close = timerfd_timer_close,
58  .timer_set_rate = timerfd_timer_set_rate,
59  .timer_ack = timerfd_timer_ack,
60  .timer_enable_continuous = timerfd_timer_enable_continuous,
61  .timer_disable_continuous = timerfd_timer_disable_continuous,
62  .timer_get_event = timerfd_timer_get_event,
63  .timer_get_max_rate = timerfd_timer_get_max_rate,
64 };
65 
67 
68 #define TIMERFD_TIMER_BUCKETS 563
69 #define TIMERFD_MAX_RATE 1000
70 
71 struct timerfd_timer {
72  int handle;
73  struct itimerspec saved_timer;
74  unsigned int is_continuous:1;
75 };
76 
77 static int timerfd_timer_hash(const void *obj, const int flags)
78 {
79  const struct timerfd_timer *timer = obj;
80 
81  return timer->handle;
82 }
83 
84 static int timerfd_timer_cmp(void *obj, void *args, int flags)
85 {
86  struct timerfd_timer *timer1 = obj, *timer2 = args;
87  return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
88 }
89 
90 static void timer_destroy(void *obj)
91 {
92  struct timerfd_timer *timer = obj;
93  close(timer->handle);
94  timer->handle = -1;
95 }
96 
97 static int timerfd_timer_open(void)
98 {
99  struct timerfd_timer *timer;
100  int handle;
101 
102  if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
103  ast_log(LOG_ERROR, "Could not allocate memory for timerfd_timer structure\n");
104  return -1;
105  }
106  if ((handle = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
107  ast_log(LOG_ERROR, "Failed to create timerfd timer: %s\n", strerror(errno));
108  ao2_ref(timer, -1);
109  return -1;
110  }
111 
112  timer->handle = handle;
113  ao2_link(timerfd_timers, timer);
114  /* Get rid of the reference from the allocation */
115  ao2_ref(timer, -1);
116  return handle;
117 }
118 
119 static void timerfd_timer_close(int handle)
120 {
121  struct timerfd_timer *our_timer, find_helper = {
122  .handle = handle,
123  };
124 
125  if (handle == -1) {
126  ast_log(LOG_ERROR, "Attempting to close timerfd handle -1");
127  return;
128  }
129 
130  if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
131  ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
132  return;
133  }
134 
135  ao2_unlink(timerfd_timers, our_timer);
136  ao2_ref(our_timer, -1);
137 }
138 
139 static int timerfd_timer_set_rate(int handle, unsigned int rate)
140 {
141  struct timerfd_timer *our_timer, find_helper = {
142  .handle = handle,
143  };
144  int res = 0;
145 
146  if (handle == -1) {
147  ast_log(LOG_ERROR, "Attempting to set rate on timerfd handle -1");
148  return -1;
149  }
150 
151  if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
152  ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
153  return -1;
154  }
155  ao2_lock(our_timer);
156 
157  our_timer->saved_timer.it_value.tv_sec = 0;
158  our_timer->saved_timer.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
159  our_timer->saved_timer.it_interval.tv_sec = our_timer->saved_timer.it_value.tv_sec;
160  our_timer->saved_timer.it_interval.tv_nsec = our_timer->saved_timer.it_value.tv_nsec;
161 
162  if (!our_timer->is_continuous) {
163  res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
164  }
165 
166  ao2_unlock(our_timer);
167  ao2_ref(our_timer, -1);
168 
169  return res;
170 }
171 
172 static int timerfd_timer_ack(int handle, unsigned int quantity)
173 {
174  uint64_t expirations;
175  int read_result = 0;
176  int res = 0;
177  struct timerfd_timer *our_timer, find_helper = {
178  .handle = handle,
179  };
180 
181  if (handle == -1) {
182  ast_log(LOG_ERROR, "Attempting to ack timerfd handle -1");
183  return -1;
184  }
185 
186  if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
187  ast_log(LOG_ERROR, "Couldn't find a timer with handle %d\n", handle);
188  return -1;
189  }
190 
191  ao2_lock(our_timer);
192 
193  do {
194  struct itimerspec timer_status;
195 
196  if (timerfd_gettime(handle, &timer_status)) {
197  ast_log(LOG_ERROR, "Call to timerfd_gettime() using handle %d error: %s\n", handle, strerror(errno));
198  expirations = 0;
199  res = -1;
200  break;
201  }
202 
203  if (timer_status.it_value.tv_sec == 0 && timer_status.it_value.tv_nsec == 0) {
204  ast_debug(1, "Avoiding read on disarmed timerfd %d\n", handle);
205  expirations = 0;
206  break;
207  }
208 
209  read_result = read(handle, &expirations, sizeof(expirations));
210  if (read_result == -1) {
211  if (errno == EINTR || errno == EAGAIN) {
212  continue;
213  } else {
214  ast_log(LOG_ERROR, "Read error: %s\n", strerror(errno));
215  res = -1;
216  break;
217  }
218  }
219  } while (read_result != sizeof(expirations));
220 
221  ao2_unlock(our_timer);
222  ao2_ref(our_timer, -1);
223 
224  if (expirations != quantity) {
225  ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, (unsigned long long) expirations);
226  }
227  return res;
228 }
229 
230 static int timerfd_timer_enable_continuous(int handle)
231 {
232  int res;
233  struct itimerspec continuous_timer = {
234  .it_value.tv_nsec = 1L,
235  };
236  struct timerfd_timer *our_timer, find_helper = {
237  .handle = handle,
238  };
239 
240  if (handle == -1) {
241  ast_log(LOG_ERROR, "Attempting to enable timerfd handle -1");
242  return -1;
243  }
244 
245  if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
246  ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
247  return -1;
248  }
249  ao2_lock(our_timer);
250 
251  if (our_timer->is_continuous) {
252  /*It's already in continous mode, no need to do
253  * anything further
254  */
255  ao2_unlock(our_timer);
256  ao2_ref(our_timer, -1);
257  return 0;
258  }
259 
260  res = timerfd_settime(handle, 0, &continuous_timer, &our_timer->saved_timer);
261  our_timer->is_continuous = 1;
262  ao2_unlock(our_timer);
263  ao2_ref(our_timer, -1);
264  return res;
265 }
266 
268 {
269  int res;
270  struct timerfd_timer *our_timer, find_helper = {
271  .handle = handle,
272  };
273 
274  if (handle == -1) {
275  ast_log(LOG_ERROR, "Attempting to disable timerfd handle -1");
276  return -1;
277  }
278 
279  if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
280  ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
281  return -1;
282  }
283  ao2_lock(our_timer);
284 
285  if (!our_timer->is_continuous) {
286  /* No reason to do anything if we're not
287  * in continuous mode
288  */
289  ao2_unlock(our_timer);
290  ao2_ref(our_timer, -1);
291  return 0;
292  }
293 
294  res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
295  our_timer->is_continuous = 0;
296  memset(&our_timer->saved_timer, 0, sizeof(our_timer->saved_timer));
297  ao2_unlock(our_timer);
298  ao2_ref(our_timer, -1);
299  return res;
300 }
301 
303 {
304  enum ast_timer_event res;
305  struct timerfd_timer *our_timer, find_helper = {
306  .handle = handle,
307  };
308 
309  if (handle == -1) {
310  ast_log(LOG_ERROR, "Attempting to get event from timerfd handle -1");
311  return -1;
312  }
313 
314  if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
315  ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
316  return -1;
317  }
318  ao2_lock(our_timer);
319 
320  if (our_timer->is_continuous) {
322  } else {
324  }
325 
326  ao2_unlock(our_timer);
327  ao2_ref(our_timer, -1);
328  return res;
329 }
330 
331 static unsigned int timerfd_timer_get_max_rate(int handle)
332 {
333  return TIMERFD_MAX_RATE;
334 }
335 
336 static int load_module(void)
337 {
338  int fd;
339 
340  /* Make sure we support the necessary clock type */
341  if ((fd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
342  ast_log(LOG_ERROR, "timerfd_create() not supported by the kernel. Not loading.\n");
344  }
345 
346  close(fd);
347 
350  }
351 
352  if (!(timing_funcs_handle = ast_register_timing_interface(&timerfd_timing))) {
353  ao2_ref(timerfd_timers, -1);
355  }
356 
358 }
359 
360 static int unload_module(void)
361 {
362  int res;
363 
365  ao2_ref(timerfd_timers, -1);
366  timerfd_timers = NULL;
367  }
368 
369  return res;
370 }
371 
372 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Timerfd Timing Interface",
373  .load = load_module,
374  .unload = unload_module,
375  .load_pri = AST_MODPRI_TIMING,
376  );
const char * name
Definition: timing.h:70
Timing module interface.
Definition: timing.h:69
#define TIMERFD_MAX_RATE
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_link(arg1, arg2)
Definition: astobj2.h:785
static int timerfd_timer_disable_continuous(int handle)
static void timerfd_timer_close(int handle)
Time-related functions and macros.
int ast_unregister_timing_interface(void *handle)
Unregister a previously registered timing interface.
Definition: timing.c:105
ast_timer_event
Definition: timing.h:57
static int timerfd_timer_hash(const void *obj, const int flags)
#define ao2_unlock(a)
Definition: astobj2.h:497
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
static int load_module(void)
static void * timing_funcs_handle
static struct ast_timing_interface timerfd_timing
Utility functions.
static int timerfd_timer_enable_continuous(int handle)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static struct ao2_container * timerfd_timers
unsigned int is_continuous
struct itimerspec saved_timer
#define ao2_ref(o, delta)
Definition: astobj2.h:472
#define ao2_lock(a)
Definition: astobj2.h:488
static int timerfd_timer_open(void)
#define LOG_ERROR
Definition: logger.h:155
static struct @350 args
static unsigned int timerfd_timer_get_max_rate(int handle)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:430
#define ao2_find(arg1, arg2, arg3)
Definition: astobj2.h:964
int errno
static int unload_module(void)
Support for logging to various files, console and syslog Configuration in file logger.conf.
static int timerfd_timer_cmp(void *obj, void *args, int flags)
#define TIMERFD_TIMER_BUCKETS
#define ao2_container_alloc(arg1, arg2, arg3)
Definition: astobj2.h:734
static void timer_destroy(void *obj)
#define ast_register_timing_interface(i)
Register a set of timing functions.
Definition: timing.h:94
static enum ast_timer_event timerfd_timer_get_event(int handle)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
static struct ast_timer * timer
Definition: chan_iax2.c:313
static int timerfd_timer_ack(int handle, unsigned int quantity)
Asterisk module definitions.
#define ao2_unlink(arg1, arg2)
Definition: astobj2.h:817
Timing source management.
static int timerfd_timer_set_rate(int handle, unsigned int rate)