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
This commit is contained in:
		
							parent
							
								
									f3dcfadac7
								
							
						
					
					
						commit
						efe75411ec
					
				
							
								
								
									
										185
									
								
								vl.c
								
								
								
								
							
							
						
						
									
										185
									
								
								vl.c
								
								
								
								
							| 
						 | 
				
			
			@ -784,12 +784,32 @@ struct QEMUTimer {
 | 
			
		|||
 | 
			
		||||
struct qemu_alarm_timer {
 | 
			
		||||
    char const *name;
 | 
			
		||||
    unsigned int flags;
 | 
			
		||||
 | 
			
		||||
    int (*start)(struct qemu_alarm_timer *t);
 | 
			
		||||
    void (*stop)(struct qemu_alarm_timer *t);
 | 
			
		||||
    void (*rearm)(struct qemu_alarm_timer *t);
 | 
			
		||||
    void *priv;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define ALARM_FLAG_DYNTICKS  0x1
 | 
			
		||||
 | 
			
		||||
static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
 | 
			
		||||
{
 | 
			
		||||
    return t->flags & ALARM_FLAG_DYNTICKS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
 | 
			
		||||
{
 | 
			
		||||
    if (!alarm_has_dynticks(t))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    t->rearm(t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: MIN_TIMER_REARM_US should be optimized */
 | 
			
		||||
#define MIN_TIMER_REARM_US 250
 | 
			
		||||
 | 
			
		||||
static struct qemu_alarm_timer *alarm_timer;
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
| 
						 | 
				
			
			@ -802,12 +822,17 @@ struct qemu_alarm_win32 {
 | 
			
		|||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
 | 
			
		||||
static int hpet_start_timer(struct qemu_alarm_timer *t);
 | 
			
		||||
| 
						 | 
				
			
			@ -816,21 +841,26 @@ 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
 | 
			
		||||
#endif /* __linux__ */
 | 
			
		||||
 | 
			
		||||
#endif /* _WIN32 */
 | 
			
		||||
 | 
			
		||||
static struct qemu_alarm_timer alarm_timers[] = {
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
    {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer,
 | 
			
		||||
     dynticks_stop_timer, dynticks_rearm_timer, NULL},
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    /* HPET - if available - is preferred */
 | 
			
		||||
    {"hpet", hpet_start_timer, hpet_stop_timer, NULL},
 | 
			
		||||
    {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL},
 | 
			
		||||
    /* ...otherwise try RTC */
 | 
			
		||||
    {"rtc", rtc_start_timer, rtc_stop_timer, NULL},
 | 
			
		||||
    {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL},
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
    {"unix", unix_start_timer, unix_stop_timer, NULL},
 | 
			
		||||
    {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL},
 | 
			
		||||
#else
 | 
			
		||||
    {"win32", win32_start_timer, win32_stop_timer, &alarm_win32_data},
 | 
			
		||||
    {"dynticks", ALARM_FLAG_DYNTICKS, win32_start_timer,
 | 
			
		||||
     win32_stop_timer, win32_rearm_timer, &alarm_win32_data},
 | 
			
		||||
    {"win32", 0, win32_start_timer,
 | 
			
		||||
     win32_stop_timer, NULL, &alarm_win32_data},
 | 
			
		||||
#endif
 | 
			
		||||
    {NULL, }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -949,6 +979,8 @@ void qemu_del_timer(QEMUTimer *ts)
 | 
			
		|||
        }
 | 
			
		||||
        pt = &t->next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_rearm_alarm_timer(alarm_timer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		|||
        /* run the callback (the timer list can be modified) */
 | 
			
		||||
        ts->cb(ts->opaque);
 | 
			
		||||
    }
 | 
			
		||||
    qemu_rearm_alarm_timer(alarm_timer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t qemu_get_clock(QEMUClock *clock)
 | 
			
		||||
| 
						 | 
				
			
			@ -1115,7 +1148,8 @@ static void host_alarm_handler(int host_signum)
 | 
			
		|||
        last_clock = ti;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
 | 
			
		||||
    if (alarm_has_dynticks(alarm_timer) ||
 | 
			
		||||
        qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
 | 
			
		||||
                           qemu_get_clock(vm_clock)) ||
 | 
			
		||||
        qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
 | 
			
		||||
                           qemu_get_clock(rt_clock))) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1136,6 +1170,30 @@ static void host_alarm_handler(int host_signum)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t qemu_next_deadline(void)
 | 
			
		||||
{
 | 
			
		||||
    int64_t nearest_delta_us = ULLONG_MAX;
 | 
			
		||||
    int64_t vmdelta_us;
 | 
			
		||||
 | 
			
		||||
    if (active_timers[QEMU_TIMER_REALTIME])
 | 
			
		||||
        nearest_delta_us = (active_timers[QEMU_TIMER_REALTIME]->expire_time -
 | 
			
		||||
                            qemu_get_clock(rt_clock))*1000;
 | 
			
		||||
 | 
			
		||||
    if (active_timers[QEMU_TIMER_VIRTUAL]) {
 | 
			
		||||
        /* round up */
 | 
			
		||||
        vmdelta_us = (active_timers[QEMU_TIMER_VIRTUAL]->expire_time -
 | 
			
		||||
                      qemu_get_clock(vm_clock)+999)/1000;
 | 
			
		||||
        if (vmdelta_us < nearest_delta_us)
 | 
			
		||||
            nearest_delta_us = vmdelta_us;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Avoid arming the timer to negative, zero, or too low values */
 | 
			
		||||
    if (nearest_delta_us <= MIN_TIMER_REARM_US)
 | 
			
		||||
        nearest_delta_us = MIN_TIMER_REARM_US;
 | 
			
		||||
 | 
			
		||||
    return nearest_delta_us;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
 | 
			
		||||
#if defined(__linux__)
 | 
			
		||||
| 
						 | 
				
			
			@ -1243,6 +1301,80 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t)
 | 
			
		|||
 | 
			
		||||
#endif /* !defined(__linux__) */
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
#if defined(TARGET_I386) && defined(USE_CODE_COPY)
 | 
			
		||||
    act.sa_flags |= SA_ONSTACK;
 | 
			
		||||
#endif
 | 
			
		||||
    act.sa_handler = host_alarm_handler;
 | 
			
		||||
 | 
			
		||||
    sigaction(SIGALRM, &act, NULL);
 | 
			
		||||
 | 
			
		||||
    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 *)host_timer;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dynticks_stop_timer(struct qemu_alarm_timer *t)
 | 
			
		||||
{
 | 
			
		||||
    timer_t host_timer = (timer_t)t->priv;
 | 
			
		||||
 | 
			
		||||
    timer_delete(host_timer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
 | 
			
		||||
{
 | 
			
		||||
    timer_t host_timer = (timer_t)t->priv;
 | 
			
		||||
    struct itimerspec timeout;
 | 
			
		||||
    int64_t nearest_delta_us = INT64_MAX;
 | 
			
		||||
    int64_t current_us;
 | 
			
		||||
 | 
			
		||||
    if (!active_timers[QEMU_TIMER_REALTIME] &&
 | 
			
		||||
                !active_timers[QEMU_TIMER_VIRTUAL])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
    nearest_delta_us = qemu_next_deadline();
 | 
			
		||||
 | 
			
		||||
    /* 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int unix_start_timer(struct qemu_alarm_timer *t)
 | 
			
		||||
{
 | 
			
		||||
    struct sigaction act;
 | 
			
		||||
| 
						 | 
				
			
			@ -1288,6 +1420,7 @@ static int win32_start_timer(struct qemu_alarm_timer *t)
 | 
			
		|||
{
 | 
			
		||||
    TIMECAPS tc;
 | 
			
		||||
    struct qemu_alarm_win32 *data = t->priv;
 | 
			
		||||
    UINT flags;
 | 
			
		||||
 | 
			
		||||
    data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL);
 | 
			
		||||
    if (!data->host_alarm) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1303,11 +1436,17 @@ static int win32_start_timer(struct qemu_alarm_timer *t)
 | 
			
		|||
 | 
			
		||||
    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
 | 
			
		||||
                        TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
 | 
			
		||||
                        flags);
 | 
			
		||||
 | 
			
		||||
    if (!data->timerId) {
 | 
			
		||||
        perror("Failed to initialize win32 alarm timer");
 | 
			
		||||
| 
						 | 
				
			
			@ -1332,6 +1471,35 @@ static void win32_stop_timer(struct qemu_alarm_timer *t)
 | 
			
		|||
    CloseHandle(data->host_alarm);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void win32_rearm_timer(struct qemu_alarm_timer *t)
 | 
			
		||||
{
 | 
			
		||||
    struct qemu_alarm_win32 *data = t->priv;
 | 
			
		||||
    uint64_t nearest_delta_us;
 | 
			
		||||
 | 
			
		||||
    if (!active_timers[QEMU_TIMER_REALTIME] &&
 | 
			
		||||
                !active_timers[QEMU_TIMER_VIRTUAL])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
    nearest_delta_us = qemu_next_deadline();
 | 
			
		||||
    nearest_delta_us /= 1000;
 | 
			
		||||
 | 
			
		||||
    timeKillEvent(data->timerId);
 | 
			
		||||
 | 
			
		||||
    data->timerId = timeSetEvent(1,
 | 
			
		||||
                        data->period,
 | 
			
		||||
                        host_alarm_handler,
 | 
			
		||||
                        (DWORD)t,
 | 
			
		||||
                        TIME_ONESHOT | TIME_PERIODIC);
 | 
			
		||||
 | 
			
		||||
    if (!data->timerId) {
 | 
			
		||||
        perror("Failed to re-arm win32 alarm timer");
 | 
			
		||||
 | 
			
		||||
        timeEndPeriod(data->period);
 | 
			
		||||
        CloseHandle(data->host_alarm);
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* _WIN32 */
 | 
			
		||||
 | 
			
		||||
static void init_timer_alarm(void)
 | 
			
		||||
| 
						 | 
				
			
			@ -6490,6 +6658,7 @@ void vm_start(void)
 | 
			
		|||
        cpu_enable_ticks();
 | 
			
		||||
        vm_running = 1;
 | 
			
		||||
        vm_state_notify(1);
 | 
			
		||||
        qemu_rearm_alarm_timer(alarm_timer);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue