There has no need to use two periodic timer to update RTC time. In this patch, we only update it when guest reading it. Signed-off-by: Yang Zhang <yang.z.zhang@xxxxxxxxx> --- hw/mc146818rtc.c | 199 +++++++++++++++--------------------------------------- 1 files changed, 56 insertions(+), 143 deletions(-) diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 4a43225..b6655ae 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -44,6 +44,8 @@ # define DPRINTF_C(format, ...) do { } while (0) #endif +#define USEC_PER_SEC 1000000 + #define RTC_REINJECT_ON_ACK_COUNT 20 #define RTC_SECONDS 0 @@ -85,6 +87,8 @@ typedef struct RTCState { uint8_t cmos_data[128]; uint8_t cmos_index; struct tm current_tm; + int64_t offset_sec; + int32_t offset_usec; int32_t base_year; qemu_irq irq; qemu_irq sqw_irq; @@ -92,20 +96,15 @@ typedef struct RTCState { /* periodic timer */ QEMUTimer *periodic_timer; int64_t next_periodic_time; - /* second update */ - int64_t next_second_time; uint16_t irq_reinject_on_ack_count; uint32_t irq_coalesced; uint32_t period; QEMUTimer *coalesced_timer; - QEMUTimer *second_timer; - QEMUTimer *second_timer2; Notifier clock_reset_notifier; LostTickPolicy lost_tick_policy; } RTCState; static void rtc_set_time(RTCState *s); -static void rtc_copy_date(RTCState *s); #ifdef TARGET_I386 static void rtc_coalesced_timer_update(RTCState *s) @@ -206,6 +205,24 @@ static void rtc_periodic_timer(void *opaque) } } +static void rtc_set_offset(RTCState *s) +{ + struct tm *tm = &s->current_tm; + struct timeval tv_now; + int64_t host_usec, guest_sec, guest_usec; + + gettimeofday(&tv_now, NULL); + host_usec = tv_now.tv_sec * USEC_PER_SEC + tv_now.tv_usec; + + guest_sec = mktimegm(tm); + /* after setting the clock, the next second tick + * will occur in exactly 500 ms*/ + guest_usec = guest_sec * USEC_PER_SEC + 500000; + + s->offset_sec = (guest_usec - host_usec) / USEC_PER_SEC; + s->offset_usec = (guest_usec - host_usec) % USEC_PER_SEC; +} + static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) { RTCState *s = opaque; @@ -232,6 +249,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) /* if in set mode, do not update the time */ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { rtc_set_time(s); + rtc_set_offset(s); } break; case RTC_REG_A: @@ -249,6 +267,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) /* if disabling set mode, update the time */ if (s->cmos_data[RTC_REG_B] & REG_B_SET) { rtc_set_time(s); + rtc_set_offset(s); } } if (((s->cmos_data[RTC_REG_B] ^ data) & (REG_B_DM | REG_B_24H)) && @@ -256,7 +275,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) /* If the time format has changed and not in set mode, update the registers immediately. */ s->cmos_data[RTC_REG_B] = data; - rtc_copy_date(s); + rtc_set_offset(s); } else { s->cmos_data[RTC_REG_B] = data; } @@ -312,10 +331,10 @@ static void rtc_set_time(RTCState *s) rtc_change_mon_event(tm); } -static void rtc_copy_date(RTCState *s) +static void rtc_set_cmos(RTCState *s) { - const struct tm *tm = &s->current_tm; int year; + struct tm *tm = &s->current_tm; s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec); s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min); @@ -338,121 +357,22 @@ static void rtc_copy_date(RTCState *s) s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year); } -/* month is between 0 and 11. */ -static int get_days_in_month(int month, int year) -{ - static const int days_tab[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - int d; - if ((unsigned )month >= 12) - return 31; - d = days_tab[month]; - if (month == 1) { - if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) - d++; - } - return d; -} - -/* update 'tm' to the next second */ -static void rtc_next_second(struct tm *tm) -{ - int days_in_month; - - tm->tm_sec++; - if ((unsigned)tm->tm_sec >= 60) { - tm->tm_sec = 0; - tm->tm_min++; - if ((unsigned)tm->tm_min >= 60) { - tm->tm_min = 0; - tm->tm_hour++; - if ((unsigned)tm->tm_hour >= 24) { - tm->tm_hour = 0; - /* next day */ - tm->tm_wday++; - if ((unsigned)tm->tm_wday >= 7) - tm->tm_wday = 0; - days_in_month = get_days_in_month(tm->tm_mon, - tm->tm_year + 1900); - tm->tm_mday++; - if (tm->tm_mday < 1) { - tm->tm_mday = 1; - } else if (tm->tm_mday > days_in_month) { - tm->tm_mday = 1; - tm->tm_mon++; - if (tm->tm_mon >= 12) { - tm->tm_mon = 0; - tm->tm_year++; - } - } - } - } - } -} - - -static void rtc_update_second(void *opaque) -{ - RTCState *s = opaque; - int64_t delay; - - /* if the oscillator is not in normal operation, we do not update */ - if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) { - s->next_second_time += get_ticks_per_sec(); - qemu_mod_timer(s->second_timer, s->next_second_time); - } else { - rtc_next_second(&s->current_tm); - - if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { - /* update in progress bit */ - s->cmos_data[RTC_REG_A] |= REG_A_UIP; - } - /* should be 244 us = 8 / 32768 seconds, but currently the - timers do not have the necessary resolution. */ - delay = (get_ticks_per_sec() * 1) / 100; - if (delay < 1) - delay = 1; - qemu_mod_timer(s->second_timer2, - s->next_second_time + delay); - } -} - -static void rtc_update_second2(void *opaque) +static void rtc_calibrate_time(RTCState *s) { - RTCState *s = opaque; + struct timeval tv_now; + struct tm *ret; + time_t guest_sec; + int64_t host_usec, offset_usec, guest_usec; - if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { - rtc_copy_date(s); - } + gettimeofday(&tv_now, NULL); + host_usec = tv_now.tv_sec * USEC_PER_SEC + tv_now.tv_usec; + offset_usec = s->offset_sec * USEC_PER_SEC + s->offset_usec; + guest_usec = host_usec + offset_usec; - /* check alarm */ - if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 || - rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) && - ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 || - rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) && - ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 || - rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) { + guest_sec = guest_usec / USEC_PER_SEC; + ret = gmtime(&guest_sec); - s->cmos_data[RTC_REG_C] |= REG_C_AF; - if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { - qemu_irq_raise(s->irq); - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - } - } - - /* update ended interrupt */ - s->cmos_data[RTC_REG_C] |= REG_C_UF; - if (s->cmos_data[RTC_REG_B] & REG_B_UIE) { - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - qemu_irq_raise(s->irq); - } - - /* clear update in progress bit */ - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - - s->next_second_time += get_ticks_per_sec(); - qemu_mod_timer(s->second_timer, s->next_second_time); + s->current_tm = *ret; } static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) @@ -470,6 +390,8 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) case RTC_DAY_OF_MONTH: case RTC_MONTH: case RTC_YEAR: + rtc_calibrate_time(s); + rtc_set_cmos(s); ret = s->cmos_data[s->cmos_index]; break; case RTC_REG_A: @@ -513,13 +435,6 @@ void rtc_set_memory(ISADevice *dev, int addr, int val) s->cmos_data[addr] = val; } -void rtc_set_date(ISADevice *dev, const struct tm *tm) -{ - RTCState *s = DO_UPCAST(RTCState, dev, dev); - s->current_tm = *tm; - rtc_copy_date(s); -} - /* PC cmos mappings */ #define REG_IBM_CENTURY_BYTE 0x32 #define REG_IBM_PS2_CENTURY_BYTE 0x37 @@ -530,9 +445,14 @@ static void rtc_set_date_from_host(ISADevice *dev) struct tm tm; int val; - /* set the CMOS date */ qemu_get_timedate(&tm, 0); - rtc_set_date(dev, &tm); + + s->offset_sec = mktimegm(&tm) - time(NULL); + s->offset_usec = 0; + + /* set the CMOS date */ + s->current_tm = tm; + rtc_set_cmos(s); val = rtc_to_bcd(s, (tm.tm_year / 100) + 19); rtc_set_memory(dev, REG_IBM_CENTURY_BYTE, val); @@ -555,9 +475,9 @@ static int rtc_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_rtc = { .name = "mc146818rtc", - .version_id = 2, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, .post_load = rtc_post_load, .fields = (VMStateField []) { VMSTATE_BUFFER(cmos_data, RTCState), @@ -569,11 +489,10 @@ static const VMStateDescription vmstate_rtc = { VMSTATE_INT32(current_tm.tm_mday, RTCState), VMSTATE_INT32(current_tm.tm_mon, RTCState), VMSTATE_INT32(current_tm.tm_year, RTCState), + VMSTATE_INT64(offset_sec, RTCState), + VMSTATE_INT32(offset_usec, RTCState), VMSTATE_TIMER(periodic_timer, RTCState), VMSTATE_INT64(next_periodic_time, RTCState), - VMSTATE_INT64(next_second_time, RTCState), - VMSTATE_TIMER(second_timer, RTCState), - VMSTATE_TIMER(second_timer2, RTCState), VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), VMSTATE_UINT32_V(period, RTCState, 2), VMSTATE_END_OF_LIST() @@ -586,8 +505,6 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *data) int64_t now = *(int64_t *)data; rtc_set_date_from_host(&s->dev); - s->next_second_time = now + (get_ticks_per_sec() * 99) / 100; - qemu_mod_timer(s->second_timer2, s->next_second_time); rtc_timer_update(s, now); #ifdef TARGET_I386 if (s->lost_tick_policy == LOST_TICK_SLEW) { @@ -634,6 +551,8 @@ static void rtc_get_date(Object *obj, Visitor *v, void *opaque, ISADevice *isa = ISA_DEVICE(obj); RTCState *s = DO_UPCAST(RTCState, dev, isa); + rtc_calibrate_time(s); + rtc_set_cmos(s); visit_start_struct(v, NULL, "struct tm", name, 0, errp); visit_type_int32(v, &s->current_tm.tm_year, "tm_year", errp); visit_type_int32(v, &s->current_tm.tm_mon, "tm_mon", errp); @@ -670,20 +589,14 @@ static int rtc_initfn(ISADevice *dev) #endif s->periodic_timer = qemu_new_timer_ns(rtc_clock, rtc_periodic_timer, s); - s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s); - s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s); s->clock_reset_notifier.notify = rtc_notify_clock_reset; qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier); - s->next_second_time = - qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100; - qemu_mod_timer(s->second_timer2, s->next_second_time); - memory_region_init_io(&s->io, &cmos_ops, s, "rtc", 2); isa_register_ioport(dev, &s->io, base); - qdev_set_legacy_instance_id(&dev->qdev, base, 2); + qdev_set_legacy_instance_id(&dev->qdev, base, 3); qemu_register_reset(rtc_reset, s); object_property_add(OBJECT(s), "date", "struct tm", -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html