]> xenbits.xensource.com Git - xen.git/commitdiff
x86/vpt: add support for level interrupts
authorRoger Pau Monné <roger.pau@citrix.com>
Tue, 24 Jul 2018 13:52:47 +0000 (15:52 +0200)
committerJan Beulich <jbeulich@suse.com>
Tue, 24 Jul 2018 13:52:47 +0000 (15:52 +0200)
Level trigger interrupts will be asserted regardless of whether the
interrupt is masked, and thus the callback will also be executed.

Add a new 'level' parameter to create_periodic_time in order to create
level triggered timers. None of the current users of vpt are switched
to use level triggered interrupts yet.

Note that periodic level triggered interrupts are not supported. This
is because level triggered interrupts always require a deassert of the
IO-APIC pin, which should be done by the caller of vpt at which point
the caller should also reset the timer if required.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/hvm/hpet.c
xen/arch/x86/hvm/i8254.c
xen/arch/x86/hvm/rtc.c
xen/arch/x86/hvm/vlapic.c
xen/arch/x86/hvm/vpt.c
xen/include/asm-x86/hvm/vpt.h

index 171afe9ac206c38ca28b9a8135d16cbef0d56e18..b7dcfa8af960c3f36be0d4bc0d124874243db820 100644 (file)
@@ -292,7 +292,7 @@ static void hpet_set_timer(HPETState *h, unsigned int tn,
     create_periodic_time(vhpet_vcpu(h), &h->pt[tn],
                          hpet_tick_to_ns(h, diff),
                          oneshot ? 0 : hpet_tick_to_ns(h, h->hpet.period[tn]),
-                         irq, NULL, NULL);
+                         irq, NULL, NULL, false);
 }
 
 static inline uint64_t hpet_fixup_reg(
index 992f08dd6c86c98c63d27957fa5bebd03477e3b0..b8ec56f8d3860979e02e7c3cdf4912b3e1044c2e 100644 (file)
@@ -191,14 +191,14 @@ static void pit_load_count(PITState *pit, int channel, int val)
         /* Periodic timer. */
         TRACE_2D(TRC_HVM_EMUL_PIT_START_TIMER, period, period);
         create_periodic_time(v, &pit->pt0, period, period, 0, pit_time_fired, 
-                             &pit->count_load_time[channel]);
+                             &pit->count_load_time[channel], false);
         break;
     case 1:
     case 4:
         /* One-shot timer. */
         TRACE_2D(TRC_HVM_EMUL_PIT_START_TIMER, period, 0);
         create_periodic_time(v, &pit->pt0, period, 0, 0, pit_time_fired,
-                             &pit->count_load_time[channel]);
+                             &pit->count_load_time[channel], false);
         break;
     default:
         TRACE_0D(TRC_HVM_EMUL_PIT_STOP_TIMER);
index cb75b99ed11e7d79e749b57e6ff0b47972d414d6..96921bb5b5e89e68cd2d833f23443248c4fd5949 100644 (file)
@@ -156,7 +156,7 @@ static void rtc_timer_update(RTCState *s)
                 {
                     TRACE_2D(TRC_HVM_EMUL_RTC_START_TIMER, delta, period);
                     create_periodic_time(v, &s->pt, delta, period,
-                                         RTC_IRQ, rtc_pf_callback, s);
+                                         RTC_IRQ, rtc_pf_callback, s, false);
                 }
                 else
                     s->check_ticks_since = now;
index e79ff84f0caf6d8abb3c53923a98bd7c17bdf6d8..fa43d8f13360e123308f019764fa0a78a31062cb 100644 (file)
@@ -762,7 +762,7 @@ static void vlapic_update_timer(struct vlapic *vlapic, uint32_t lvtt,
         create_periodic_time(current, &vlapic->pt, delta,
                              is_periodic ? period : 0, vlapic->pt.irq,
                              is_periodic ? vlapic_pt_cb : NULL,
-                             &vlapic->timer_last_update);
+                             &vlapic->timer_last_update, false);
 
         vlapic->timer_last_update = vlapic->pt.last_plt_gtime;
         if ( !tmict_updated )
@@ -1166,7 +1166,7 @@ void vlapic_tdt_msr_set(struct vlapic *vlapic, uint64_t value)
                         TRC_PAR_LONG(0LL), vlapic->pt.irq);
         create_periodic_time(v, &vlapic->pt, delta, 0,
                              vlapic->pt.irq, vlapic_tdt_pt_cb,
-                             &vlapic->timer_last_update);
+                             &vlapic->timer_last_update, false);
         vlapic->timer_last_update = vlapic->pt.last_plt_gtime;
     }
     else
@@ -1180,7 +1180,7 @@ void vlapic_tdt_msr_set(struct vlapic *vlapic, uint64_t value)
                             TRC_PAR_LONG(0LL), vlapic->pt.irq);
             create_periodic_time(v, &vlapic->pt, 0, 0,
                                  vlapic->pt.irq, vlapic_tdt_pt_cb,
-                                 &vlapic->timer_last_update);
+                                 &vlapic->timer_last_update, false);
             vlapic->timer_last_update = vlapic->pt.last_plt_gtime;
         }
         else
@@ -1423,7 +1423,7 @@ static void lapic_rearm(struct vlapic *s)
                          vlapic_lvtt_period(s) ? period : 0,
                          s->pt.irq,
                          vlapic_lvtt_period(s) ? vlapic_pt_cb : NULL,
-                         &s->timer_last_update);
+                         &s->timer_last_update, false);
     s->timer_last_update = s->pt.last_plt_gtime;
 }
 
index f655457e03c7442739a37937879597d35a225059..6ac4c913bb19aeb4f3ca61911887d43f1d4dd730 100644 (file)
@@ -306,6 +306,7 @@ int pt_update_irq(struct vcpu *v)
     struct periodic_time *pt, *temp, *earliest_pt;
     uint64_t max_lag;
     int irq, pt_vector = -1;
+    bool level;
 
     spin_lock(&v->arch.hvm_vcpu.tm_lock);
 
@@ -316,7 +317,9 @@ int pt_update_irq(struct vcpu *v)
         if ( pt->pending_intr_nr )
         {
             /* RTC code takes care of disabling the timer itself. */
-            if ( (pt->irq != RTC_IRQ || !pt->priv) && pt_irq_masked(pt) )
+            if ( (pt->irq != RTC_IRQ || !pt->priv) && pt_irq_masked(pt) &&
+                 /* Level interrupts should be asserted even if masked. */
+                 !pt->level )
             {
                 /* suspend timer emulation */
                 list_del(&pt->list);
@@ -341,6 +344,7 @@ int pt_update_irq(struct vcpu *v)
 
     earliest_pt->irq_issued = 1;
     irq = earliest_pt->irq;
+    level = earliest_pt->level;
 
     spin_unlock(&v->arch.hvm_vcpu.tm_lock);
 
@@ -374,13 +378,37 @@ int pt_update_irq(struct vcpu *v)
         break;
 
     case PTSRC_ioapic:
-        /*
-         * NB: At the moment IO-APIC routed interrupts generated by vpt devices
-         * (HPET) are edge-triggered.
-         */
-        pt_vector = hvm_ioapic_assert(v->domain, irq, false);
+        pt_vector = hvm_ioapic_assert(v->domain, irq, level);
         if ( pt_vector < 0 || !vlapic_test_irq(vcpu_vlapic(v), pt_vector) )
+        {
             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;
+
+                spin_lock(&v->arch.hvm_vcpu.tm_lock);
+                /* Make sure the timer is still on the list. */
+                list_for_each_entry ( pt, &v->arch.hvm_vcpu.tm_list, list )
+                    if ( pt == earliest_pt )
+                    {
+                        pt_irq_fired(v, pt);
+                        cb = pt->cb;
+                        cb_priv = pt->priv;
+                        break;
+                    }
+                spin_unlock(&v->arch.hvm_vcpu.tm_lock);
+
+                if ( cb != NULL )
+                    cb(v, cb_priv);
+            }
+        }
         break;
     }
 
@@ -447,12 +475,13 @@ void pt_migrate(struct vcpu *v)
 
 void create_periodic_time(
     struct vcpu *v, struct periodic_time *pt, uint64_t delta,
-    uint64_t period, uint8_t irq, time_cb *cb, void *data)
+    uint64_t period, uint8_t irq, time_cb *cb, void *data, bool level)
 {
     if ( !pt->source ||
          (irq >= NR_ISAIRQS && pt->source == PTSRC_isa) ||
-         (irq >= hvm_domain_irq(v->domain)->nr_gsis &&
-          pt->source == PTSRC_ioapic) )
+         (level && period) ||
+         (pt->source == PTSRC_ioapic ? irq >= hvm_domain_irq(v->domain)->nr_gsis
+                                     : level) )
     {
         ASSERT_UNREACHABLE();
         return;
@@ -480,6 +509,7 @@ void create_periodic_time(
     pt->last_plt_gtime = hvm_get_guest_time(pt->vcpu);
     pt->irq = irq;
     pt->one_shot = !period;
+    pt->level = level;
     pt->scheduled = NOW() + delta;
 
     if ( !pt->one_shot )
index f693c0bcf1155e657bc7421071a583d92ea7efbd..61c26ed8b2120385331c351e33fb9d87a9abf87e 100644 (file)
@@ -42,6 +42,7 @@ struct periodic_time {
     bool do_not_freeze;
     bool irq_issued;
     bool warned_timeout_too_short;
+    bool level;
 #define PTSRC_isa    1 /* ISA time source */
 #define PTSRC_lapic  2 /* LAPIC time source */
 #define PTSRC_ioapic 3 /* IOAPIC time source */
@@ -169,7 +170,7 @@ void pt_may_unmask_irq(struct domain *d, struct periodic_time *vlapic_pt);
  */
 void create_periodic_time(
     struct vcpu *v, struct periodic_time *pt, uint64_t delta,
-    uint64_t period, uint8_t irq, time_cb *cb, void *data);
+    uint64_t period, uint8_t irq, time_cb *cb, void *data, bool level);
 void destroy_periodic_time(struct periodic_time *pt);
 
 int pv_pit_handler(int port, int data, int write);