Commit 06f45fe9 authored by Juergen Gross's avatar Juergen Gross Committed by Boris Ostrovsky
Browse files

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: default avatarJuergen Gross <jgross@suse.com>
Reviewed-by: default avatarBoris Ostrovsky <boris.ostrovsky@oracle.com>
Link: https://lore.kernel.org/r/20210219154030.10892-7-jgross@suse.com


Signed-off-by: default avatarBoris Ostrovsky <boris.ostrovsky@oracle.com>
parent 7c70f3a7
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
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.
+25 −2
Original line number Diff line number Diff line
@@ -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);
	info->u.interdomain = dev;
	if (dev)
		atomic_inc(&dev->event_channels);

	return ret;
}
@@ -568,18 +570,28 @@ static void xen_irq_lateeoi_locked(struct irq_info *info, bool spurious)
		return;

	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 (info->spurious_cnt != 0xFF)
				info->spurious_cnt++;
		}
		if (info->spurious_cnt > 1) {
			delay = 1 << (info->spurious_cnt - 2);
		if (info->spurious_cnt > threshold) {
			delay = 1 << (info->spurious_cnt - 1 - threshold);
			if (delay > HZ)
				delay = HZ;
			if (!info->eoi_time)
				info->eoi_cpu = smp_processor_id();
			info->eoi_time = get_jiffies_64() + delay;
			if (dev)
				atomic_add(delay, &dev->jiffies_eoi_delayed);
		}
		if (dev)
			atomic_inc(&dev->spurious_events);
	} else {
		info->spurious_cnt = 0;
	}
@@ -908,6 +920,7 @@ static void __unbind_from_irq(unsigned int irq)

	if (VALID_EVTCHN(evtchn)) {
		unsigned int cpu = cpu_from_irq(irq);
		struct xenbus_device *dev;

		xen_evtchn_close(evtchn);

@@ -918,6 +931,11 @@ static void __unbind_from_irq(unsigned int irq)
		case IRQT_IPI:
			per_cpu(ipi_to_irq, cpu)[ipi_from_irq(irq)] = -1;
			break;
		case IRQT_EVTCHN:
			dev = info->u.interdomain;
			if (dev)
				atomic_dec(&dev->event_channels);
			break;
		default:
			break;
		}
@@ -1581,6 +1599,7 @@ void handle_irq_for_port(evtchn_port_t port, struct evtchn_loop_ctrl *ctrl)
{
	int irq;
	struct irq_info *info;
	struct xenbus_device *dev;

	irq = get_evtchn_to_irq(port);
	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);

	dev = (info->type == IRQT_EVTCHN) ? info->u.interdomain : NULL;
	if (dev)
		atomic_inc(&dev->events);

	if (ctrl->defer_eoi) {
		info->eoi_cpu = smp_processor_id();
		info->irq_epoch = __this_cpu_read(irq_epoch);
+66 −0
Original line number Diff line number Diff line
@@ -206,6 +206,65 @@ void xenbus_otherend_changed(struct xenbus_watch *watch,
}
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)
{
	struct xenbus_device *dev = to_xenbus_device(_dev);
@@ -253,6 +312,11 @@ int xenbus_dev_probe(struct device *_dev)
		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;
fail_put:
	module_put(drv->driver.owner);
@@ -269,6 +333,8 @@ int xenbus_dev_remove(struct device *_dev)

	DPRINTK("%s", dev->nodename);

	sysfs_remove_group(&dev->dev.kobj, &xenbus_group);

	free_otherend_watch(dev);

	if (drv->remove) {
+7 −0
Original line number Diff line number Diff line
@@ -88,6 +88,13 @@ struct xenbus_device {
	struct completion down;
	struct work_struct work;
	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)