aboutsummaryrefslogtreecommitdiff
path: root/nptl/tst-cancel17.c
blob: 2a8c951afe9b2620442f5ed16062479af4bf08ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/* Copyright (C) 2003, 2005 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#include <aio.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


static pthread_barrier_t b;


/* Cleanup handling test.  */
static int cl_called;

static void
cl (void *arg)
{
  ++cl_called;
}


static void *
tf (void *arg)
{
  int r = pthread_barrier_wait (&b);
  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
    {
      puts ("tf: barrier_wait failed");
      exit (1);
    }

  pthread_cleanup_push (cl, NULL);

  const struct aiocb *l[1] = { arg };

  TEMP_FAILURE_RETRY (aio_suspend (l, 1, NULL));

  pthread_cleanup_pop (0);

  puts ("tf: aio_suspend returned");

  exit (1);
}


static void *
tf2 (void *arg)
{
  int r = pthread_barrier_wait (&b);
  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
    {
      puts ("tf2: barrier_wait failed");
      exit (1);
    }

  pthread_cleanup_push (cl, NULL);

  const struct aiocb *l[1] = { arg };
  struct timespec ts = { .tv_sec = 1000, .tv_nsec = 0 };

  TEMP_FAILURE_RETRY (aio_suspend (l, 1, &ts));

  pthread_cleanup_pop (0);

  puts ("tf2: aio_suspend returned");

  exit (1);
}


static int
do_test (void)
{
  int fds[2];
  if (pipe (fds) != 0)
    {
      puts ("pipe failed");
      return 1;
    }

  struct aiocb a, a2, *ap;
  char mem[1];
  memset (&a, '\0', sizeof (a));
  a.aio_fildes = fds[0];
  a.aio_buf = mem;
  a.aio_nbytes = sizeof (mem);
  if (aio_read (&a) != 0)
    {
      puts ("aio_read failed");
      return 1;
    }

  if (pthread_barrier_init (&b, NULL, 2) != 0)
    {
      puts ("barrier_init failed");
      return 1;
    }

  pthread_t th;
  if (pthread_create (&th, NULL, tf, &a) != 0)
    {
      puts ("1st create failed");
      return 1;
    }

  int r = pthread_barrier_wait (&b);
  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
    {
      puts ("barrier_wait failed");
      exit (1);
    }

  struct timespec  ts = { .tv_sec = 0, .tv_nsec = 100000000 };
  while (nanosleep (&ts, &ts) != 0)
    continue;

  puts ("going to cancel tf in-time");
  if (pthread_cancel (th) != 0)
    {
      puts ("1st cancel failed");
      return 1;
    }

  void *status;
  if (pthread_join (th, &status) != 0)
    {
      puts ("1st join failed");
      return 1;
    }
  if (status != PTHREAD_CANCELED)
    {
      puts ("1st thread not canceled");
      return 1;
    }

  if (cl_called == 0)
    {
      puts ("tf cleanup handler not called");
      return 1;
    }
  if (cl_called > 1)
    {
      puts ("tf cleanup handler called more than once");
      return 1;
    }

  cl_called = 0;

  if (pthread_create (&th, NULL, tf2, &a) != 0)
    {
      puts ("2nd create failed");
      return 1;
    }

  r = pthread_barrier_wait (&b);
  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
    {
      puts ("2nd barrier_wait failed");
      exit (1);
    }

  ts.tv_sec = 0;
  ts.tv_nsec = 100000000;
  while (nanosleep (&ts, &ts) != 0)
    continue;

  puts ("going to cancel tf2 in-time");
  if (pthread_cancel (th) != 0)
    {
      puts ("2nd cancel failed");
      return 1;
    }

  if (pthread_join (th, &status) != 0)
    {
      puts ("2nd join failed");
      return 1;
    }
  if (status != PTHREAD_CANCELED)
    {
      puts ("2nd thread not canceled");
      return 1;
    }

  if (cl_called == 0)
    {
      puts ("tf2 cleanup handler not called");
      return 1;
    }
  if (cl_called > 1)
    {
      puts ("tf2 cleanup handler called more than once");
      return 1;
    }

  puts ("in-time cancellation succeeded");

  ap = &a;
  if (aio_cancel (fds[0], &a) != AIO_CANCELED)
    {
      puts ("aio_cancel failed");
      /* If aio_cancel failed, we cannot reuse aiocb a.  */
      ap = &a2;
    }


  cl_called = 0;

  size_t len2 = fpathconf (fds[1], _PC_PIPE_BUF);
  size_t page_size = sysconf (_SC_PAGESIZE);
  len2 = 20 * (len2 < page_size ? page_size : len2) + sizeof (mem) + 1;
  char *mem2 = malloc (len2);
  if (mem2 == NULL)
    {
      puts ("could not allocate memory for pipe write");
      return 1;
    }

  memset (ap, '\0', sizeof (*ap));
  ap->aio_fildes = fds[1];
  ap->aio_buf = mem2;
  ap->aio_nbytes = len2;
  if (aio_write (ap) != 0)
    {
      puts ("aio_write failed");
      return 1;
    }

  if (pthread_create (&th, NULL, tf, ap) != 0)
    {
      puts ("3rd create failed");
      return 1;
    }

  puts ("going to cancel tf early");
  if (pthread_cancel (th) != 0)
    {
      puts ("3rd cancel failed");
      return 1;
    }

  r = pthread_barrier_wait (&b);
  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
    {
      puts ("3rd barrier_wait failed");
      exit (1);
    }

  if (pthread_join (th, &status) != 0)
    {
      puts ("3rd join failed");
      return 1;
    }
  if (status != PTHREAD_CANCELED)
    {
      puts ("3rd thread not canceled");
      return 1;
    }

  if (cl_called == 0)
    {
      puts ("tf cleanup handler not called");
      return 1;
    }
  if (cl_called > 1)
    {
      puts ("tf cleanup handler called more than once");
      return 1;
    }

  cl_called = 0;

  if (pthread_create (&th, NULL, tf2, ap) != 0)
    {
      puts ("4th create failed");
      return 1;
    }

  puts ("going to cancel tf2 early");
  if (pthread_cancel (th) != 0)
    {
      puts ("4th cancel failed");
      return 1;
    }

  r = pthread_barrier_wait (&b);
  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
    {
      puts ("4th barrier_wait failed");
      exit (1);
    }

  if (pthread_join (th, &status) != 0)
    {
      puts ("4th join failed");
      return 1;
    }
  if (status != PTHREAD_CANCELED)
    {
      puts ("4th thread not canceled");
      return 1;
    }

  if (cl_called == 0)
    {
      puts ("tf2 cleanup handler not called");
      return 1;
    }
  if (cl_called > 1)
    {
      puts ("tf2 cleanup handler called more than once");
      return 1;
    }

  puts ("early cancellation succeeded");

  return 0;
}

#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"
s="hl kwa">return; t->rearm(t); } /* TODO: MIN_TIMER_REARM_US should be optimized */ #define MIN_TIMER_REARM_US 250 #ifdef _WIN32 struct qemu_alarm_win32 { MMRESULT timerId; unsigned int period; } alarm_win32_data = {0, 0}; static int win32_start_timer(struct qemu_alarm_timer *t); static void win32_stop_timer(struct qemu_alarm_timer *t); static void win32_rearm_timer(struct qemu_alarm_timer *t); #else static int unix_start_timer(struct qemu_alarm_timer *t); static void unix_stop_timer(struct qemu_alarm_timer *t); #ifdef __linux__ static int dynticks_start_timer(struct qemu_alarm_timer *t); static void dynticks_stop_timer(struct qemu_alarm_timer *t); static void dynticks_rearm_timer(struct qemu_alarm_timer *t); static int hpet_start_timer(struct qemu_alarm_timer *t); static void hpet_stop_timer(struct qemu_alarm_timer *t); static int rtc_start_timer(struct qemu_alarm_timer *t); static void rtc_stop_timer(struct qemu_alarm_timer *t); #endif /* __linux__ */ #endif /* _WIN32 */ /* Correlation between real and virtual time is always going to be fairly approximate, so ignore small variation. When the guest is idle real and virtual time will be aligned in the IO wait loop. */ #define ICOUNT_WOBBLE (get_ticks_per_sec() / 10) static void icount_adjust(void) { int64_t cur_time; int64_t cur_icount; int64_t delta; static int64_t last_delta; /* If the VM is not running, then do nothing. */ if (!vm_running) return; cur_time = cpu_get_clock(); cur_icount = qemu_get_clock(vm_clock); delta = cur_icount - cur_time; /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ if (delta > 0 && last_delta + ICOUNT_WOBBLE < delta * 2 && icount_time_shift > 0) { /* The guest is getting too far ahead. Slow time down. */ icount_time_shift--; } if (delta < 0 && last_delta - ICOUNT_WOBBLE > delta * 2 && icount_time_shift < MAX_ICOUNT_SHIFT) { /* The guest is getting too far behind. Speed time up. */ icount_time_shift++; } last_delta = delta; qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift); } static void icount_adjust_rt(void * opaque) { qemu_mod_timer(icount_rt_timer, qemu_get_clock(rt_clock) + 1000); icount_adjust(); } static void icount_adjust_vm(void * opaque) { qemu_mod_timer(icount_vm_timer, qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); icount_adjust(); } int64_t qemu_icount_round(int64_t count) { return (count + (1 << icount_time_shift) - 1) >> icount_time_shift; } static struct qemu_alarm_timer alarm_timers[] = { #ifndef _WIN32 #ifdef __linux__ {"dynticks", dynticks_start_timer, dynticks_stop_timer, dynticks_rearm_timer, NULL}, /* HPET - if available - is preferred */ {"hpet", hpet_start_timer, hpet_stop_timer, NULL, NULL}, /* ...otherwise try RTC */ {"rtc", rtc_start_timer, rtc_stop_timer, NULL, NULL}, #endif {"unix", unix_start_timer, unix_stop_timer, NULL, NULL}, #else {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer, &alarm_win32_data}, {"win32", win32_start_timer, win32_stop_timer, NULL, &alarm_win32_data}, #endif {NULL, } }; static void show_available_alarms(void) { int i; printf("Available alarm timers, in order of precedence:\n"); for (i = 0; alarm_timers[i].name; i++) printf("%s\n", alarm_timers[i].name); } void configure_alarms(char const *opt) { int i; int cur = 0; int count = ARRAY_SIZE(alarm_timers) - 1; char *arg; char *name; struct qemu_alarm_timer tmp; if (!strcmp(opt, "?")) { show_available_alarms(); exit(0); } arg = qemu_strdup(opt); /* Reorder the array */ name = strtok(arg, ","); while (name) { for (i = 0; i < count && alarm_timers[i].name; i++) { if (!strcmp(alarm_timers[i].name, name)) break; } if (i == count) { fprintf(stderr, "Unknown clock %s\n", name); goto next; } if (i < cur) /* Ignore */ goto next; /* Swap */ tmp = alarm_timers[i]; alarm_timers[i] = alarm_timers[cur]; alarm_timers[cur] = tmp; cur++; next: name = strtok(NULL, ","); } qemu_free(arg); if (cur) { /* Disable remaining timers */ for (i = cur; i < count; i++) alarm_timers[i].name = NULL; } else { show_available_alarms(); exit(1); } } #define QEMU_NUM_CLOCKS 3 QEMUClock *rt_clock; QEMUClock *vm_clock; QEMUClock *host_clock; static QEMUTimer *active_timers[QEMU_NUM_CLOCKS]; static QEMUClock *qemu_new_clock(int type) { QEMUClock *clock; clock = qemu_mallocz(sizeof(QEMUClock)); clock->type = type; clock->enabled = 1; return clock; } void qemu_clock_enable(QEMUClock *clock, int enabled) { clock->enabled = enabled; } QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) { QEMUTimer *ts; ts = qemu_mallocz(sizeof(QEMUTimer)); ts->clock = clock; ts->cb = cb; ts->opaque = opaque; return ts; } void qemu_free_timer(QEMUTimer *ts) { qemu_free(ts); } /* stop a timer, but do not dealloc it */ void qemu_del_timer(QEMUTimer *ts) { QEMUTimer **pt, *t; /* NOTE: this code must be signal safe because qemu_timer_expired() can be called from a signal. */ pt = &active_timers[ts->clock->type]; for(;;) { t = *pt; if (!t) break; if (t == ts) { *pt = t->next; break; } pt = &t->next; } } /* modify the current timer so that it will be fired when current_time >= expire_time. The corresponding callback will be called. */ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) { QEMUTimer **pt, *t; qemu_del_timer(ts); /* add the timer in the sorted list */ /* NOTE: this code must be signal safe because qemu_timer_expired() can be called from a signal. */ pt = &active_timers[ts->clock->type]; for(;;) { t = *pt; if (!t) break; if (t->expire_time > expire_time) break; pt = &t->next; } ts->expire_time = expire_time; ts->next = *pt; *pt = ts; /* Rearm if necessary */ if (pt == &active_timers[ts->clock->type]) { if (!alarm_timer->pending) { qemu_rearm_alarm_timer(alarm_timer); } /* Interrupt execution to force deadline recalculation. */ if (use_icount) qemu_notify_event(); } } int qemu_timer_pending(QEMUTimer *ts) { QEMUTimer *t; for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) { if (t == ts) return 1; } return 0; } int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) { if (!timer_head) return 0; return (timer_head->expire_time <= current_time); } static void qemu_run_timers(QEMUClock *clock) { QEMUTimer **ptimer_head, *ts; int64_t current_time; if (!clock->enabled) return; current_time = qemu_get_clock (clock); ptimer_head = &active_timers[clock->type]; for(;;) { ts = *ptimer_head; if (!ts || ts->expire_time > current_time) break; /* remove timer from the list before calling the callback */ *ptimer_head = ts->next; ts->next = NULL; /* run the callback (the timer list can be modified) */ ts->cb(ts->opaque); } } int64_t qemu_get_clock(QEMUClock *clock) { switch(clock->type) { case QEMU_CLOCK_REALTIME: return get_clock() / 1000000; default: case QEMU_CLOCK_VIRTUAL: if (use_icount) { return cpu_get_icount(); } else { return cpu_get_clock(); } case QEMU_CLOCK_HOST: return get_clock_realtime(); } } int64_t qemu_get_clock_ns(QEMUClock *clock) { switch(clock->type) { case QEMU_CLOCK_REALTIME: return get_clock(); default: case QEMU_CLOCK_VIRTUAL: if (use_icount) { return cpu_get_icount(); } else { return cpu_get_clock(); } case QEMU_CLOCK_HOST: return get_clock_realtime(); } } void init_clocks(void) { init_get_clock(); rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME); vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL); host_clock = qemu_new_clock(QEMU_CLOCK_HOST); rtc_clock = host_clock; } /* save a timer */ void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) { uint64_t expire_time; if (qemu_timer_pending(ts)) { expire_time = ts->expire_time; } else { expire_time = -1; } qemu_put_be64(f, expire_time); } void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) { uint64_t expire_time; expire_time = qemu_get_be64(f); if (expire_time != -1) { qemu_mod_timer(ts, expire_time); } else { qemu_del_timer(ts); } } static const VMStateDescription vmstate_timers = { .name = "timer", .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { VMSTATE_INT64(cpu_ticks_offset, TimersState), VMSTATE_INT64(dummy, TimersState), VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), VMSTATE_END_OF_LIST() } }; void configure_icount(const char *option) { vmstate_register(NULL, 0, &vmstate_timers, &timers_state); if (!option) return; if (strcmp(option, "auto") != 0) { icount_time_shift = strtol(option, NULL, 0); use_icount = 1; return; } use_icount = 2; /* 125MIPS seems a reasonable initial guess at the guest speed. It will be corrected fairly quickly anyway. */ icount_time_shift = 3; /* Have both realtime and virtual time triggers for speed adjustment. The realtime trigger catches emulated time passing too slowly, the virtual time trigger catches emulated time passing too fast. Realtime triggers occur even when idle, so use them less frequently than VM triggers. */ icount_rt_timer = qemu_new_timer(rt_clock, icount_adjust_rt, NULL); qemu_mod_timer(icount_rt_timer, qemu_get_clock(rt_clock) + 1000); icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL); qemu_mod_timer(icount_vm_timer, qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); } void qemu_run_all_timers(void) { alarm_timer->pending = 0; /* rearm timer, if not periodic */ if (alarm_timer->expired) { alarm_timer->expired = 0; qemu_rearm_alarm_timer(alarm_timer); } /* vm time timers */ if (vm_running) { qemu_run_timers(vm_clock); } qemu_run_timers(rt_clock); qemu_run_timers(host_clock); } #ifdef _WIN32 static void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) #else static void host_alarm_handler(int host_signum) #endif { struct qemu_alarm_timer *t = alarm_timer; if (!t) return; #if 0 #define DISP_FREQ 1000 { static int64_t delta_min = INT64_MAX; static int64_t delta_max, delta_cum, last_clock, delta, ti; static int count; ti = qemu_get_clock(vm_clock); if (last_clock != 0) { delta = ti - last_clock; if (delta < delta_min) delta_min = delta; if (delta > delta_max) delta_max = delta; delta_cum += delta; if (++count == DISP_FREQ) { printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n", muldiv64(delta_min, 1000000, get_ticks_per_sec()), muldiv64(delta_max, 1000000, get_ticks_per_sec()), muldiv64(delta_cum, 1000000 / DISP_FREQ, get_ticks_per_sec()), (double)get_ticks_per_sec() / ((double)delta_cum / DISP_FREQ)); count = 0; delta_min = INT64_MAX; delta_max = 0; delta_cum = 0; } } last_clock = ti; } #endif if (alarm_has_dynticks(t) || (!use_icount && qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL], qemu_get_clock(vm_clock))) || qemu_timer_expired(active_timers[QEMU_CLOCK_REALTIME], qemu_get_clock(rt_clock)) || qemu_timer_expired(active_timers[QEMU_CLOCK_HOST], qemu_get_clock(host_clock))) { t->expired = alarm_has_dynticks(t); t->pending = 1; qemu_notify_event(); } } int64_t qemu_next_deadline(void) { /* To avoid problems with overflow limit this to 2^32. */ int64_t delta = INT32_MAX; if (active_timers[QEMU_CLOCK_VIRTUAL]) { delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - qemu_get_clock(vm_clock); } if (active_timers[QEMU_CLOCK_HOST]) { int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time - qemu_get_clock(host_clock); if (hdelta < delta) delta = hdelta; } if (delta < 0) delta = 0; return delta; } #ifndef _WIN32 #if defined(__linux__) #define RTC_FREQ 1024 static uint64_t qemu_next_deadline_dyntick(void) { int64_t delta; int64_t rtdelta; if (use_icount) delta = INT32_MAX; else delta = (qemu_next_deadline() + 999) / 1000; if (active_timers[QEMU_CLOCK_REALTIME]) { rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time - qemu_get_clock(rt_clock))*1000; if (rtdelta < delta) delta = rtdelta; } if (delta < MIN_TIMER_REARM_US) delta = MIN_TIMER_REARM_US; return delta; } static void enable_sigio_timer(int fd) { struct sigaction act; /* timer signal */ sigfillset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = host_alarm_handler; sigaction(SIGIO, &act, NULL); fcntl_setfl(fd, O_ASYNC); fcntl(fd, F_SETOWN, getpid()); } static int hpet_start_timer(struct qemu_alarm_timer *t) { struct hpet_info info; int r, fd; fd = qemu_open("/dev/hpet", O_RDONLY); if (fd < 0) return -1; /* Set frequency */ r = ioctl(fd, HPET_IRQFREQ, RTC_FREQ); if (r < 0) { fprintf(stderr, "Could not configure '/dev/hpet' to have a 1024Hz timer. This is not a fatal\n" "error, but for better emulation accuracy type:\n" "'echo 1024 > /proc/sys/dev/hpet/max-user-freq' as root.\n"); goto fail; } /* Check capabilities */ r = ioctl(fd, HPET_INFO, &info); if (r < 0) goto fail; /* Enable periodic mode */ r = ioctl(fd, HPET_EPI, 0); if (info.hi_flags && (r < 0)) goto fail; /* Enable interrupt */ r = ioctl(fd, HPET_IE_ON, 0); if (r < 0) goto fail; enable_sigio_timer(fd); t->priv = (void *)(long)fd; return 0; fail: close(fd); return -1; } static void hpet_stop_timer(struct qemu_alarm_timer *t) { int fd = (long)t->priv; close(fd); } static int rtc_start_timer(struct qemu_alarm_timer *t) { int rtc_fd; unsigned long current_rtc_freq = 0; TFR(rtc_fd = qemu_open("/dev/rtc", O_RDONLY)); if (rtc_fd < 0) return -1; ioctl(rtc_fd, RTC_IRQP_READ, &current_rtc_freq); if (current_rtc_freq != RTC_FREQ && ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) { fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n" "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n" "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n"); goto fail; } if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) { fail: close(rtc_fd); return -1; } enable_sigio_timer(rtc_fd); t->priv = (void *)(long)rtc_fd; return 0; } static void rtc_stop_timer(struct qemu_alarm_timer *t) { int rtc_fd = (long)t->priv; close(rtc_fd); } static int dynticks_start_timer(struct qemu_alarm_timer *t) { struct sigevent ev; timer_t host_timer; struct sigaction act; sigfillset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = host_alarm_handler; sigaction(SIGALRM, &act, NULL); /* * Initialize ev struct to 0 to avoid valgrind complaining * about uninitialized data in timer_create call */ memset(&ev, 0, sizeof(ev)); ev.sigev_value.sival_int = 0; ev.sigev_notify = SIGEV_SIGNAL; ev.sigev_signo = SIGALRM; if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { perror("timer_create"); /* disable dynticks */ fprintf(stderr, "Dynamic Ticks disabled\n"); return -1; } t->priv = (void *)(long)host_timer; return 0; } static void dynticks_stop_timer(struct qemu_alarm_timer *t) { timer_t host_timer = (timer_t)(long)t->priv; timer_delete(host_timer); } static void dynticks_rearm_timer(struct qemu_alarm_timer *t) { timer_t host_timer = (timer_t)(long)t->priv; struct itimerspec timeout; int64_t nearest_delta_us = INT64_MAX; int64_t current_us; assert(alarm_has_dynticks(t)); if (!active_timers[QEMU_CLOCK_REALTIME] && !active_timers[QEMU_CLOCK_VIRTUAL] && !active_timers[QEMU_CLOCK_HOST]) return; nearest_delta_us = qemu_next_deadline_dyntick(); /* check whether a timer is already running */ if (timer_gettime(host_timer, &timeout)) { perror("gettime"); fprintf(stderr, "Internal timer error: aborting\n"); exit(1); } current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000; if (current_us && current_us <= nearest_delta_us) return; timeout.it_interval.tv_sec = 0; timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */ timeout.it_value.tv_sec = nearest_delta_us / 1000000; timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000; if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) { perror("settime"); fprintf(stderr, "Internal timer error: aborting\n"); exit(1); } } #endif /* defined(__linux__) */ static int unix_start_timer(struct qemu_alarm_timer *t) { struct sigaction act; struct itimerval itv; int err; /* timer signal */ sigfillset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = host_alarm_handler; sigaction(SIGALRM, &act, NULL); itv.it_interval.tv_sec = 0; /* for i386 kernel 2.6 to get 1 ms */ itv.it_interval.tv_usec = 999; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 10 * 1000; err = setitimer(ITIMER_REAL, &itv, NULL); if (err) return -1; return 0; } static void unix_stop_timer(struct qemu_alarm_timer *t) { struct itimerval itv; memset(&itv, 0, sizeof(itv)); setitimer(ITIMER_REAL, &itv, NULL); } #endif /* !defined(_WIN32) */ #ifdef _WIN32 static int win32_start_timer(struct qemu_alarm_timer *t) { TIMECAPS tc; struct qemu_alarm_win32 *data = t->priv; UINT flags; memset(&tc, 0, sizeof(tc)); timeGetDevCaps(&tc, sizeof(tc)); data->period = tc.wPeriodMin; timeBeginPeriod(data->period); flags = TIME_CALLBACK_FUNCTION; if (alarm_has_dynticks(t)) flags |= TIME_ONESHOT; else flags |= TIME_PERIODIC; data->timerId = timeSetEvent(1, // interval (ms) data->period, // resolution host_alarm_handler, // function (DWORD)t, // parameter flags); if (!data->timerId) { fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", GetLastError()); timeEndPeriod(data->period); return -1; } return 0; } static void win32_stop_timer(struct qemu_alarm_timer *t) { struct qemu_alarm_win32 *data = t->priv; timeKillEvent(data->timerId); timeEndPeriod(data->period); } static void win32_rearm_timer(struct qemu_alarm_timer *t) { struct qemu_alarm_win32 *data = t->priv; assert(alarm_has_dynticks(t)); if (!active_timers[QEMU_CLOCK_REALTIME] && !active_timers[QEMU_CLOCK_VIRTUAL] && !active_timers[QEMU_CLOCK_HOST]) return; timeKillEvent(data->timerId); data->timerId = timeSetEvent(1, data->period, host_alarm_handler, (DWORD)t, TIME_ONESHOT | TIME_CALLBACK_FUNCTION); if (!data->timerId) { fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n", GetLastError()); timeEndPeriod(data->period); exit(1); } } #endif /* _WIN32 */ static void alarm_timer_on_change_state_rearm(void *opaque, int running, int reason) { if (running) qemu_rearm_alarm_timer((struct qemu_alarm_timer *) opaque); } int init_timer_alarm(void) { struct qemu_alarm_timer *t = NULL; int i, err = -1; for (i = 0; alarm_timers[i].name; i++) { t = &alarm_timers[i]; err = t->start(t); if (!err) break; } if (err) { err = -ENOENT; goto fail; } /* first event is at time 0 */ t->pending = 1; alarm_timer = t; qemu_add_vm_change_state_handler(alarm_timer_on_change_state_rearm, t); return 0; fail: return err; } void quit_timers(void) { struct qemu_alarm_timer *t = alarm_timer; alarm_timer = NULL; t->stop(t); } int qemu_calculate_timeout(void) { #ifndef CONFIG_IOTHREAD int timeout; if (!vm_running) timeout = 5000; else { /* XXX: use timeout computed from timers */ int64_t add; int64_t delta; /* Advance virtual time to the next event. */ delta = qemu_icount_delta(); if (delta > 0) { /* If virtual time is ahead of real time then just wait for IO. */ timeout = (delta + 999999) / 1000000; } else { /* Wait for either IO to occur or the next timer event. */ add = qemu_next_deadline(); /* We advance the timer before checking for IO. Limit the amount we advance so that early IO activity won't get the guest too far ahead. */ if (add > 10000000) add = 10000000; delta += add; qemu_icount += qemu_icount_round (add); timeout = delta / 1000000; if (timeout < 0) timeout = 0; } } return timeout; #else /* CONFIG_IOTHREAD */ return 1000; #endif }