summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuergen Gross <jgross@suse.com>2020-09-14 14:01:02 +0200
committerJuergen Gross <jgross@suse.com>2020-10-20 10:22:19 +0200
commit5f7f77400ab5b357b5fdb7122c3442239672186c (patch)
tree97e21b64edbc6345af5aec7658a358200e80cddb
parente99502f76271d6bc4e374fe368c50c67a1fd3070 (diff)
xen/events: block rogue events for some time
In order to avoid high dom0 load due to rogue guests sending events at high frequency, block those events in case there was no action needed in dom0 to handle the events. This is done by adding a per-event counter, which set to zero in case an EOI without the XEN_EOI_FLAG_SPURIOUS is received from a backend driver, and incremented when this flag has been set. In case the counter is 2 or higher delay the EOI by 1 << (cnt - 2) jiffies, but not more than 1 second. In order not to waste memory shorten the per-event refcnt to two bytes (it should normally never exceed a value of 2). Add an overflow check to evtchn_get() to make sure the 2 bytes really won't overflow. This is part of XSA-332. Cc: stable@vger.kernel.org Signed-off-by: Juergen Gross <jgross@suse.com> Reviewed-by: Jan Beulich <jbeulich@suse.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Reviewed-by: Wei Liu <wl@xen.org>
-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 */