Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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 void *timerfd_timer_open(void);
45 static void timerfd_timer_close(void *data);
46 static int timerfd_timer_set_rate(void *data, unsigned int rate);
47 static int timerfd_timer_ack(void *data, unsigned int quantity);
48 static int timerfd_timer_enable_continuous(void *data);
49 static int timerfd_timer_disable_continuous(void *data);
50 static enum ast_timer_event timerfd_timer_get_event(void *data);
51 static unsigned int timerfd_timer_get_max_rate(void *data);
52 static int timerfd_timer_fd(void *data);
53 
55  .name = "timerfd",
56  .priority = 200,
57  .timer_open = timerfd_timer_open,
58  .timer_close = timerfd_timer_close,
59  .timer_set_rate = timerfd_timer_set_rate,
60  .timer_ack = timerfd_timer_ack,
61  .timer_enable_continuous = timerfd_timer_enable_continuous,
62  .timer_disable_continuous = timerfd_timer_disable_continuous,
63  .timer_get_event = timerfd_timer_get_event,
64  .timer_get_max_rate = timerfd_timer_get_max_rate,
65  .timer_fd = timerfd_timer_fd,
66 };
67 
68 #define TIMERFD_MAX_RATE 1000
69 
70 struct timerfd_timer {
71  int fd;
72  struct itimerspec saved_timer;
73  unsigned int is_continuous:1;
74 };
75 
76 static void timer_destroy(void *obj)
77 {
78  struct timerfd_timer *timer = obj;
79  if (timer->fd > -1) {
80  close(timer->fd);
81  }
82 }
83 
84 static void *timerfd_timer_open(void)
85 {
86  struct timerfd_timer *timer;
87 
88  if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
89  ast_log(LOG_ERROR, "Could not allocate memory for timerfd_timer structure\n");
90  return NULL;
91  }
92  if ((timer->fd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
93  ast_log(LOG_ERROR, "Failed to create timerfd timer: %s\n", strerror(errno));
94  ao2_ref(timer, -1);
95  return NULL;
96  }
97 
98  return timer;
99 }
100 
101 static void timerfd_timer_close(void *data)
102 {
103  ao2_ref(data, -1);
104 }
105 
106 static int timerfd_timer_set_rate(void *data, unsigned int rate)
107 {
108  struct timerfd_timer *timer = data;
109  int res = 0;
110 
111  ao2_lock(timer);
112 
113  timer->saved_timer.it_value.tv_sec = 0;
114  timer->saved_timer.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
115  timer->saved_timer.it_interval.tv_sec = timer->saved_timer.it_value.tv_sec;
116  timer->saved_timer.it_interval.tv_nsec = timer->saved_timer.it_value.tv_nsec;
117 
118  if (!timer->is_continuous) {
119  res = timerfd_settime(timer->fd, 0, &timer->saved_timer, NULL);
120  }
121 
122  ao2_unlock(timer);
123 
124  return res;
125 }
126 
127 static int timerfd_timer_ack(void *data, unsigned int quantity)
128 {
129  struct timerfd_timer *timer = data;
130  uint64_t expirations;
131  int read_result = 0;
132  int res = 0;
133 
134  ao2_lock(timer);
135 
136  do {
137  struct itimerspec timer_status;
138 
139  if (timerfd_gettime(timer->fd, &timer_status)) {
140  ast_log(LOG_ERROR, "Call to timerfd_gettime() using handle %d error: %s\n", timer->fd, strerror(errno));
141  expirations = 0;
142  res = -1;
143  break;
144  }
145 
146  if (timer_status.it_value.tv_sec == 0 && timer_status.it_value.tv_nsec == 0) {
147  ast_debug(1, "Avoiding read on disarmed timerfd %d\n", timer->fd);
148  expirations = 0;
149  break;
150  }
151 
152  read_result = read(timer->fd, &expirations, sizeof(expirations));
153  if (read_result == -1) {
154  if (errno == EINTR || errno == EAGAIN) {
155  continue;
156  } else {
157  ast_log(LOG_ERROR, "Read error: %s\n", strerror(errno));
158  res = -1;
159  break;
160  }
161  }
162  } while (read_result != sizeof(expirations));
163 
164  ao2_unlock(timer);
165 
166  if (expirations != quantity) {
167  ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, (unsigned long long) expirations);
168  }
169 
170  return res;
171 }
172 
173 static int timerfd_timer_enable_continuous(void *data)
174 {
175  struct timerfd_timer *timer = data;
176  int res;
177  static const struct itimerspec continuous_timer = {
178  .it_value.tv_nsec = 1L,
179  };
180 
181  ao2_lock(timer);
182 
183  if (timer->is_continuous) {
184  /*It's already in continous mode, no need to do
185  * anything further
186  */
187  ao2_unlock(timer);
188  return 0;
189  }
190 
191  res = timerfd_settime(timer->fd, 0, &continuous_timer, &timer->saved_timer);
192  timer->is_continuous = 1;
193  ao2_unlock(timer);
194 
195  return res;
196 }
197 
198 static int timerfd_timer_disable_continuous(void *data)
199 {
200  struct timerfd_timer *timer = data;
201  int res;
202 
203  ao2_lock(timer);
204 
205  if (!timer->is_continuous) {
206  /* No reason to do anything if we're not
207  * in continuous mode
208  */
209  ao2_unlock(timer);
210  return 0;
211  }
212 
213  res = timerfd_settime(timer->fd, 0, &timer->saved_timer, NULL);
214  timer->is_continuous = 0;
215  memset(&timer->saved_timer, 0, sizeof(timer->saved_timer));
216  ao2_unlock(timer);
217 
218  return res;
219 }
220 
222 {
223  struct timerfd_timer *timer = data;
224  enum ast_timer_event res;
225 
226  ao2_lock(timer);
227 
228  if (timer->is_continuous) {
230  } else {
232  }
233 
234  ao2_unlock(timer);
235 
236  return res;
237 }
238 
239 static unsigned int timerfd_timer_get_max_rate(void *data)
240 {
241  return TIMERFD_MAX_RATE;
242 }
243 
244 static int timerfd_timer_fd(void *data)
245 {
246  struct timerfd_timer *timer = data;
247 
248  return timer->fd;
249 }
250 
251 static int load_module(void)
252 {
253  int fd;
254 
255  /* Make sure we support the necessary clock type */
256  if ((fd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
257  ast_log(LOG_ERROR, "timerfd_create() not supported by the kernel. Not loading.\n");
259  }
260 
261  close(fd);
262 
263  if (!(timing_funcs_handle = ast_register_timing_interface(&timerfd_timing))) {
265  }
266 
268 }
269 
270 static int unload_module(void)
271 {
273 }
274 
275 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Timerfd Timing Interface",
276  .support_level = AST_MODULE_SUPPORT_CORE,
277  .load = load_module,
278  .unload = unload_module,
279  .load_pri = AST_MODPRI_TIMING,
280 );
const char * name
Definition: timing.h:70
static enum ast_timer_event timerfd_timer_get_event(void *data)
Timing module interface.
Definition: timing.h:69
#define TIMERFD_MAX_RATE
Asterisk main include file. File version handling, generic pbx functions.
Time-related functions and macros.
int ast_unregister_timing_interface(void *handle)
Unregister a previously registered timing interface.
Definition: timing.c:104
ast_timer_event
Definition: timing.h:57
#define ao2_unlock(a)
Definition: astobj2.h:730
static int load_module(void)
static void * timing_funcs_handle
#define NULL
Definition: resample.c:96
static int timerfd_timer_ack(void *data, unsigned int quantity)
static struct ast_timing_interface timerfd_timing
Utility functions.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static int timerfd_timer_disable_continuous(void *data)
unsigned int is_continuous
struct itimerspec saved_timer
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_lock(a)
Definition: astobj2.h:718
static void timerfd_timer_close(void *data)
static int timerfd_timer_enable_continuous(void *data)
#define LOG_ERROR
Definition: logger.h:285
static void * timerfd_timer_open(void)
int errno
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static unsigned int timerfd_timer_get_max_rate(void *data)
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int unload_module(void)
static int timerfd_timer_set_rate(void *data, unsigned int rate)
Support for logging to various files, console and syslog Configuration in file logger.conf.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
static void timer_destroy(void *obj)
static int timerfd_timer_fd(void *data)
#define ast_register_timing_interface(i)
Register a set of timing functions.
Definition: timing.h:95
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
static struct ast_timer * timer
Definition: chan_iax2.c:360
Asterisk module definitions.
Timing source management.