@@ -33,6 +33,7 @@
#include "hw/sysbus.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/timer/i8254.h"
+#include "sysemu/kvm.h"
//#define HPET_DEBUG
#ifdef HPET_DEBUG
@@ -68,6 +69,12 @@ typedef struct HPETTimer { /* timers */
*/
} HPETTimer;
+typedef struct IrqfdInfo {
+ EventNotifier *n;
+ EventNotifier *rn;
+ int gsi;
+} IrqfdInfo;
+
typedef struct HPETState {
/*< private >*/
SysBusDevice parent_obj;
@@ -76,12 +83,14 @@ typedef struct HPETState {
MemoryRegion iomem;
uint64_t hpet_offset;
qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
+ IrqfdInfo irqfds[HPET_NUM_IRQ_ROUTES];
uint32_t flags;
uint8_t rtc_irq_level;
qemu_irq pit_enabled;
uint8_t num_timers;
uint32_t intcap;
HPETTimer timer[HPET_MAX_TIMERS];
+ bool dedicate_mode;
/* Memory-mapped, software visible registers */
uint64_t capability; /* capabilities */
@@ -206,14 +215,19 @@ static void update_irq(struct HPETTimer *timer, int set)
if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
s->isr &= ~mask;
if (!timer_fsb_route(timer)) {
- /* fold the ICH PIRQ# pin's internal inversion logic into hpet */
- if (route >= ISA_NUM_IRQS) {
- qemu_irq_raise(s->irqs[route]);
+ if (s->dedicate_mode && s->irqfds[timer->tn].n) {
+ event_notifier_set(s->irqfds[timer->tn].n);
} else {
- qemu_irq_lower(s->irqs[route]);
+ /* fold the ICH PIRQ# pin's internal inversion logic into hpet */
+ if (route >= ISA_NUM_IRQS) {
+ qemu_irq_raise(s->irqs[route]);
+ } else {
+ qemu_irq_lower(s->irqs[route]);
+ }
}
}
} else if (timer_fsb_route(timer)) {
+ /* For the case of fsb, we resort to the lock in mem dispatcher */
stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff);
} else if (timer->config & HPET_TN_TYPE_LEVEL) {
s->isr |= mask;
@@ -479,6 +493,32 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
return 0;
}
+static void timer_assign_irqfd(HPETState *s, HPETTimer *timer, bool assign)
+{
+ int irqnum = (timer->config & HPET_TN_INT_ROUTE_MASK)
+ >> HPET_TN_INT_ROUTE_SHIFT;
+ int level = timer->fsb & HPET_TN_TYPE_LEVEL;
+ IrqfdInfo *info = &s->irqfds[timer->tn];
+
+ if (assign) {
+ if (!info->n) {
+ info->n = g_new0(EventNotifier, 1);
+ info->rn = g_new0(EventNotifier, 1);
+ event_notifier_init(info->n, 0);
+ event_notifier_init(info->rn, 0);
+ }
+ info->gsi = qemu_irq_route_gsi(s->irqs[irqnum]);
+ kvm_irqchip_add_irqfd_notifier(kvm_state, info->n,
+ level ? info->rn : NULL, info->gsi, 1);
+ } else {
+ kvm_irqchip_remove_irqfd_notifier(kvm_state, info->n, info->gsi);
+ g_free(info->n);
+ g_free(info->rn);
+ info->n = NULL;
+ info->rn = NULL;
+ }
+}
+
static void hpet_ram_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
@@ -514,8 +554,14 @@ static void hpet_ram_write(void *opaque, hwaddr addr,
timer->period = (uint32_t)timer->period;
}
if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) {
+ if (s->dedicate_mode) {
+ timer_assign_irqfd(s, timer, true);
+ }
hpet_set_timer(timer);
} else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
+ if (s->dedicate_mode) {
+ timer_assign_irqfd(s, timer, false);
+ }
hpet_del_timer(timer);
}
break;
Running hpet in iothread, there could be variable payload, which will finally affect the accurate of timing. So we want to run hpet on dedicated thread. For hpet, almost of the things can run out of BQL, except interrupt. We step around interrupt by using irqfd. Signed-off-by: Liu Ping Fan <pingfank@linux.vnet.ibm.com> --- hw/timer/hpet.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-)