lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:	Tue, 28 Jul 2015 18:03:23 +0100
From:	Martyn Welch <martyn.welch@...com>
To:	Dmitry Kalinkin <dmitry.kalinkin@...il.com>,
	<linux-kernel@...r.kernel.org>, <devel@...verdev.osuosl.org>
CC:	Manohar Vanga <manohar.vanga@...il.com>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	"Hans J. Koch" <hjk@...sjkoch.de>,
	"Igor Alekseev" <igor.alekseev@...p.ru>
Subject: Re: [RFC] Generic VME UIO

Hi Dmitry,

On 22/07/15 19:09, Dmitry Kalinkin wrote:
> Linux kernel has supported VME bus since 2009. The support comes in a form
> of kernel driver API that is backed by a couple drivers for PCI-VME
> bridges.  There is also a vme_user driver that provides a generic userpsace
> interface to do data transfer and generate interrupts on the bus. Due to
> specifics of the VME bus, this interface doesn't fit into the UIO
> framework. The other useful feature is to be able to receive VME interrupts
> in the userspace can, on the other hand, benefit from reusing UIO.
>
> VME bus offers seven interrupt request lines IRQ1-IRQ7 corresponding to
> seven interrupt levels.  In the event of interrupt, the interrupt handler
> board is to prioritize interrupts in accordance to their levels.  The
> interrupt handler then takes ownership over the data bus to perform an
> interrupt acknowledge cycle where it supplies an interrupt level to be
> acknowledged. When multiple interrupters are producing interrupt at the
> same level, only one interrupt gets acknowledged based on interrupters
> position in IACKIN/IACKOUT daisy- chain.  The response of the interrupter
> to a relevant interrupt will contain a 8, 16 or 32 bit interrupt vector
> (also called STATUS/ID). After the interrupt acknowledge cycle, the
> interrupter is to remove it's interrupt request from the bus. Such
> standartized scheme is called "Release On AcKnowledge" (ROAK).
>
> Like PCI, VME has it's own corner case where interrupt request is removed
> on VME device register access. VME specification acknowledges this
> behaviour and calls it "Release On Register Access" (RORA) and requires
> RORA devices to still provide interrupt vector value in acknowledge cycles.
> I'm not aware how widespread the RORA devices are.
>

 From memory all the hardware I've seen is RORA, but from talking to 
you, it seems you've had the opposite experience, so I suspect it might 
be quite market dependent.

> The driver below provides a generic userspace interface to handle ROAK VME
> device interrupts.
>
> The user is to enable interrupt vectors through a sysfs interface. For
> example, enabling handler for interrupt vector 0x6b at the level 1 will
> look like:
>
>    echo 1 > /sys/bus/vme/devices/vme_uio.0-0/irq/1/6b/enabled
>
> A separate UIO device is created for each handler. Waiting for event can be
> done as:
>
>    dd if=/dev/uio0 bs=4 count=1
>
> In response for this RFC I would like to hear your comments or suggestions
> on the proposed sysfs interface, about the idea in general. Some tips on
> how to better handle kobject cleanup are also very welcome.
>

Looks like a good start to me, please provide some documentation under 
Documentation/ before submitting (and please clearly state that it will 
only work for ROAK hardware!).

> The vme_uio driver is provided separately for the ease of review, it's code
> is intended for the merge into vme_user.
>
> Signed-off-by: Dmitry Kalinkin <dmitry.kalinkin@...il.com>
> Cc: Igor Alekseev <igor.alekseev@...p.ru>
>
> ---
>   drivers/staging/vme/devices/Kconfig   |  10 +++
>   drivers/staging/vme/devices/Makefile  |   1 +
>   drivers/staging/vme/devices/vme_uio.c | 158 ++++++++++++++++++++++++++++++++++
>   drivers/vme/vme_bridge.h              |   4 +-
>   include/linux/vme.h                   |   3 +
>   5 files changed, 175 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/staging/vme/devices/vme_uio.c
>
> diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
> index 1d2ff0c..0300226 100644
> --- a/drivers/staging/vme/devices/Kconfig
> +++ b/drivers/staging/vme/devices/Kconfig
> @@ -1,5 +1,15 @@
>   comment "VME Device Drivers"
>
> +config VME_UIO
> +	tristate "VME UIO user space access driver"
> +	depends on STAGING && VME_BUS && UIO
> +	help
> +	 Say Y here to include UIO interface to VME. This module currently
> +	 allows you to deliver VME interrupts to user space.
> +
> +	 To compile this driver as a module, choose M here. The module will
> +	 be called vme_uio. If unsure, say N.
> +
>   config VME_USER
>   	tristate "VME user space access driver"
>   	depends on STAGING
> diff --git a/drivers/staging/vme/devices/Makefile b/drivers/staging/vme/devices/Makefile
> index 172512c..c198004 100644
> --- a/drivers/staging/vme/devices/Makefile
> +++ b/drivers/staging/vme/devices/Makefile
> @@ -2,6 +2,7 @@
>   # Makefile for the VME device drivers.
>   #
>
> +obj-$(CONFIG_VME_UIO)		+= vme_uio.o
>   obj-$(CONFIG_VME_USER)		+= vme_user.o
>
>   vme_pio2-objs	:= vme_pio2_cntr.o vme_pio2_gpio.o vme_pio2_core.o
> diff --git a/drivers/staging/vme/devices/vme_uio.c b/drivers/staging/vme/devices/vme_uio.c
> new file mode 100644
> index 0000000..4c55a23
> --- /dev/null
> +++ b/drivers/staging/vme/devices/vme_uio.c
> @@ -0,0 +1,158 @@
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/uio_driver.h>
> +#include <linux/vme.h>
> +
> +static void vme_uio_int(int level, int status_id, void *priv)
> +{
> +	struct uio_info *info = priv;
> +
> +	uio_event_notify(info);
> +}
> +
> +struct int_sysfs_entry {
> +	struct kobj_attribute kobj_attr;
> +	struct vme_dev *vdev;
> +	struct uio_info uio;
> +	int level;
> +	int statid;
> +	int enabled;
> +};
> +
> +static ssize_t int_enabled_show(struct kobject *kobj,
> +				struct kobj_attribute *attr, char *buf)
> +{
> +	struct int_sysfs_entry *entry;
> +
> +	entry = container_of(attr, struct int_sysfs_entry, kobj_attr);
> +	return sprintf(buf, "%d\n", entry->enabled);
> +}
> +
> +static ssize_t int_enabled_store(struct kobject *kobj,
> +				 struct kobj_attribute *attr, const char *buf,
> +				 size_t count)
> +{
> +	int enabled;
> +	struct int_sysfs_entry *entry;
> +	int ret;
> +
> +	entry = container_of(attr, struct int_sysfs_entry, kobj_attr);
> +
> +	ret = kstrtoint(buf, 0, &enabled);
> +	if (ret)
> +		return ret;
> +	enabled = !!enabled;
> +
> +	if (enabled == entry->enabled)
> +		return count;
> +
> +	if (enabled) {
> +		ret = uio_register_device(&entry->vdev->dev, &entry->uio);
> +		if (ret) {
> +			enabled = 0;
> +			return ret;
> +		}
> +
> +		ret = vme_irq_request(entry->vdev, entry->level, entry->statid,
> +				      vme_uio_int, &entry->uio);
> +		if (ret) {

You need a uio_unregister_device() here, or create a exit path and jump 
to it with goto (which could also be used for disabling an interrupt I 
suppose).

> +			enabled = 0;
> +			return ret;
> +		}
> +	} else {
> +		vme_irq_free(entry->vdev, entry->level, entry->statid);
> +
> +		uio_unregister_device(&entry->uio);
> +	}
> +
> +	entry->enabled = enabled;
> +
> +	return count;
> +}
> +
> +static struct kobj_attribute int_enabled_attribute =
> +	__ATTR(enabled, 0644, int_enabled_show, int_enabled_store);
> +
> +static int vme_uio_match(struct vme_dev *vdev)
> +{
> +	return 1;
> +}
> +
> +static int vme_uio_probe(struct vme_dev *vdev)
> +{
> +	int ret, level, statid;
> +
> +	int bus_num = vme_bus_num(vdev);
> +
> +	struct kobject *kobj = kobject_create_and_add("irq", &vdev->dev.kobj);
> +
> +	for (level = 1; level <= 7; level++) {
> +		char *level_node_name = kasprintf(GFP_KERNEL, "%d", level);
> +		struct kobject *level_node = kobject_create_and_add(
> +			level_node_name, kobj);
> +		if (!level_node)
> +			return -ENOMEM;
> +
> +		for (statid = 0; statid < VME_NUM_STATUSID; statid++) {
> +			char *statid_node_name = kasprintf(GFP_KERNEL,
> +							   "%02x", statid);
> +			struct kobject *statid_node;
> +			struct int_sysfs_entry *entry;
> +
> +			statid_node = kobject_create_and_add(statid_node_name,
> +							     level_node);
> +			if (!statid_node)
> +				return -ENOMEM;
> +
> +			entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +			if (!entry)
> +				return -ENOMEM;
> +			entry->uio.name = kasprintf(GFP_KERNEL,
> +						    "vme_irq_%d_%d_%02x",
> +						    bus_num, level, statid);
> +			entry->uio.version = "1";
> +			entry->uio.irq = UIO_IRQ_CUSTOM;
> +			entry->level = level;
> +			entry->statid = statid;
> +			entry->vdev = vdev;
> +			entry->enabled = 0;
> +			entry->kobj_attr = int_enabled_attribute;
> +			ret = sysfs_create_file(statid_node,
> +						&entry->kobj_attr.attr);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int vme_uio_remove(struct vme_dev *vdev)
> +{
> +	/* XXX Cleanup here */
> +	return 0;
> +}
> +
> +static struct vme_driver vme_uio_driver = {
> +	.name = "vme_uio",
> +	.match = vme_uio_match,
> +	.probe = vme_uio_probe,
> +	.remove = vme_uio_remove,
> +};
> +
> +static int __init vme_uio_init(void)
> +{
> +	return vme_register_driver(&vme_uio_driver, 1);
> +}
> +
> +static void __exit vme_uio_exit(void)
> +{
> +	vme_unregister_driver(&vme_uio_driver);
> +}
> +
> +module_init(vme_uio_init);
> +module_exit(vme_uio_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Dmitry Kalinkin <dmitry.kalinkin@...il.com>");
> diff --git a/drivers/vme/vme_bridge.h b/drivers/vme/vme_bridge.h
> index 37d2fd7..a3ef63b 100644
> --- a/drivers/vme/vme_bridge.h
> +++ b/drivers/vme/vme_bridge.h
> @@ -1,6 +1,8 @@
>   #ifndef _VME_BRIDGE_H_
>   #define _VME_BRIDGE_H_
>
> +#include <linux/vme.h>
> +
>   #define VME_CRCSR_BUF_SIZE (508*1024)
>   /*
>    * Resource structures
> @@ -88,7 +90,7 @@ struct vme_callback {
>
>   struct vme_irq {
>   	int count;
> -	struct vme_callback callback[256];
> +	struct vme_callback callback[VME_NUM_STATUSID];
>   };
>
>   /* Allow 16 characters for name (including null character) */
> diff --git a/include/linux/vme.h b/include/linux/vme.h
> index c013135..71e4a6d 100644
> --- a/include/linux/vme.h
> +++ b/include/linux/vme.h
> @@ -81,6 +81,9 @@ struct vme_resource {
>
>   extern struct bus_type vme_bus_type;
>
> +/* Number of VME interrupt vectors */
> +#define VME_NUM_STATUSID	256
> +
>   /* VME_MAX_BRIDGES comes from the type of vme_bus_numbers */
>   #define VME_MAX_BRIDGES		(sizeof(unsigned int)*8)
>   #define VME_MAX_SLOTS		32
>

-- 
Martyn Welch (Lead Software Engineer)  | Registered in England and Wales
GE Intelligent Platforms               | (3828642) at 100 Barbirolli Square
T +44(0)1327322748                     | Manchester, M2 3AB
E martyn.welch@...com                  | VAT:GB 927559189
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists