diff --git a/devicemodel/core/vm_event.c b/devicemodel/core/vm_event.c index 6a797b609..11ac0ed87 100644 --- a/devicemodel/core/vm_event.c +++ b/devicemodel/core/vm_event.c @@ -33,6 +33,8 @@ #define THROTTLE_WINDOW 1U /* time window for throttle counter, in secs*/ +#define BROKEN_TIME ((time_t)-1) + typedef void (*vm_event_handler)(struct vmctx *ctx, struct vm_event *event); typedef void (*vm_event_generate_jdata)(cJSON *event_obj, struct vm_event *event); @@ -43,6 +45,9 @@ static char dm_vm_event_page[4096] __aligned(4096); static pthread_t vm_event_tid; static void general_event_handler(struct vmctx *ctx, struct vm_event *event); +static void rtc_chg_event_handler(struct vmctx *ctx, struct vm_event *event); + +static void gen_rtc_chg_jdata(cJSON *event_obj, struct vm_event *event); enum event_source_type { EVENT_SOURCE_TYPE_HV, @@ -70,14 +75,13 @@ struct vm_event_proc { vm_event_handler ve_handler; uint32_t throttle_rate; /* how many events allowed per sec */ struct event_throttle_ctl throttle_ctl; - vm_event_generate_jdata gen_jdata_handler; + vm_event_generate_jdata gen_jdata_handler; /* how to transtfer vm_event data to json txt */ }; - static struct vm_event_proc ve_proc[VM_EVENT_COUNT] = { [VM_EVENT_RTC_CHG] = { - .ve_handler = general_event_handler, - .gen_jdata_handler = NULL, + .ve_handler = rtc_chg_event_handler, + .gen_jdata_handler = gen_rtc_chg_jdata, .throttle_rate = 1, }, [VM_EVENT_POWEROFF] = { @@ -206,6 +210,7 @@ static char *generate_vm_event_message(struct vm_event *event) fprintf(stderr, "Failed to generate vm_event message.\n"); cJSON_Delete(event_obj); + return event_msg; } @@ -225,6 +230,79 @@ static void general_event_handler(struct vmctx *ctx, struct vm_event *event) emit_vm_event(ctx, event); } +static void gen_rtc_chg_jdata(cJSON *event_obj, struct vm_event *event) +{ + struct rtc_change_event_data *data = (struct rtc_change_event_data *)event->event_data; + cJSON *val; + + val = cJSON_CreateNumber(data->delta_time); + if (val != NULL) { + cJSON_AddItemToObject(event_obj, "delta_time", val); + } + val = cJSON_CreateNumber(data->last_time); + if (val != NULL) { + cJSON_AddItemToObject(event_obj, "last_time", val); + } +} + +/* assume we only have one unique rtc source */ + +static struct acrn_timer rtc_chg_event_timer = { + .clockid = CLOCK_MONOTONIC, +}; +static pthread_mutex_t rtc_chg_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct timespec time_window_start; +static time_t last_time_cached = BROKEN_TIME; +static time_t delta_time_sum = 0; +#define RTC_CHG_WAIT_TIME 1 /* 1 second */ +static void rtc_chg_event_handler(struct vmctx *ctx, struct vm_event *event) +{ + struct itimerspec timer_spec; + struct rtc_change_event_data *data = (struct rtc_change_event_data *)event->event_data; + + /* + * RTC time is not reliable until guest finishes updating all RTC date/time regs. + * So wait for some time, if no more change happens, we can conclude that the RTC + * change has been done. + */ + timer_spec.it_value.tv_sec = RTC_CHG_WAIT_TIME; + timer_spec.it_value.tv_nsec = 0; + timer_spec.it_interval.tv_sec = 0; + timer_spec.it_interval.tv_nsec = 0; + pthread_mutex_lock(&rtc_chg_mutex); + if (last_time_cached == BROKEN_TIME) { + last_time_cached = data->last_time; + } + delta_time_sum += data->delta_time; + /* The last timer will be overwriten if it is not triggered yet. */ + acrn_timer_settime(&rtc_chg_event_timer, &timer_spec); + clock_gettime(CLOCK_MONOTONIC, &time_window_start); + pthread_mutex_unlock(&rtc_chg_mutex); +} + +static void rtc_chg_timer_cb(void *arg, uint64_t nexp) +{ + struct timespec now, delta; + struct timespec time_window_size = {RTC_CHG_WAIT_TIME, 0}; + struct vmctx *ctx = arg; + struct vm_event send_event; + struct rtc_change_event_data *data = (struct rtc_change_event_data *)send_event.event_data; + + pthread_mutex_lock(&rtc_chg_mutex); + clock_gettime(CLOCK_MONOTONIC, &now); + delta = now; + timespecsub(&delta, &time_window_start); + /* possible racing problem here. make sure this is the right timer cb for the vm_event */ + if (timespeccmp(&delta, &time_window_size, >=)) { + data->delta_time = delta_time_sum; + data->last_time = last_time_cached; + emit_vm_event(ctx, &send_event); + last_time_cached = BROKEN_TIME; + delta_time_sum = 0; + } + pthread_mutex_unlock(&rtc_chg_mutex); +} + static void *vm_event_thread(void *param) { int n, i; @@ -372,6 +450,8 @@ int vm_event_init(struct vmctx *ctx) goto out; } + acrn_timer_init(&rtc_chg_event_timer, rtc_chg_timer_cb, ctx); + started = true; return 0; diff --git a/devicemodel/hw/platform/rtc.c b/devicemodel/hw/platform/rtc.c index f3b90c16c..82de9b7fb 100644 --- a/devicemodel/hw/platform/rtc.c +++ b/devicemodel/hw/platform/rtc.c @@ -41,6 +41,7 @@ #include "timer.h" #include "acpi.h" #include "lpc.h" +#include "vm_event.h" #include "log.h" @@ -80,6 +81,7 @@ struct vrtc { u_int addr; /* RTC register to read or write */ time_t base_uptime; time_t base_rtctime; + time_t halted_rtctime; struct rtcdev rtcdev; }; @@ -725,6 +727,18 @@ vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval) } } +static void +send_rtc_chg_event(time_t newtime, time_t lasttime) +{ + struct vm_event event; + struct rtc_change_event_data *data = (struct rtc_change_event_data *)event.event_data; + + event.type = VM_EVENT_RTC_CHG; + data->delta_time = newtime - lasttime; + data->last_time = lasttime; + dm_send_vm_event(&event); +} + static int vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval) { @@ -751,12 +765,19 @@ vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval) if (rtctime == VRTC_BROKEN_TIME) { if (rtc_flag_broken_time) return -1; + } else { + /* send rtc change event if rtc time changed during halt */ + if (vrtc->halted_rtctime != VRTC_BROKEN_TIME && + rtctime != vrtc->halted_rtctime) { + send_rtc_chg_event(rtctime, vrtc->halted_rtctime); + vrtc->halted_rtctime = VRTC_BROKEN_TIME; + } } } else { curtime = vrtc_curtime(vrtc, &basetime); if (curtime != vrtc->base_rtctime) return -1; - + vrtc->halted_rtctime = curtime; /* * Force a refresh of the RTC date/time fields so * they reflect the time right before the guest set @@ -982,10 +1003,18 @@ vrtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, * so re-calculate the RTC date/time. */ if (vrtc_is_time_register(offset) && !rtc_halted(vrtc)) { + time_t last_time = curtime; curtime = rtc_to_secs(vrtc); error = vrtc_time_update(vrtc, curtime, monotonic_time()); - if ((error != 0) || (curtime == VRTC_BROKEN_TIME && rtc_flag_broken_time)) + if ((error != 0) || (curtime == VRTC_BROKEN_TIME && rtc_flag_broken_time)) { error = -1; + } else { + /* We don't know when the Guest has finished the RTC change action. + * So send an event each time the date/time regs has been updated. + * The event handler will process those events. + */ + send_rtc_chg_event(rtc_to_secs(vrtc), last_time); + } } } @@ -1105,6 +1134,7 @@ vrtc_init(struct vmctx *ctx) /* Reset the index register to a safe value. */ vrtc->addr = RTC_STATUSD; + vrtc->halted_rtctime = VRTC_BROKEN_TIME; /* * Initialize RTC time to 00:00:00 Jan 1, 1970 if curtime = 0 */ diff --git a/devicemodel/include/vm_event.h b/devicemodel/include/vm_event.h index 7a7ea423a..2ed4e498b 100644 --- a/devicemodel/include/vm_event.h +++ b/devicemodel/include/vm_event.h @@ -13,5 +13,6 @@ int vm_event_init(struct vmctx *ctx); int vm_event_deinit(void); int dm_send_vm_event(struct vm_event *event); +uint32_t get_dm_vm_event_overrun_count(void); #endif /* VM_EVENT_H */ diff --git a/hypervisor/include/public/acrn_common.h b/hypervisor/include/public/acrn_common.h index c61314f86..eb2053a2d 100644 --- a/hypervisor/include/public/acrn_common.h +++ b/hypervisor/include/public/acrn_common.h @@ -842,6 +842,16 @@ struct vm_event { uint8_t event_data[VM_EVENT_DATA_LEN]; }; +struct rtc_change_event_data { + int64_t last_time; /* time(in secs) of the RTC defore been set */ + int64_t delta_time; /* delta of time(in secs) the RTC has been changed */ +}; + +/* DM vrtc's reference time is besed on sys time, while the HV vrtc is based on pRTC. + * When the delta time is sent to users, they need to know what it is relative to. + */ +#define RTC_CHG_RELATIVE_PHYSICAL_RTC 0 +#define RTC_CHG_RELATIVE_SERVICE_VM_SYS_TIME 1 /** * @} */