diff --git a/hypervisor/arch/x86/guest/vm.c b/hypervisor/arch/x86/guest/vm.c index 4cffc66c5..8848d1e3c 100644 --- a/hypervisor/arch/x86/guest/vm.c +++ b/hypervisor/arch/x86/guest/vm.c @@ -187,6 +187,7 @@ int create_vm(struct vm_description *vm_desc, struct vm **rtn_vm) INIT_LIST_HEAD(&vm->softirq_dev_entry_list); spinlock_init(&vm->softirq_dev_lock); + vm->intr_inject_delay_delta = 0UL; /* Set up IO bit-mask such that VM exit occurs on * selected IO ranges diff --git a/hypervisor/common/ptdev.c b/hypervisor/common/ptdev.c index 1d18b2cb4..62db57bbd 100644 --- a/hypervisor/common/ptdev.c +++ b/hypervisor/common/ptdev.c @@ -36,6 +36,14 @@ static void ptdev_enqueue_softirq(struct ptdev_remapping_info *entry) fire_softirq(SOFTIRQ_PTDEV); } +static void ptdev_intr_delay_callback(void *data) +{ + struct ptdev_remapping_info *entry = + (struct ptdev_remapping_info *) data; + + ptdev_enqueue_softirq(entry); +} + struct ptdev_remapping_info* ptdev_dequeue_softirq(struct vm *vm) { @@ -44,10 +52,20 @@ ptdev_dequeue_softirq(struct vm *vm) spinlock_irqsave_obtain(&vm->softirq_dev_lock, &rflags); - if (!list_empty(&vm->softirq_dev_entry_list)) { + while (!list_empty(&vm->softirq_dev_entry_list)) { entry = get_first_item(&vm->softirq_dev_entry_list, struct ptdev_remapping_info, softirq_node); + list_del_init(&entry->softirq_node); + + /* if vm0, just dequeue, if uos, check delay timer */ + if (is_vm0(entry->vm) || + timer_expired(&entry->intr_delay_timer)) { + break; + } else { + /* add it into timer list; dequeue next one */ + (void)add_timer(&entry->intr_delay_timer); + } } spinlock_irqrestore_release(&vm->softirq_dev_lock, rflags); @@ -69,6 +87,10 @@ alloc_entry(struct vm *vm, uint32_t intr_type) INIT_LIST_HEAD(&entry->softirq_node); INIT_LIST_HEAD(&entry->entry_node); + entry->intr_count = 0UL; + initialize_timer(&entry->intr_delay_timer, ptdev_intr_delay_callback, + entry, 0UL, 0, 0UL); + atomic_clear32(&entry->active, ACTIVE_FLAG); list_add(&entry->entry_node, &ptdev_list); @@ -116,6 +138,22 @@ static void ptdev_interrupt_handler(__unused uint32_t irq, void *data) struct ptdev_remapping_info *entry = (struct ptdev_remapping_info *) data; + /* + * "interrupt storm" detection & delay intr injection just for UOS + * pass-thru devices, collect its data and delay injection if needed + */ + if (!is_vm0(entry->vm)) { + entry->intr_count++; + + /* if delta > 0, set the delay TSC, dequeue to handle */ + if (entry->vm->intr_inject_delay_delta > 0UL) { + entry->intr_delay_timer.fire_tsc = rdtsc() + + entry->vm->intr_inject_delay_delta; + } else { + entry->intr_delay_timer.fire_tsc = 0UL; + } + } + ptdev_enqueue_softirq(entry); } @@ -169,3 +207,27 @@ void ptdev_release_all_entries(struct vm *vm) release_all_entries(vm); spinlock_release(&ptdev_lock); } + +uint32_t get_vm_ptdev_intr_data(const struct vm *target_vm, uint64_t *buffer, + uint32_t buffer_cnt) +{ + uint32_t index = 0U; + struct ptdev_remapping_info *entry; + struct list_head *pos, *tmp; + + list_for_each_safe(pos, tmp, &ptdev_list) { + entry = list_entry(pos, struct ptdev_remapping_info, + entry_node); + if (entry->vm == target_vm) { + buffer[index] = entry->allocated_pirq; + buffer[index + 1U] = entry->intr_count; + + index += 2U; + if (index >= buffer_cnt) { + break; + } + } + } + + return index; +} diff --git a/hypervisor/include/arch/x86/guest/vm.h b/hypervisor/include/arch/x86/guest/vm.h index 893abe84a..ceeb5ffc2 100644 --- a/hypervisor/include/arch/x86/guest/vm.h +++ b/hypervisor/include/arch/x86/guest/vm.h @@ -173,6 +173,7 @@ struct vm { spinlock_t softirq_dev_lock; struct list_head softirq_dev_entry_list; + uint64_t intr_inject_delay_delta; /* delay of intr injection */ } __aligned(CPU_PAGE_SIZE); #ifdef CONFIG_PARTITION_MODE diff --git a/hypervisor/include/arch/x86/timer.h b/hypervisor/include/arch/x86/timer.h index 6c575f454..5565f2abc 100644 --- a/hypervisor/include/arch/x86/timer.h +++ b/hypervisor/include/arch/x86/timer.h @@ -48,6 +48,11 @@ static inline void initialize_timer(struct hv_timer *timer, } } +static inline bool timer_expired(const struct hv_timer *timer) +{ + return ((timer->fire_tsc == 0UL) || (rdtsc() >= timer->fire_tsc)); +} + /* * Don't call add_timer/del_timer in the timer callback function. */ diff --git a/hypervisor/include/common/ptdev.h b/hypervisor/include/common/ptdev.h index 7476e349b..26a21600f 100644 --- a/hypervisor/include/common/ptdev.h +++ b/hypervisor/include/common/ptdev.h @@ -61,6 +61,9 @@ struct ptdev_remapping_info { struct list_head softirq_node; struct list_head entry_node; struct ptdev_msi_info msi; + + uint64_t intr_count; + struct hv_timer intr_delay_timer; /* used for delay intr injection */ }; extern struct list_head ptdev_list; @@ -83,4 +86,7 @@ void ptdev_deactivate_entry(struct ptdev_remapping_info *entry); void get_ptdev_info(char *str_arg, int str_max); #endif /* HV_DEBUG */ +uint32_t get_vm_ptdev_intr_data(const struct vm *target_vm, uint64_t *buffer, + uint32_t buffer_cnt); + #endif /* PTDEV_H */