|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v2 09/11] x86/vpt: switch interrupt injection model
On 30.09.2020 12:41, Roger Pau Monne wrote:
> Currently vPT relies on timers being assigned to a vCPU and performing
> checks on every return to HVM guest in order to check if an interrupt
> from a vPT timer assigned to the vCPU is currently being injected.
>
> This model doesn't work properly since the interrupt destination vCPU
> of a vPT timer can be different from the vCPU where the timer is
> currently assigned, in which case the timer would get stuck because it
> never sees the interrupt as being injected.
>
> Knowing when a vPT interrupt is injected is relevant for the guest
> timer modes where missed vPT interrupts are not discarded and instead
> are accumulated and injected when possible.
>
> This change aims to modify the logic described above, so that vPT
> doesn't need to check on every return to HVM guest if a vPT interrupt
> is being injected. In order to achieve this the vPT code is modified
> to make use of the new EOI callbacks, so that virtual timers can
> detect when a interrupt has been serviced by the guest by waiting for
> the EOI callback to execute.
>
> This model also simplifies some of the logic, as when executing the
> timer EOI callback Xen can try to inject another interrupt if the
> timer has interrupts pending for delivery.
>
> Note that timers are still bound to a vCPU for the time being, this
> relation however doesn't limit the interrupt destination anymore, and
> will be removed by further patches.
>
> This model has been tested with Windows 7 guests without showing any
> timer delay, even when the guest was limited to have very little CPU
> capacity and pending virtual timer interrupts accumulate.
>
> Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
> ---
> Changes since v1:
> - New in this version.
> ---
> Sorry, this is a big change, but I'm having issues splitting it into
> smaller pieces as the functionality needs to be changed in one go, or
> else timers would be broken.
>
> If this approach seems sensible I can try to split it up.
If it can't sensibly be split, so be it, I would say. And yes, the
approach does look sensible to me, supported by ...
> ---
> xen/arch/x86/hvm/svm/intr.c | 3 -
> xen/arch/x86/hvm/vmx/intr.c | 59 ------
> xen/arch/x86/hvm/vpt.c | 326 ++++++++++++++--------------------
> xen/include/asm-x86/hvm/vpt.h | 5 +-
> 4 files changed, 135 insertions(+), 258 deletions(-)
... this diffstat. Good work!
Just a couple of nits, but before giving this my ack I may need to
go through it a 2nd time.
> +/*
> + * The same callback is shared between LAPIC and PIC/IO-APIC based timers, as
> + * we ignore the first parameter that's different between them.
> + */
> +static void eoi_callback(unsigned int unused, void *data)
> {
> - struct list_head *head = &v->arch.hvm.tm_list;
> - struct periodic_time *pt, *temp, *earliest_pt;
> - uint64_t max_lag;
> - int irq, pt_vector = -1;
> - bool level;
> + struct periodic_time *pt = data;
> + struct vcpu *v;
> + time_cb *cb = NULL;
> + void *cb_priv;
>
> - pt_vcpu_lock(v);
> + pt_lock(pt);
>
> - earliest_pt = NULL;
> - max_lag = -1ULL;
> - list_for_each_entry_safe ( pt, temp, head, list )
> + pt_irq_fired(pt->vcpu, pt);
> + if ( pt->pending_intr_nr )
> {
> - if ( pt->pending_intr_nr )
> + if ( inject_interrupt(pt) )
> + {
> + pt->pending_intr_nr--;
> + cb = pt->cb;
> + cb_priv = pt->priv;
> + v = pt->vcpu;
> + }
> + else
> {
> - /* RTC code takes care of disabling the timer itself. */
> - if ( (pt->irq != RTC_IRQ || !pt->priv) && pt_irq_masked(pt) &&
> - /* Level interrupts should be asserted even if masked. */
> - !pt->level )
> - {
> - /* suspend timer emulation */
> + /* Masked. */
> + if ( pt->on_list )
> list_del(&pt->list);
> - pt->on_list = 0;
> - }
> - else
> - {
> - if ( (pt->last_plt_gtime + pt->period) < max_lag )
> - {
> - max_lag = pt->last_plt_gtime + pt->period;
> - earliest_pt = pt;
> - }
> - }
> + pt->on_list = false;
> }
> }
>
> - if ( earliest_pt == NULL )
> - {
> - pt_vcpu_unlock(v);
> - return -1;
> - }
> + pt_unlock(pt);
>
> - earliest_pt->irq_issued = 1;
> - irq = earliest_pt->irq;
> - level = earliest_pt->level;
> + if ( cb != NULL )
> + cb(v, cb_priv);
Nit: Like done elsewhere, omit the " != NULL"?
> + /* Update time when an interrupt is injected. */
> + if ( mode_is(v->domain, one_missed_tick_pending) ||
> + mode_is(v->domain, no_missed_ticks_pending) )
> + pt->last_plt_gtime = hvm_get_guest_time(v);
> + else
> + pt->last_plt_gtime += pt->period;
>
> - pt_vcpu_unlock(v);
> + if ( mode_is(v->domain, delay_for_missed_ticks) &&
This looks to be possible to move into the "else" above, but on the
whole maybe everything together would best be handled by switch()?
> @@ -543,6 +443,24 @@ void create_periodic_time(
> pt->cb = cb;
> pt->priv = data;
>
> + switch ( pt->source )
> + {
> + int rc;
> +
> + case PTSRC_isa:
> + irq = hvm_isa_irq_to_gsi(irq);
> + /* fallthrough */
> + case PTSRC_ioapic:
> + pt->eoi_cb.callback = eoi_callback;
> + pt->eoi_cb.data = pt;
> + rc = hvm_gsi_register_callback(v->domain, irq, &pt->eoi_cb);
> + if ( rc )
> + gdprintk(XENLOG_WARNING,
> + "unable to register callback for timer GSI %u: %d\n",
> + irq, rc);
If this triggers, would it be helpful to also log pt->source?
Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |