xen/events: add per-xenbus device event statistics and settings

Add syfs nodes for each xenbus device showing event statistics (number
of events and spurious events, number of associated event channels)
and for setting a spurious event threshold in case a frontend is
sending too many events without being rogue on purpose.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Link: https://lore.kernel.org/r/20210219154030.10892-7-jgross@suse.com
Signed-off-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
This commit is contained in:
Juergen Gross 2021-02-19 16:40:28 +01:00 коммит произвёл Boris Ostrovsky
Родитель 7c70f3a748
Коммит 06f45fe96f
4 изменённых файлов: 139 добавлений и 2 удалений

Просмотреть файл

@ -0,0 +1,41 @@
What: /sys/devices/*/xenbus/event_channels
Date: February 2021
Contact: Xen Developers mailing list <xen-devel@lists.xenproject.org>
Description:
Number of Xen event channels associated with a kernel based
paravirtualized device frontend or backend.
What: /sys/devices/*/xenbus/events
Date: February 2021
Contact: Xen Developers mailing list <xen-devel@lists.xenproject.org>
Description:
Total number of Xen events received for a Xen pv device
frontend or backend.
What: /sys/devices/*/xenbus/jiffies_eoi_delayed
Date: February 2021
Contact: Xen Developers mailing list <xen-devel@lists.xenproject.org>
Description:
Summed up time in jiffies the EOI of an interrupt for a Xen
pv device has been delayed in order to avoid stalls due to
event storms. This value rising is a first sign for a rogue
other end of the pv device.
What: /sys/devices/*/xenbus/spurious_events
Date: February 2021
Contact: Xen Developers mailing list <xen-devel@lists.xenproject.org>
Description:
Number of events received for a Xen pv device which did not
require any action. Too many spurious events in a row will
trigger delayed EOI processing.
What: /sys/devices/*/xenbus/spurious_threshold
Date: February 2021
Contact: Xen Developers mailing list <xen-devel@lists.xenproject.org>
Description:
Controls the tolerated number of subsequent spurious events
before delayed EOI processing is triggered for a Xen pv
device. Default is 1. This can be modified in case the other
end of the pv device is issuing spurious events on a regular
basis and is known not to be malicious on purpose. Raising
the value for such cases can improve pv device performance.

Просмотреть файл

@ -323,6 +323,8 @@ static int xen_irq_info_evtchn_setup(unsigned irq,
ret = xen_irq_info_common_setup(info, irq, IRQT_EVTCHN, evtchn, 0); ret = xen_irq_info_common_setup(info, irq, IRQT_EVTCHN, evtchn, 0);
info->u.interdomain = dev; info->u.interdomain = dev;
if (dev)
atomic_inc(&dev->event_channels);
return ret; return ret;
} }
@ -568,18 +570,28 @@ static void xen_irq_lateeoi_locked(struct irq_info *info, bool spurious)
return; return;
if (spurious) { if (spurious) {
struct xenbus_device *dev = info->u.interdomain;
unsigned int threshold = 1;
if (dev && dev->spurious_threshold)
threshold = dev->spurious_threshold;
if ((1 << info->spurious_cnt) < (HZ << 2)) { if ((1 << info->spurious_cnt) < (HZ << 2)) {
if (info->spurious_cnt != 0xFF) if (info->spurious_cnt != 0xFF)
info->spurious_cnt++; info->spurious_cnt++;
} }
if (info->spurious_cnt > 1) { if (info->spurious_cnt > threshold) {
delay = 1 << (info->spurious_cnt - 2); delay = 1 << (info->spurious_cnt - 1 - threshold);
if (delay > HZ) if (delay > HZ)
delay = HZ; delay = HZ;
if (!info->eoi_time) if (!info->eoi_time)
info->eoi_cpu = smp_processor_id(); info->eoi_cpu = smp_processor_id();
info->eoi_time = get_jiffies_64() + delay; info->eoi_time = get_jiffies_64() + delay;
if (dev)
atomic_add(delay, &dev->jiffies_eoi_delayed);
} }
if (dev)
atomic_inc(&dev->spurious_events);
} else { } else {
info->spurious_cnt = 0; info->spurious_cnt = 0;
} }
@ -908,6 +920,7 @@ static void __unbind_from_irq(unsigned int irq)
if (VALID_EVTCHN(evtchn)) { if (VALID_EVTCHN(evtchn)) {
unsigned int cpu = cpu_from_irq(irq); unsigned int cpu = cpu_from_irq(irq);
struct xenbus_device *dev;
xen_evtchn_close(evtchn); xen_evtchn_close(evtchn);
@ -918,6 +931,11 @@ static void __unbind_from_irq(unsigned int irq)
case IRQT_IPI: case IRQT_IPI:
per_cpu(ipi_to_irq, cpu)[ipi_from_irq(irq)] = -1; per_cpu(ipi_to_irq, cpu)[ipi_from_irq(irq)] = -1;
break; break;
case IRQT_EVTCHN:
dev = info->u.interdomain;
if (dev)
atomic_dec(&dev->event_channels);
break;
default: default:
break; break;
} }
@ -1581,6 +1599,7 @@ void handle_irq_for_port(evtchn_port_t port, struct evtchn_loop_ctrl *ctrl)
{ {
int irq; int irq;
struct irq_info *info; struct irq_info *info;
struct xenbus_device *dev;
irq = get_evtchn_to_irq(port); irq = get_evtchn_to_irq(port);
if (irq == -1) if (irq == -1)
@ -1610,6 +1629,10 @@ void handle_irq_for_port(evtchn_port_t port, struct evtchn_loop_ctrl *ctrl)
info = info_for_irq(irq); info = info_for_irq(irq);
dev = (info->type == IRQT_EVTCHN) ? info->u.interdomain : NULL;
if (dev)
atomic_inc(&dev->events);
if (ctrl->defer_eoi) { if (ctrl->defer_eoi) {
info->eoi_cpu = smp_processor_id(); info->eoi_cpu = smp_processor_id();
info->irq_epoch = __this_cpu_read(irq_epoch); info->irq_epoch = __this_cpu_read(irq_epoch);

Просмотреть файл

@ -206,6 +206,65 @@ void xenbus_otherend_changed(struct xenbus_watch *watch,
} }
EXPORT_SYMBOL_GPL(xenbus_otherend_changed); EXPORT_SYMBOL_GPL(xenbus_otherend_changed);
#define XENBUS_SHOW_STAT(name) \
static ssize_t show_##name(struct device *_dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct xenbus_device *dev = to_xenbus_device(_dev); \
\
return sprintf(buf, "%d\n", atomic_read(&dev->name)); \
} \
static DEVICE_ATTR(name, 0444, show_##name, NULL)
XENBUS_SHOW_STAT(event_channels);
XENBUS_SHOW_STAT(events);
XENBUS_SHOW_STAT(spurious_events);
XENBUS_SHOW_STAT(jiffies_eoi_delayed);
static ssize_t show_spurious_threshold(struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct xenbus_device *dev = to_xenbus_device(_dev);
return sprintf(buf, "%d\n", dev->spurious_threshold);
}
static ssize_t set_spurious_threshold(struct device *_dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct xenbus_device *dev = to_xenbus_device(_dev);
unsigned int val;
ssize_t ret;
ret = kstrtouint(buf, 0, &val);
if (ret)
return ret;
dev->spurious_threshold = val;
return count;
}
static DEVICE_ATTR(spurious_threshold, 0644, show_spurious_threshold,
set_spurious_threshold);
static struct attribute *xenbus_attrs[] = {
&dev_attr_event_channels.attr,
&dev_attr_events.attr,
&dev_attr_spurious_events.attr,
&dev_attr_jiffies_eoi_delayed.attr,
&dev_attr_spurious_threshold.attr,
NULL
};
static const struct attribute_group xenbus_group = {
.name = "xenbus",
.attrs = xenbus_attrs,
};
int xenbus_dev_probe(struct device *_dev) int xenbus_dev_probe(struct device *_dev)
{ {
struct xenbus_device *dev = to_xenbus_device(_dev); struct xenbus_device *dev = to_xenbus_device(_dev);
@ -253,6 +312,11 @@ int xenbus_dev_probe(struct device *_dev)
return err; return err;
} }
dev->spurious_threshold = 1;
if (sysfs_create_group(&dev->dev.kobj, &xenbus_group))
dev_warn(&dev->dev, "sysfs_create_group on %s failed.\n",
dev->nodename);
return 0; return 0;
fail_put: fail_put:
module_put(drv->driver.owner); module_put(drv->driver.owner);
@ -269,6 +333,8 @@ int xenbus_dev_remove(struct device *_dev)
DPRINTK("%s", dev->nodename); DPRINTK("%s", dev->nodename);
sysfs_remove_group(&dev->dev.kobj, &xenbus_group);
free_otherend_watch(dev); free_otherend_watch(dev);
if (drv->remove) { if (drv->remove) {

Просмотреть файл

@ -88,6 +88,13 @@ struct xenbus_device {
struct completion down; struct completion down;
struct work_struct work; struct work_struct work;
struct semaphore reclaim_sem; struct semaphore reclaim_sem;
/* Event channel based statistics and settings. */
atomic_t event_channels;
atomic_t events;
atomic_t spurious_events;
atomic_t jiffies_eoi_delayed;
unsigned int spurious_threshold;
}; };
static inline struct xenbus_device *to_xenbus_device(struct device *dev) static inline struct xenbus_device *to_xenbus_device(struct device *dev)