summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/xen/events/events_base.c27
-rw-r--r--drivers/xen/events/events_internal.h3
2 files changed, 24 insertions, 6 deletions
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index cde096a6f11d..cc317739e786 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -461,17 +461,34 @@ static void lateeoi_list_add(struct irq_info *info)
spin_unlock_irqrestore(&eoi->eoi_list_lock, flags);
}
-static void xen_irq_lateeoi_locked(struct irq_info *info)
+static void xen_irq_lateeoi_locked(struct irq_info *info, bool spurious)
{
evtchn_port_t evtchn;
unsigned int cpu;
+ unsigned int delay = 0;
evtchn = info->evtchn;
if (!VALID_EVTCHN(evtchn) || !list_empty(&info->eoi_list))
return;
+ if (spurious) {
+ if ((1 << info->spurious_cnt) < (HZ << 2))
+ info->spurious_cnt++;
+ if (info->spurious_cnt > 1) {
+ delay = 1 << (info->spurious_cnt - 2);
+ if (delay > HZ)
+ delay = HZ;
+ if (!info->eoi_time)
+ info->eoi_cpu = smp_processor_id();
+ info->eoi_time = get_jiffies_64() + delay;
+ }
+ } else {
+ info->spurious_cnt = 0;
+ }
+
cpu = info->eoi_cpu;
- if (info->eoi_time && info->irq_epoch == per_cpu(irq_epoch, cpu)) {
+ if (info->eoi_time &&
+ (info->irq_epoch == per_cpu(irq_epoch, cpu) || delay)) {
lateeoi_list_add(info);
return;
}
@@ -508,7 +525,7 @@ static void xen_irq_lateeoi_worker(struct work_struct *work)
info->eoi_time = 0;
- xen_irq_lateeoi_locked(info);
+ xen_irq_lateeoi_locked(info, false);
}
if (info)
@@ -537,7 +554,7 @@ void xen_irq_lateeoi(unsigned int irq, unsigned int eoi_flags)
info = info_for_irq(irq);
if (info)
- xen_irq_lateeoi_locked(info);
+ xen_irq_lateeoi_locked(info, eoi_flags & XEN_EOI_FLAG_SPURIOUS);
read_unlock_irqrestore(&evtchn_rwlock, flags);
}
@@ -1441,7 +1458,7 @@ int evtchn_get(evtchn_port_t evtchn)
goto done;
err = -EINVAL;
- if (info->refcnt <= 0)
+ if (info->refcnt <= 0 || info->refcnt == SHRT_MAX)
goto done;
info->refcnt++;
diff --git a/drivers/xen/events/events_internal.h b/drivers/xen/events/events_internal.h
index aac05cf52ced..82937d90d7d7 100644
--- a/drivers/xen/events/events_internal.h
+++ b/drivers/xen/events/events_internal.h
@@ -31,7 +31,8 @@ enum xen_irq_type {
struct irq_info {
struct list_head list;
struct list_head eoi_list;
- int refcnt;
+ short refcnt;
+ short spurious_cnt;
enum xen_irq_type type; /* type */
unsigned irq;
evtchn_port_t evtchn; /* event channel */