|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v4 08/12] x86/vpt: switch interrupt injection model
On 20.04.2021 16:07, Roger Pau Monne wrote:
> @@ -295,188 +248,153 @@ static void pt_irq_fired(struct vcpu *v, struct
> periodic_time *pt)
> list_del(&pt->list);
> pt->on_list = false;
> pt->pending_intr_nr = 0;
> +
> + return;
> }
> - else 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);
> - pt_process_missed_ticks(pt);
> - pt->pending_intr_nr = 0; /* 'collapse' all missed ticks */
> - set_timer(&pt->timer, pt->scheduled);
> - }
> - else
> +
> + pt_process_missed_ticks(pt);
> + /* 'collapse' missed ticks according to the selected mode. */
> + switch ( pt->vcpu->domain->arch.hvm.params[HVM_PARAM_TIMER_MODE] )
> {
> - pt->last_plt_gtime += pt->period;
> - if ( --pt->pending_intr_nr == 0 )
> - {
> - pt_process_missed_ticks(pt);
> - if ( pt->pending_intr_nr == 0 )
> - set_timer(&pt->timer, pt->scheduled);
> - }
> + case HVMPTM_one_missed_tick_pending:
> + pt->pending_intr_nr = min(pt->pending_intr_nr, 1u);
> + break;
> +
> + case HVMPTM_no_missed_ticks_pending:
> + pt->pending_intr_nr = 0;
> + break;
> }
>
> - if ( mode_is(v->domain, delay_for_missed_ticks) &&
> - (hvm_get_guest_time(v) < pt->last_plt_gtime) )
> - hvm_set_guest_time(v, pt->last_plt_gtime);
> + if ( !pt->pending_intr_nr )
> + set_timer(&pt->timer, pt->scheduled);
> }
>
> -int pt_update_irq(struct vcpu *v)
> +static void pt_timer_fn(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;
> + unsigned int irq;
>
> - pt_vcpu_lock(v);
> + pt_lock(pt);
>
> - earliest_pt = NULL;
> - max_lag = -1ULL;
> - list_for_each_entry_safe ( pt, temp, head, list )
> + v = pt->vcpu;
> + irq = pt->irq;
> +
> + if ( inject_interrupt(pt) )
> {
> - if ( pt->pending_intr_nr )
> - {
> - if ( pt_irq_masked(pt) &&
> - /* Level interrupts should be asserted even if masked. */
> - !pt->level )
> - {
> - /* suspend timer emulation */
> - 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->scheduled += pt->period;
> + pt->do_not_freeze = 0;
Nit: "false" please.
> + cb = pt->cb;
> + cb_priv = pt->priv;
> }
> -
> - if ( earliest_pt == NULL )
> + else
> {
> - pt_vcpu_unlock(v);
> - return -1;
> + /* Masked. */
> + if ( pt->on_list )
> + list_del(&pt->list);
> + pt->on_list = false;
> + pt->pending_intr_nr++;
> }
inject_interrupt() returns whether it was able to deliver the interrupt.
This in particular fails if the interrupt was masked and is edge
triggered. This, unexpectedly to me, reports success if a level triggered
interrupt was already pending. But in either event, the missed ticks
accounting is, as per my understanding of the comment in hvm/params.h,
supposed to be dealing with missing delivery due to preemption only. An
interrupt being masked / already pending may not be in this state due to
the guest having got preempted, though. A guest keeping a timer interrupt
masked for an extended period of time should not get a flood of
interrupts later on, no matter what HVM_PARAM_TIMER_MODE is set to.
However, I'm not going to exclude that little bit of doc is wrong, or
implementation and doc aren't in agreement already before your change.
> - earliest_pt->irq_issued = 1;
This looks to be the only place where the field gets set to non-zero.
If the field is unused after this change, it wants deleting. I notice
patch 11 does so, but it may be worthwhile pointing out
- in the description here, that field removal will happen later,
- in the later patch, that this field was already unused (and doesn't
become dead by the other removal done there).
> - irq = earliest_pt->irq;
> - level = earliest_pt->level;
> + pt_unlock(pt);
>
> - pt_vcpu_unlock(v);
> + if ( cb )
> + cb(v, cb_priv);
> +}
>
> - switch ( earliest_pt->source )
> - {
> - case PTSRC_lapic:
> - /*
> - * If periodic timer interrupt is handled by lapic, its vector in
> - * IRR is returned and used to set eoi_exit_bitmap for virtual
> - * interrupt delivery case. Otherwise return -1 to do nothing.
> - */
> - vlapic_set_irq(vcpu_vlapic(v), irq, 0);
> - pt_vector = irq;
> - break;
> +static void eoi_callback(struct periodic_time *pt)
> +{
> + struct vcpu *v = NULL;
> + time_cb *cb = NULL;
> + void *cb_priv = NULL;
>
> - case PTSRC_isa:
> - hvm_isa_irq_deassert(v->domain, irq);
> - if ( platform_legacy_irq(irq) && vlapic_accept_pic_intr(v) &&
> - v->domain->arch.hvm.vpic[irq >> 3].int_output )
> - hvm_isa_irq_assert(v->domain, irq, NULL);
> - else
> + pt_lock(pt);
> +
> + irq_eoi(pt);
> + if ( pt->pending_intr_nr )
> + {
> + if ( inject_interrupt(pt) )
> {
> - pt_vector = hvm_isa_irq_assert(v->domain, irq,
> vioapic_get_vector);
> - /*
> - * hvm_isa_irq_assert may not set the corresponding bit in vIRR
> - * when mask field of IOAPIC RTE is set. Check it again.
> - */
> - if ( pt_vector < 0 || !vlapic_test_irq(vcpu_vlapic(v),
> pt_vector) )
> - pt_vector = -1;
> + pt->pending_intr_nr--;
> + cb = pt->cb;
> + cb_priv = pt->priv;
> + v = pt->vcpu;
> }
> - break;
> -
> - case PTSRC_ioapic:
> - pt_vector = hvm_ioapic_assert(v->domain, irq, level);
> - if ( pt_vector < 0 || !vlapic_test_irq(vcpu_vlapic(v), pt_vector) )
> + else
> {
> - pt_vector = -1;
> - if ( level )
> - {
> - /*
> - * Level interrupts are always asserted because the pin
> assert
> - * count is incremented regardless of whether the pin is
> masked
> - * or the vector latched in IRR, so also execute the callback
> - * associated with the timer.
> - */
> - time_cb *cb = NULL;
> - void *cb_priv = NULL;
> -
> - pt_vcpu_lock(v);
> - /* Make sure the timer is still on the list. */
> - list_for_each_entry ( pt, &v->arch.hvm.tm_list, list )
> - if ( pt == earliest_pt )
> - {
> - pt_irq_fired(v, pt);
> - cb = pt->cb;
> - cb_priv = pt->priv;
> - break;
> - }
> - pt_vcpu_unlock(v);
> -
> - if ( cb != NULL )
> - cb(v, cb_priv);
> - }
> + /* Masked. */
> + if ( pt->on_list )
> + list_del(&pt->list);
> + pt->on_list = false;
> }
> - break;
> }
>
> - return pt_vector;
> + pt_unlock(pt);
> +
> + if ( cb )
> + cb(v, cb_priv);
> }
>
> -static struct periodic_time *is_pt_irq(
> - struct vcpu *v, struct hvm_intack intack)
> +static void vlapic_eoi_callback(struct vcpu *unused, unsigned int unused2,
> + void *data)
> {
> - struct list_head *head = &v->arch.hvm.tm_list;
> - struct periodic_time *pt;
> -
> - list_for_each_entry ( pt, head, list )
> - {
> - if ( pt->pending_intr_nr && pt->irq_issued &&
> - (intack.vector == pt_irq_vector(pt, intack.source)) )
> - return pt;
> - }
> + eoi_callback(data);
> +}
>
> - return NULL;
> +static void vioapic_eoi_callback(struct domain *unused, unsigned int unused2,
> + void *data)
> +{
> + eoi_callback(data);
> }
>
> -void pt_intr_post(struct vcpu *v, struct hvm_intack intack)
> +static bool inject_interrupt(struct periodic_time *pt)
> {
> - struct periodic_time *pt;
> - time_cb *cb;
> - void *cb_priv;
> + struct vcpu *v = pt->vcpu;
> + struct domain *d = v->domain;
> + unsigned int irq = pt->irq;
>
> - if ( intack.source == hvm_intsrc_vector )
> - return;
> + /* Level interrupts should be asserted even if masked. */
> + if ( pt_irq_masked(pt) && !pt->level )
> + return false;
>
> - pt_vcpu_lock(v);
> -
> - pt = is_pt_irq(v, intack);
> - if ( pt == NULL )
> + switch ( pt->source )
> {
> - pt_vcpu_unlock(v);
> - return;
> + case PTSRC_lapic:
> + vlapic_set_irq_callback(vcpu_vlapic(v), pt->irq, 0,
> vlapic_eoi_callback,
> + pt);
> + break;
> +
> + case PTSRC_isa:
> + hvm_isa_irq_deassert(d, irq);
> + hvm_isa_irq_assert(d, irq, NULL);
> + break;
> +
> + case PTSRC_ioapic:
> + hvm_ioapic_assert(d, irq, pt->level);
> + break;
> }
Why do ISA IRQs get de-asserted first, but IO-APIC ones don't? I
notice e.g. hvm_set_callback_irq_level() and hvm_set_pci_link_route()
have similar apparent asymmetries, so I guess I'm missing something.
In particular I can't spot - even prior to this change - where
hvm_irq->gsi_assert_count[gsi] would get decremented for a level
triggered IRQ, when hvm_ioapic_deassert() gets called only from
hvm/hpet.c:hpet_write(). I guess the main point is that that's the
only case of a level triggered timer interrupt for us?
> @@ -641,20 +590,29 @@ void pt_adjust_global_vcpu_target(struct vcpu *v)
> write_unlock(&pl_time->vhpet.lock);
> }
>
> -
> static void pt_resume(struct periodic_time *pt)
> {
> + struct vcpu *v;
> + time_cb *cb = NULL;
> + void *cb_priv;
> +
> if ( pt->vcpu == NULL )
> return;
>
> pt_lock(pt);
> - if ( pt->pending_intr_nr && !pt->on_list )
> + if ( pt->pending_intr_nr && !pt->on_list && inject_interrupt(pt) )
> {
> + pt->pending_intr_nr--;
> + cb = pt->cb;
> + cb_priv = pt->priv;
> + v = pt->vcpu;
> pt->on_list = 1;
> list_add(&pt->list, &pt->vcpu->arch.hvm.tm_list);
> - vcpu_kick(pt->vcpu);
Just for my own understanding: The replacement of this is what happens
down the call tree from inject_interrupt()?
Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |