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,12 +784,32 @@ struct QEMUTimer { | ||
784 | 784 | ||
785 | struct qemu_alarm_timer { | 785 | struct qemu_alarm_timer { |
786 | char const *name; | 786 | char const *name; |
787 | + unsigned int flags; | ||
787 | 788 | ||
788 | int (*start)(struct qemu_alarm_timer *t); | 789 | int (*start)(struct qemu_alarm_timer *t); |
789 | void (*stop)(struct qemu_alarm_timer *t); | 790 | void (*stop)(struct qemu_alarm_timer *t); |
791 | + void (*rearm)(struct qemu_alarm_timer *t); | ||
790 | void *priv; | 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 | static struct qemu_alarm_timer *alarm_timer; | 813 | static struct qemu_alarm_timer *alarm_timer; |
794 | 814 | ||
795 | #ifdef _WIN32 | 815 | #ifdef _WIN32 |
@@ -802,12 +822,17 @@ struct qemu_alarm_win32 { | @@ -802,12 +822,17 @@ struct qemu_alarm_win32 { | ||
802 | 822 | ||
803 | static int win32_start_timer(struct qemu_alarm_timer *t); | 823 | static int win32_start_timer(struct qemu_alarm_timer *t); |
804 | static void win32_stop_timer(struct qemu_alarm_timer *t); | 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 | #else | 827 | #else |
807 | 828 | ||
808 | static int unix_start_timer(struct qemu_alarm_timer *t); | 829 | static int unix_start_timer(struct qemu_alarm_timer *t); |
809 | static void unix_stop_timer(struct qemu_alarm_timer *t); | 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 | #ifdef __linux__ | 836 | #ifdef __linux__ |
812 | 837 | ||
813 | static int hpet_start_timer(struct qemu_alarm_timer *t); | 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,21 +841,26 @@ static void hpet_stop_timer(struct qemu_alarm_timer *t); | ||
816 | static int rtc_start_timer(struct qemu_alarm_timer *t); | 841 | static int rtc_start_timer(struct qemu_alarm_timer *t); |
817 | static void rtc_stop_timer(struct qemu_alarm_timer *t); | 842 | static void rtc_stop_timer(struct qemu_alarm_timer *t); |
818 | 843 | ||
819 | -#endif | 844 | +#endif /* __linux__ */ |
820 | 845 | ||
821 | #endif /* _WIN32 */ | 846 | #endif /* _WIN32 */ |
822 | 847 | ||
823 | static struct qemu_alarm_timer alarm_timers[] = { | 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 | #ifdef __linux__ | 852 | #ifdef __linux__ |
825 | /* HPET - if available - is preferred */ | 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 | /* ...otherwise try RTC */ | 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 | #endif | 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 | #else | 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 | #endif | 864 | #endif |
835 | {NULL, } | 865 | {NULL, } |
836 | }; | 866 | }; |
@@ -949,6 +979,8 @@ void qemu_del_timer(QEMUTimer *ts) | @@ -949,6 +979,8 @@ void qemu_del_timer(QEMUTimer *ts) | ||
949 | } | 979 | } |
950 | pt = &t->next; | 980 | pt = &t->next; |
951 | } | 981 | } |
982 | + | ||
983 | + qemu_rearm_alarm_timer(alarm_timer); | ||
952 | } | 984 | } |
953 | 985 | ||
954 | /* modify the current timer so that it will be fired when current_time | 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,6 +1040,7 @@ static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time) | ||
1008 | /* run the callback (the timer list can be modified) */ | 1040 | /* run the callback (the timer list can be modified) */ |
1009 | ts->cb(ts->opaque); | 1041 | ts->cb(ts->opaque); |
1010 | } | 1042 | } |
1043 | + qemu_rearm_alarm_timer(alarm_timer); | ||
1011 | } | 1044 | } |
1012 | 1045 | ||
1013 | int64_t qemu_get_clock(QEMUClock *clock) | 1046 | int64_t qemu_get_clock(QEMUClock *clock) |
@@ -1115,7 +1148,8 @@ static void host_alarm_handler(int host_signum) | @@ -1115,7 +1148,8 @@ static void host_alarm_handler(int host_signum) | ||
1115 | last_clock = ti; | 1148 | last_clock = ti; |
1116 | } | 1149 | } |
1117 | #endif | 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 | qemu_get_clock(vm_clock)) || | 1153 | qemu_get_clock(vm_clock)) || |
1120 | qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], | 1154 | qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], |
1121 | qemu_get_clock(rt_clock))) { | 1155 | qemu_get_clock(rt_clock))) { |
@@ -1136,6 +1170,30 @@ static void host_alarm_handler(int host_signum) | @@ -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 | #ifndef _WIN32 | 1197 | #ifndef _WIN32 |
1140 | 1198 | ||
1141 | #if defined(__linux__) | 1199 | #if defined(__linux__) |
@@ -1243,6 +1301,80 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t) | @@ -1243,6 +1301,80 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t) | ||
1243 | 1301 | ||
1244 | #endif /* !defined(__linux__) */ | 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 | static int unix_start_timer(struct qemu_alarm_timer *t) | 1378 | static int unix_start_timer(struct qemu_alarm_timer *t) |
1247 | { | 1379 | { |
1248 | struct sigaction act; | 1380 | struct sigaction act; |
@@ -1288,6 +1420,7 @@ static int win32_start_timer(struct qemu_alarm_timer *t) | @@ -1288,6 +1420,7 @@ static int win32_start_timer(struct qemu_alarm_timer *t) | ||
1288 | { | 1420 | { |
1289 | TIMECAPS tc; | 1421 | TIMECAPS tc; |
1290 | struct qemu_alarm_win32 *data = t->priv; | 1422 | struct qemu_alarm_win32 *data = t->priv; |
1423 | + UINT flags; | ||
1291 | 1424 | ||
1292 | data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); | 1425 | data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); |
1293 | if (!data->host_alarm) { | 1426 | if (!data->host_alarm) { |
@@ -1303,11 +1436,17 @@ static int win32_start_timer(struct qemu_alarm_timer *t) | @@ -1303,11 +1436,17 @@ static int win32_start_timer(struct qemu_alarm_timer *t) | ||
1303 | 1436 | ||
1304 | timeBeginPeriod(data->period); | 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 | data->timerId = timeSetEvent(1, // interval (ms) | 1445 | data->timerId = timeSetEvent(1, // interval (ms) |
1307 | data->period, // resolution | 1446 | data->period, // resolution |
1308 | host_alarm_handler, // function | 1447 | host_alarm_handler, // function |
1309 | (DWORD)t, // parameter | 1448 | (DWORD)t, // parameter |
1310 | - TIME_PERIODIC | TIME_CALLBACK_FUNCTION); | 1449 | + flags); |
1311 | 1450 | ||
1312 | if (!data->timerId) { | 1451 | if (!data->timerId) { |
1313 | perror("Failed to initialize win32 alarm timer"); | 1452 | perror("Failed to initialize win32 alarm timer"); |
@@ -1332,6 +1471,35 @@ static void win32_stop_timer(struct qemu_alarm_timer *t) | @@ -1332,6 +1471,35 @@ static void win32_stop_timer(struct qemu_alarm_timer *t) | ||
1332 | CloseHandle(data->host_alarm); | 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 | #endif /* _WIN32 */ | 1503 | #endif /* _WIN32 */ |
1336 | 1504 | ||
1337 | static void init_timer_alarm(void) | 1505 | static void init_timer_alarm(void) |
@@ -6490,6 +6658,7 @@ void vm_start(void) | @@ -6490,6 +6658,7 @@ void vm_start(void) | ||
6490 | cpu_enable_ticks(); | 6658 | cpu_enable_ticks(); |
6491 | vm_running = 1; | 6659 | vm_running = 1; |
6492 | vm_state_notify(1); | 6660 | vm_state_notify(1); |
6661 | + qemu_rearm_alarm_timer(alarm_timer); | ||
6493 | } | 6662 | } |
6494 | } | 6663 | } |
6495 | 6664 |