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 | ... | ... |