Commit efe75411ec7113294810ca7abc52ac400505e96d
1 parent
f3dcfada
Add support for dynamic ticks, by Luca Tettamanti and Dan Kenigsberg.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3130 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
177 additions
and
8 deletions
vl.c
| ... | ... | @@ -784,12 +784,32 @@ struct QEMUTimer { |
| 784 | 784 | |
| 785 | 785 | struct qemu_alarm_timer { |
| 786 | 786 | char const *name; |
| 787 | + unsigned int flags; | |
| 787 | 788 | |
| 788 | 789 | int (*start)(struct qemu_alarm_timer *t); |
| 789 | 790 | void (*stop)(struct qemu_alarm_timer *t); |
| 791 | + void (*rearm)(struct qemu_alarm_timer *t); | |
| 790 | 792 | void *priv; |
| 791 | 793 | }; |
| 792 | 794 | |
| 795 | +#define ALARM_FLAG_DYNTICKS 0x1 | |
| 796 | + | |
| 797 | +static inline int alarm_has_dynticks(struct qemu_alarm_timer *t) | |
| 798 | +{ | |
| 799 | + return t->flags & ALARM_FLAG_DYNTICKS; | |
| 800 | +} | |
| 801 | + | |
| 802 | +static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) | |
| 803 | +{ | |
| 804 | + if (!alarm_has_dynticks(t)) | |
| 805 | + return; | |
| 806 | + | |
| 807 | + t->rearm(t); | |
| 808 | +} | |
| 809 | + | |
| 810 | +/* TODO: MIN_TIMER_REARM_US should be optimized */ | |
| 811 | +#define MIN_TIMER_REARM_US 250 | |
| 812 | + | |
| 793 | 813 | static struct qemu_alarm_timer *alarm_timer; |
| 794 | 814 | |
| 795 | 815 | #ifdef _WIN32 |
| ... | ... | @@ -802,12 +822,17 @@ struct qemu_alarm_win32 { |
| 802 | 822 | |
| 803 | 823 | static int win32_start_timer(struct qemu_alarm_timer *t); |
| 804 | 824 | static void win32_stop_timer(struct qemu_alarm_timer *t); |
| 825 | +static void win32_rearm_timer(struct qemu_alarm_timer *t); | |
| 805 | 826 | |
| 806 | 827 | #else |
| 807 | 828 | |
| 808 | 829 | static int unix_start_timer(struct qemu_alarm_timer *t); |
| 809 | 830 | static void unix_stop_timer(struct qemu_alarm_timer *t); |
| 810 | 831 | |
| 832 | +static int dynticks_start_timer(struct qemu_alarm_timer *t); | |
| 833 | +static void dynticks_stop_timer(struct qemu_alarm_timer *t); | |
| 834 | +static void dynticks_rearm_timer(struct qemu_alarm_timer *t); | |
| 835 | + | |
| 811 | 836 | #ifdef __linux__ |
| 812 | 837 | |
| 813 | 838 | static int hpet_start_timer(struct qemu_alarm_timer *t); |
| ... | ... | @@ -816,21 +841,26 @@ static void hpet_stop_timer(struct qemu_alarm_timer *t); |
| 816 | 841 | static int rtc_start_timer(struct qemu_alarm_timer *t); |
| 817 | 842 | static void rtc_stop_timer(struct qemu_alarm_timer *t); |
| 818 | 843 | |
| 819 | -#endif | |
| 844 | +#endif /* __linux__ */ | |
| 820 | 845 | |
| 821 | 846 | #endif /* _WIN32 */ |
| 822 | 847 | |
| 823 | 848 | static struct qemu_alarm_timer alarm_timers[] = { |
| 849 | +#ifndef _WIN32 | |
| 850 | + {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer, | |
| 851 | + dynticks_stop_timer, dynticks_rearm_timer, NULL}, | |
| 824 | 852 | #ifdef __linux__ |
| 825 | 853 | /* HPET - if available - is preferred */ |
| 826 | - {"hpet", hpet_start_timer, hpet_stop_timer, NULL}, | |
| 854 | + {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL}, | |
| 827 | 855 | /* ...otherwise try RTC */ |
| 828 | - {"rtc", rtc_start_timer, rtc_stop_timer, NULL}, | |
| 856 | + {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL}, | |
| 829 | 857 | #endif |
| 830 | -#ifndef _WIN32 | |
| 831 | - {"unix", unix_start_timer, unix_stop_timer, NULL}, | |
| 858 | + {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL}, | |
| 832 | 859 | #else |
| 833 | - {"win32", win32_start_timer, win32_stop_timer, &alarm_win32_data}, | |
| 860 | + {"dynticks", ALARM_FLAG_DYNTICKS, win32_start_timer, | |
| 861 | + win32_stop_timer, win32_rearm_timer, &alarm_win32_data}, | |
| 862 | + {"win32", 0, win32_start_timer, | |
| 863 | + win32_stop_timer, NULL, &alarm_win32_data}, | |
| 834 | 864 | #endif |
| 835 | 865 | {NULL, } |
| 836 | 866 | }; |
| ... | ... | @@ -949,6 +979,8 @@ void qemu_del_timer(QEMUTimer *ts) |
| 949 | 979 | } |
| 950 | 980 | pt = &t->next; |
| 951 | 981 | } |
| 982 | + | |
| 983 | + qemu_rearm_alarm_timer(alarm_timer); | |
| 952 | 984 | } |
| 953 | 985 | |
| 954 | 986 | /* modify the current timer so that it will be fired when current_time |
| ... | ... | @@ -1008,6 +1040,7 @@ static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time) |
| 1008 | 1040 | /* run the callback (the timer list can be modified) */ |
| 1009 | 1041 | ts->cb(ts->opaque); |
| 1010 | 1042 | } |
| 1043 | + qemu_rearm_alarm_timer(alarm_timer); | |
| 1011 | 1044 | } |
| 1012 | 1045 | |
| 1013 | 1046 | int64_t qemu_get_clock(QEMUClock *clock) |
| ... | ... | @@ -1115,7 +1148,8 @@ static void host_alarm_handler(int host_signum) |
| 1115 | 1148 | last_clock = ti; |
| 1116 | 1149 | } |
| 1117 | 1150 | #endif |
| 1118 | - if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL], | |
| 1151 | + if (alarm_has_dynticks(alarm_timer) || | |
| 1152 | + qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL], | |
| 1119 | 1153 | qemu_get_clock(vm_clock)) || |
| 1120 | 1154 | qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], |
| 1121 | 1155 | qemu_get_clock(rt_clock))) { |
| ... | ... | @@ -1136,6 +1170,30 @@ static void host_alarm_handler(int host_signum) |
| 1136 | 1170 | } |
| 1137 | 1171 | } |
| 1138 | 1172 | |
| 1173 | +static uint64_t qemu_next_deadline(void) | |
| 1174 | +{ | |
| 1175 | + int64_t nearest_delta_us = ULLONG_MAX; | |
| 1176 | + int64_t vmdelta_us; | |
| 1177 | + | |
| 1178 | + if (active_timers[QEMU_TIMER_REALTIME]) | |
| 1179 | + nearest_delta_us = (active_timers[QEMU_TIMER_REALTIME]->expire_time - | |
| 1180 | + qemu_get_clock(rt_clock))*1000; | |
| 1181 | + | |
| 1182 | + if (active_timers[QEMU_TIMER_VIRTUAL]) { | |
| 1183 | + /* round up */ | |
| 1184 | + vmdelta_us = (active_timers[QEMU_TIMER_VIRTUAL]->expire_time - | |
| 1185 | + qemu_get_clock(vm_clock)+999)/1000; | |
| 1186 | + if (vmdelta_us < nearest_delta_us) | |
| 1187 | + nearest_delta_us = vmdelta_us; | |
| 1188 | + } | |
| 1189 | + | |
| 1190 | + /* Avoid arming the timer to negative, zero, or too low values */ | |
| 1191 | + if (nearest_delta_us <= MIN_TIMER_REARM_US) | |
| 1192 | + nearest_delta_us = MIN_TIMER_REARM_US; | |
| 1193 | + | |
| 1194 | + return nearest_delta_us; | |
| 1195 | +} | |
| 1196 | + | |
| 1139 | 1197 | #ifndef _WIN32 |
| 1140 | 1198 | |
| 1141 | 1199 | #if defined(__linux__) |
| ... | ... | @@ -1243,6 +1301,80 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t) |
| 1243 | 1301 | |
| 1244 | 1302 | #endif /* !defined(__linux__) */ |
| 1245 | 1303 | |
| 1304 | +static int dynticks_start_timer(struct qemu_alarm_timer *t) | |
| 1305 | +{ | |
| 1306 | + struct sigevent ev; | |
| 1307 | + timer_t host_timer; | |
| 1308 | + struct sigaction act; | |
| 1309 | + | |
| 1310 | + sigfillset(&act.sa_mask); | |
| 1311 | + act.sa_flags = 0; | |
| 1312 | +#if defined(TARGET_I386) && defined(USE_CODE_COPY) | |
| 1313 | + act.sa_flags |= SA_ONSTACK; | |
| 1314 | +#endif | |
| 1315 | + act.sa_handler = host_alarm_handler; | |
| 1316 | + | |
| 1317 | + sigaction(SIGALRM, &act, NULL); | |
| 1318 | + | |
| 1319 | + ev.sigev_value.sival_int = 0; | |
| 1320 | + ev.sigev_notify = SIGEV_SIGNAL; | |
| 1321 | + ev.sigev_signo = SIGALRM; | |
| 1322 | + | |
| 1323 | + if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { | |
| 1324 | + perror("timer_create"); | |
| 1325 | + | |
| 1326 | + /* disable dynticks */ | |
| 1327 | + fprintf(stderr, "Dynamic Ticks disabled\n"); | |
| 1328 | + | |
| 1329 | + return -1; | |
| 1330 | + } | |
| 1331 | + | |
| 1332 | + t->priv = (void *)host_timer; | |
| 1333 | + | |
| 1334 | + return 0; | |
| 1335 | +} | |
| 1336 | + | |
| 1337 | +static void dynticks_stop_timer(struct qemu_alarm_timer *t) | |
| 1338 | +{ | |
| 1339 | + timer_t host_timer = (timer_t)t->priv; | |
| 1340 | + | |
| 1341 | + timer_delete(host_timer); | |
| 1342 | +} | |
| 1343 | + | |
| 1344 | +static void dynticks_rearm_timer(struct qemu_alarm_timer *t) | |
| 1345 | +{ | |
| 1346 | + timer_t host_timer = (timer_t)t->priv; | |
| 1347 | + struct itimerspec timeout; | |
| 1348 | + int64_t nearest_delta_us = INT64_MAX; | |
| 1349 | + int64_t current_us; | |
| 1350 | + | |
| 1351 | + if (!active_timers[QEMU_TIMER_REALTIME] && | |
| 1352 | + !active_timers[QEMU_TIMER_VIRTUAL]) | |
| 1353 | + return; | |
| 1354 | + | |
| 1355 | + nearest_delta_us = qemu_next_deadline(); | |
| 1356 | + | |
| 1357 | + /* check whether a timer is already running */ | |
| 1358 | + if (timer_gettime(host_timer, &timeout)) { | |
| 1359 | + perror("gettime"); | |
| 1360 | + fprintf(stderr, "Internal timer error: aborting\n"); | |
| 1361 | + exit(1); | |
| 1362 | + } | |
| 1363 | + current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000; | |
| 1364 | + if (current_us && current_us <= nearest_delta_us) | |
| 1365 | + return; | |
| 1366 | + | |
| 1367 | + timeout.it_interval.tv_sec = 0; | |
| 1368 | + timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */ | |
| 1369 | + timeout.it_value.tv_sec = nearest_delta_us / 1000000; | |
| 1370 | + timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000; | |
| 1371 | + if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) { | |
| 1372 | + perror("settime"); | |
| 1373 | + fprintf(stderr, "Internal timer error: aborting\n"); | |
| 1374 | + exit(1); | |
| 1375 | + } | |
| 1376 | +} | |
| 1377 | + | |
| 1246 | 1378 | static int unix_start_timer(struct qemu_alarm_timer *t) |
| 1247 | 1379 | { |
| 1248 | 1380 | struct sigaction act; |
| ... | ... | @@ -1288,6 +1420,7 @@ static int win32_start_timer(struct qemu_alarm_timer *t) |
| 1288 | 1420 | { |
| 1289 | 1421 | TIMECAPS tc; |
| 1290 | 1422 | struct qemu_alarm_win32 *data = t->priv; |
| 1423 | + UINT flags; | |
| 1291 | 1424 | |
| 1292 | 1425 | data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 1293 | 1426 | if (!data->host_alarm) { |
| ... | ... | @@ -1303,11 +1436,17 @@ static int win32_start_timer(struct qemu_alarm_timer *t) |
| 1303 | 1436 | |
| 1304 | 1437 | timeBeginPeriod(data->period); |
| 1305 | 1438 | |
| 1439 | + flags = TIME_CALLBACK_FUNCTION; | |
| 1440 | + if (alarm_has_dynticks(t)) | |
| 1441 | + flags |= TIME_ONESHOT; | |
| 1442 | + else | |
| 1443 | + flags |= TIME_PERIODIC; | |
| 1444 | + | |
| 1306 | 1445 | data->timerId = timeSetEvent(1, // interval (ms) |
| 1307 | 1446 | data->period, // resolution |
| 1308 | 1447 | host_alarm_handler, // function |
| 1309 | 1448 | (DWORD)t, // parameter |
| 1310 | - TIME_PERIODIC | TIME_CALLBACK_FUNCTION); | |
| 1449 | + flags); | |
| 1311 | 1450 | |
| 1312 | 1451 | if (!data->timerId) { |
| 1313 | 1452 | perror("Failed to initialize win32 alarm timer"); |
| ... | ... | @@ -1332,6 +1471,35 @@ static void win32_stop_timer(struct qemu_alarm_timer *t) |
| 1332 | 1471 | CloseHandle(data->host_alarm); |
| 1333 | 1472 | } |
| 1334 | 1473 | |
| 1474 | +static void win32_rearm_timer(struct qemu_alarm_timer *t) | |
| 1475 | +{ | |
| 1476 | + struct qemu_alarm_win32 *data = t->priv; | |
| 1477 | + uint64_t nearest_delta_us; | |
| 1478 | + | |
| 1479 | + if (!active_timers[QEMU_TIMER_REALTIME] && | |
| 1480 | + !active_timers[QEMU_TIMER_VIRTUAL]) | |
| 1481 | + return; | |
| 1482 | + | |
| 1483 | + nearest_delta_us = qemu_next_deadline(); | |
| 1484 | + nearest_delta_us /= 1000; | |
| 1485 | + | |
| 1486 | + timeKillEvent(data->timerId); | |
| 1487 | + | |
| 1488 | + data->timerId = timeSetEvent(1, | |
| 1489 | + data->period, | |
| 1490 | + host_alarm_handler, | |
| 1491 | + (DWORD)t, | |
| 1492 | + TIME_ONESHOT | TIME_PERIODIC); | |
| 1493 | + | |
| 1494 | + if (!data->timerId) { | |
| 1495 | + perror("Failed to re-arm win32 alarm timer"); | |
| 1496 | + | |
| 1497 | + timeEndPeriod(data->period); | |
| 1498 | + CloseHandle(data->host_alarm); | |
| 1499 | + exit(1); | |
| 1500 | + } | |
| 1501 | +} | |
| 1502 | + | |
| 1335 | 1503 | #endif /* _WIN32 */ |
| 1336 | 1504 | |
| 1337 | 1505 | static void init_timer_alarm(void) |
| ... | ... | @@ -6490,6 +6658,7 @@ void vm_start(void) |
| 6490 | 6658 | cpu_enable_ticks(); |
| 6491 | 6659 | vm_running = 1; |
| 6492 | 6660 | vm_state_notify(1); |
| 6661 | + qemu_rearm_alarm_timer(alarm_timer); | |
| 6493 | 6662 | } |
| 6494 | 6663 | } |
| 6495 | 6664 | ... | ... |