Commit c89940133c02810197c405814439b0d529e5d551
1 parent
7603d156
Rework alarm timer infrastrucure, by Luca Tettamanti.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3125 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
186 additions
and
104 deletions
vl.c
| @@ -781,19 +781,59 @@ struct QEMUTimer { | @@ -781,19 +781,59 @@ struct QEMUTimer { | ||
| 781 | struct QEMUTimer *next; | 781 | struct QEMUTimer *next; |
| 782 | }; | 782 | }; |
| 783 | 783 | ||
| 784 | -QEMUClock *rt_clock; | ||
| 785 | -QEMUClock *vm_clock; | 784 | +struct qemu_alarm_timer { |
| 785 | + char const *name; | ||
| 786 | + | ||
| 787 | + int (*start)(struct qemu_alarm_timer *t); | ||
| 788 | + void (*stop)(struct qemu_alarm_timer *t); | ||
| 789 | + void *priv; | ||
| 790 | +}; | ||
| 791 | + | ||
| 792 | +static struct qemu_alarm_timer *alarm_timer; | ||
| 786 | 793 | ||
| 787 | -static QEMUTimer *active_timers[2]; | ||
| 788 | #ifdef _WIN32 | 794 | #ifdef _WIN32 |
| 789 | -static MMRESULT timerID; | ||
| 790 | -static HANDLE host_alarm = NULL; | ||
| 791 | -static unsigned int period = 1; | 795 | + |
| 796 | +struct qemu_alarm_win32 { | ||
| 797 | + MMRESULT timerId; | ||
| 798 | + HANDLE host_alarm; | ||
| 799 | + unsigned int period; | ||
| 800 | +} alarm_win32_data = {0, NULL, -1}; | ||
| 801 | + | ||
| 802 | +static int win32_start_timer(struct qemu_alarm_timer *t); | ||
| 803 | +static void win32_stop_timer(struct qemu_alarm_timer *t); | ||
| 804 | + | ||
| 792 | #else | 805 | #else |
| 793 | -/* frequency of the times() clock tick */ | ||
| 794 | -static int timer_freq; | 806 | + |
| 807 | +static int unix_start_timer(struct qemu_alarm_timer *t); | ||
| 808 | +static void unix_stop_timer(struct qemu_alarm_timer *t); | ||
| 809 | + | ||
| 810 | +#ifdef __linux__ | ||
| 811 | + | ||
| 812 | +static int rtc_start_timer(struct qemu_alarm_timer *t); | ||
| 813 | +static void rtc_stop_timer(struct qemu_alarm_timer *t); | ||
| 814 | + | ||
| 795 | #endif | 815 | #endif |
| 796 | 816 | ||
| 817 | +#endif /* _WIN32 */ | ||
| 818 | + | ||
| 819 | +static struct qemu_alarm_timer alarm_timers[] = { | ||
| 820 | +#ifdef __linux__ | ||
| 821 | + /* RTC - if available - is preferred */ | ||
| 822 | + {"rtc", rtc_start_timer, rtc_stop_timer, NULL}, | ||
| 823 | +#endif | ||
| 824 | +#ifndef _WIN32 | ||
| 825 | + {"unix", unix_start_timer, unix_stop_timer, NULL}, | ||
| 826 | +#else | ||
| 827 | + {"win32", win32_start_timer, win32_stop_timer, &alarm_win32_data}, | ||
| 828 | +#endif | ||
| 829 | + {NULL, } | ||
| 830 | +}; | ||
| 831 | + | ||
| 832 | +QEMUClock *rt_clock; | ||
| 833 | +QEMUClock *vm_clock; | ||
| 834 | + | ||
| 835 | +static QEMUTimer *active_timers[2]; | ||
| 836 | + | ||
| 797 | QEMUClock *qemu_new_clock(int type) | 837 | QEMUClock *qemu_new_clock(int type) |
| 798 | { | 838 | { |
| 799 | QEMUClock *clock; | 839 | QEMUClock *clock; |
| @@ -1009,7 +1049,8 @@ static void host_alarm_handler(int host_signum) | @@ -1009,7 +1049,8 @@ static void host_alarm_handler(int host_signum) | ||
| 1009 | qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], | 1049 | qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], |
| 1010 | qemu_get_clock(rt_clock))) { | 1050 | qemu_get_clock(rt_clock))) { |
| 1011 | #ifdef _WIN32 | 1051 | #ifdef _WIN32 |
| 1012 | - SetEvent(host_alarm); | 1052 | + struct qemu_alarm_win32 *data = ((struct qemu_alarm_timer*)dwUser)->priv; |
| 1053 | + SetEvent(data->host_alarm); | ||
| 1013 | #endif | 1054 | #endif |
| 1014 | CPUState *env = cpu_single_env; | 1055 | CPUState *env = cpu_single_env; |
| 1015 | if (env) { | 1056 | if (env) { |
| @@ -1030,10 +1071,27 @@ static void host_alarm_handler(int host_signum) | @@ -1030,10 +1071,27 @@ static void host_alarm_handler(int host_signum) | ||
| 1030 | 1071 | ||
| 1031 | #define RTC_FREQ 1024 | 1072 | #define RTC_FREQ 1024 |
| 1032 | 1073 | ||
| 1033 | -static int rtc_fd; | 1074 | +static void enable_sigio_timer(int fd) |
| 1075 | +{ | ||
| 1076 | + struct sigaction act; | ||
| 1034 | 1077 | ||
| 1035 | -static int start_rtc_timer(void) | 1078 | + /* timer signal */ |
| 1079 | + sigfillset(&act.sa_mask); | ||
| 1080 | + act.sa_flags = 0; | ||
| 1081 | +#if defined (TARGET_I386) && defined(USE_CODE_COPY) | ||
| 1082 | + act.sa_flags |= SA_ONSTACK; | ||
| 1083 | +#endif | ||
| 1084 | + act.sa_handler = host_alarm_handler; | ||
| 1085 | + | ||
| 1086 | + sigaction(SIGIO, &act, NULL); | ||
| 1087 | + fcntl(fd, F_SETFL, O_ASYNC); | ||
| 1088 | + fcntl(fd, F_SETOWN, getpid()); | ||
| 1089 | +} | ||
| 1090 | + | ||
| 1091 | +static int rtc_start_timer(struct qemu_alarm_timer *t) | ||
| 1036 | { | 1092 | { |
| 1093 | + int rtc_fd; | ||
| 1094 | + | ||
| 1037 | TFR(rtc_fd = open("/dev/rtc", O_RDONLY)); | 1095 | TFR(rtc_fd = open("/dev/rtc", O_RDONLY)); |
| 1038 | if (rtc_fd < 0) | 1096 | if (rtc_fd < 0) |
| 1039 | return -1; | 1097 | return -1; |
| @@ -1048,117 +1106,142 @@ static int start_rtc_timer(void) | @@ -1048,117 +1106,142 @@ static int start_rtc_timer(void) | ||
| 1048 | close(rtc_fd); | 1106 | close(rtc_fd); |
| 1049 | return -1; | 1107 | return -1; |
| 1050 | } | 1108 | } |
| 1051 | - pit_min_timer_count = PIT_FREQ / RTC_FREQ; | 1109 | + |
| 1110 | + enable_sigio_timer(rtc_fd); | ||
| 1111 | + | ||
| 1112 | + t->priv = (void *)rtc_fd; | ||
| 1113 | + | ||
| 1052 | return 0; | 1114 | return 0; |
| 1053 | } | 1115 | } |
| 1054 | 1116 | ||
| 1055 | -#else | ||
| 1056 | - | ||
| 1057 | -static int start_rtc_timer(void) | 1117 | +static void rtc_stop_timer(struct qemu_alarm_timer *t) |
| 1058 | { | 1118 | { |
| 1059 | - return -1; | 1119 | + int rtc_fd = (int)t->priv; |
| 1120 | + | ||
| 1121 | + close(rtc_fd); | ||
| 1060 | } | 1122 | } |
| 1061 | 1123 | ||
| 1062 | #endif /* !defined(__linux__) */ | 1124 | #endif /* !defined(__linux__) */ |
| 1063 | 1125 | ||
| 1064 | -#endif /* !defined(_WIN32) */ | 1126 | +static int unix_start_timer(struct qemu_alarm_timer *t) |
| 1127 | +{ | ||
| 1128 | + struct sigaction act; | ||
| 1129 | + struct itimerval itv; | ||
| 1130 | + int err; | ||
| 1065 | 1131 | ||
| 1066 | -static void init_timer_alarm(void) | 1132 | + /* timer signal */ |
| 1133 | + sigfillset(&act.sa_mask); | ||
| 1134 | + act.sa_flags = 0; | ||
| 1135 | +#if defined(TARGET_I386) && defined(USE_CODE_COPY) | ||
| 1136 | + act.sa_flags |= SA_ONSTACK; | ||
| 1137 | +#endif | ||
| 1138 | + act.sa_handler = host_alarm_handler; | ||
| 1139 | + | ||
| 1140 | + sigaction(SIGALRM, &act, NULL); | ||
| 1141 | + | ||
| 1142 | + itv.it_interval.tv_sec = 0; | ||
| 1143 | + /* for i386 kernel 2.6 to get 1 ms */ | ||
| 1144 | + itv.it_interval.tv_usec = 999; | ||
| 1145 | + itv.it_value.tv_sec = 0; | ||
| 1146 | + itv.it_value.tv_usec = 10 * 1000; | ||
| 1147 | + | ||
| 1148 | + err = setitimer(ITIMER_REAL, &itv, NULL); | ||
| 1149 | + if (err) | ||
| 1150 | + return -1; | ||
| 1151 | + | ||
| 1152 | + return 0; | ||
| 1153 | +} | ||
| 1154 | + | ||
| 1155 | +static void unix_stop_timer(struct qemu_alarm_timer *t) | ||
| 1067 | { | 1156 | { |
| 1157 | + struct itimerval itv; | ||
| 1158 | + | ||
| 1159 | + memset(&itv, 0, sizeof(itv)); | ||
| 1160 | + setitimer(ITIMER_REAL, &itv, NULL); | ||
| 1161 | +} | ||
| 1162 | + | ||
| 1163 | +#endif /* !defined(_WIN32) */ | ||
| 1164 | + | ||
| 1068 | #ifdef _WIN32 | 1165 | #ifdef _WIN32 |
| 1069 | - { | ||
| 1070 | - int count=0; | ||
| 1071 | - TIMECAPS tc; | ||
| 1072 | - | ||
| 1073 | - ZeroMemory(&tc, sizeof(TIMECAPS)); | ||
| 1074 | - timeGetDevCaps(&tc, sizeof(TIMECAPS)); | ||
| 1075 | - if (period < tc.wPeriodMin) | ||
| 1076 | - period = tc.wPeriodMin; | ||
| 1077 | - timeBeginPeriod(period); | ||
| 1078 | - timerID = timeSetEvent(1, // interval (ms) | ||
| 1079 | - period, // resolution | ||
| 1080 | - host_alarm_handler, // function | ||
| 1081 | - (DWORD)&count, // user parameter | ||
| 1082 | - TIME_PERIODIC | TIME_CALLBACK_FUNCTION); | ||
| 1083 | - if( !timerID ) { | ||
| 1084 | - perror("failed timer alarm"); | ||
| 1085 | - exit(1); | ||
| 1086 | - } | ||
| 1087 | - host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); | ||
| 1088 | - if (!host_alarm) { | ||
| 1089 | - perror("failed CreateEvent"); | ||
| 1090 | - exit(1); | ||
| 1091 | - } | ||
| 1092 | - qemu_add_wait_object(host_alarm, NULL, NULL); | 1166 | + |
| 1167 | +static int win32_start_timer(struct qemu_alarm_timer *t) | ||
| 1168 | +{ | ||
| 1169 | + TIMECAPS tc; | ||
| 1170 | + struct qemu_alarm_win32 *data = t->priv; | ||
| 1171 | + | ||
| 1172 | + data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); | ||
| 1173 | + if (!data->host_alarm) { | ||
| 1174 | + perror("Failed CreateEvent"); | ||
| 1175 | + return -1 | ||
| 1093 | } | 1176 | } |
| 1094 | - pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000; | ||
| 1095 | -#else | ||
| 1096 | - { | ||
| 1097 | - struct sigaction act; | ||
| 1098 | - struct itimerval itv; | ||
| 1099 | - | ||
| 1100 | - /* get times() syscall frequency */ | ||
| 1101 | - timer_freq = sysconf(_SC_CLK_TCK); | ||
| 1102 | - | ||
| 1103 | - /* timer signal */ | ||
| 1104 | - sigfillset(&act.sa_mask); | ||
| 1105 | - act.sa_flags = 0; | ||
| 1106 | -#if defined (TARGET_I386) && defined(USE_CODE_COPY) | ||
| 1107 | - act.sa_flags |= SA_ONSTACK; | ||
| 1108 | -#endif | ||
| 1109 | - act.sa_handler = host_alarm_handler; | ||
| 1110 | - sigaction(SIGALRM, &act, NULL); | ||
| 1111 | 1177 | ||
| 1112 | - itv.it_interval.tv_sec = 0; | ||
| 1113 | - itv.it_interval.tv_usec = 999; /* for i386 kernel 2.6 to get 1 ms */ | ||
| 1114 | - itv.it_value.tv_sec = 0; | ||
| 1115 | - itv.it_value.tv_usec = 10 * 1000; | ||
| 1116 | - setitimer(ITIMER_REAL, &itv, NULL); | ||
| 1117 | - /* we probe the tick duration of the kernel to inform the user if | ||
| 1118 | - the emulated kernel requested a too high timer frequency */ | ||
| 1119 | - getitimer(ITIMER_REAL, &itv); | 1178 | + memset(&tc, 0, sizeof(tc)); |
| 1179 | + timeGetDevCaps(&tc, sizeof(tc)); | ||
| 1120 | 1180 | ||
| 1121 | -#if defined(__linux__) | ||
| 1122 | - /* XXX: force /dev/rtc usage because even 2.6 kernels may not | ||
| 1123 | - have timers with 1 ms resolution. The correct solution will | ||
| 1124 | - be to use the POSIX real time timers available in recent | ||
| 1125 | - 2.6 kernels */ | ||
| 1126 | - if (itv.it_interval.tv_usec > 1000 || 1) { | ||
| 1127 | - /* try to use /dev/rtc to have a faster timer */ | ||
| 1128 | - if (start_rtc_timer() < 0) | ||
| 1129 | - goto use_itimer; | ||
| 1130 | - /* disable itimer */ | ||
| 1131 | - itv.it_interval.tv_sec = 0; | ||
| 1132 | - itv.it_interval.tv_usec = 0; | ||
| 1133 | - itv.it_value.tv_sec = 0; | ||
| 1134 | - itv.it_value.tv_usec = 0; | ||
| 1135 | - setitimer(ITIMER_REAL, &itv, NULL); | ||
| 1136 | - | ||
| 1137 | - /* use the RTC */ | ||
| 1138 | - sigaction(SIGIO, &act, NULL); | ||
| 1139 | - fcntl(rtc_fd, F_SETFL, O_ASYNC); | ||
| 1140 | - fcntl(rtc_fd, F_SETOWN, getpid()); | ||
| 1141 | - } else | ||
| 1142 | -#endif /* defined(__linux__) */ | ||
| 1143 | - { | ||
| 1144 | - use_itimer: | ||
| 1145 | - pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * | ||
| 1146 | - PIT_FREQ) / 1000000; | ||
| 1147 | - } | 1181 | + if (data->period < tc.wPeriodMin) |
| 1182 | + data->period = tc.wPeriodMin; | ||
| 1183 | + | ||
| 1184 | + timeBeginPeriod(data->period); | ||
| 1185 | + | ||
| 1186 | + data->timerId = timeSetEvent(1, // interval (ms) | ||
| 1187 | + data->period, // resolution | ||
| 1188 | + host_alarm_handler, // function | ||
| 1189 | + (DWORD)t, // parameter | ||
| 1190 | + TIME_PERIODIC | TIME_CALLBACK_FUNCTION); | ||
| 1191 | + | ||
| 1192 | + if (!data->timerId) { | ||
| 1193 | + perror("Failed to initialize win32 alarm timer"); | ||
| 1194 | + | ||
| 1195 | + timeEndPeriod(data->period); | ||
| 1196 | + CloseHandle(data->host_alarm); | ||
| 1197 | + return -1; | ||
| 1148 | } | 1198 | } |
| 1149 | -#endif | 1199 | + |
| 1200 | + qemu_add_wait_object(data->host_alarm, NULL, NULL); | ||
| 1201 | + | ||
| 1202 | + return 0; | ||
| 1150 | } | 1203 | } |
| 1151 | 1204 | ||
| 1152 | -void quit_timers(void) | 1205 | +static void win32_stop_timer(struct qemu_alarm_timer *t) |
| 1153 | { | 1206 | { |
| 1154 | -#ifdef _WIN32 | ||
| 1155 | - timeKillEvent(timerID); | ||
| 1156 | - timeEndPeriod(period); | ||
| 1157 | - if (host_alarm) { | ||
| 1158 | - CloseHandle(host_alarm); | ||
| 1159 | - host_alarm = NULL; | 1207 | + struct qemu_alarm_win32 *data = t->priv; |
| 1208 | + | ||
| 1209 | + timeKillEvent(data->timerId); | ||
| 1210 | + timeEndPeriod(data->period); | ||
| 1211 | + | ||
| 1212 | + CloseHandle(data->host_alarm); | ||
| 1213 | +} | ||
| 1214 | + | ||
| 1215 | +#endif /* _WIN32 */ | ||
| 1216 | + | ||
| 1217 | +static void init_timer_alarm(void) | ||
| 1218 | +{ | ||
| 1219 | + struct qemu_alarm_timer *t; | ||
| 1220 | + int i, err = -1; | ||
| 1221 | + | ||
| 1222 | + for (i = 0; alarm_timers[i].name; i++) { | ||
| 1223 | + t = &alarm_timers[i]; | ||
| 1224 | + | ||
| 1225 | + printf("trying %s...\n", t->name); | ||
| 1226 | + | ||
| 1227 | + err = t->start(t); | ||
| 1228 | + if (!err) | ||
| 1229 | + break; | ||
| 1160 | } | 1230 | } |
| 1161 | -#endif | 1231 | + |
| 1232 | + if (err) { | ||
| 1233 | + fprintf(stderr, "Unable to find any suitable alarm timer.\n"); | ||
| 1234 | + fprintf(stderr, "Terminating\n"); | ||
| 1235 | + exit(1); | ||
| 1236 | + } | ||
| 1237 | + | ||
| 1238 | + alarm_timer = t; | ||
| 1239 | +} | ||
| 1240 | + | ||
| 1241 | +void quit_timers(void) | ||
| 1242 | +{ | ||
| 1243 | + alarm_timer->stop(alarm_timer); | ||
| 1244 | + alarm_timer = NULL; | ||
| 1162 | } | 1245 | } |
| 1163 | 1246 | ||
| 1164 | /***********************************************************/ | 1247 | /***********************************************************/ |
vl.h
| @@ -447,7 +447,6 @@ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time); | @@ -447,7 +447,6 @@ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time); | ||
| 447 | int qemu_timer_pending(QEMUTimer *ts); | 447 | int qemu_timer_pending(QEMUTimer *ts); |
| 448 | 448 | ||
| 449 | extern int64_t ticks_per_sec; | 449 | extern int64_t ticks_per_sec; |
| 450 | -extern int pit_min_timer_count; | ||
| 451 | 450 | ||
| 452 | int64_t cpu_get_ticks(void); | 451 | int64_t cpu_get_ticks(void); |
| 453 | void cpu_enable_ticks(void); | 452 | void cpu_enable_ticks(void); |