[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20120312223254.30321.74405.stgit@bling.home>
Date: Mon, 12 Mar 2012 16:32:54 -0600
From: Alex Williamson <alex.williamson@...hat.com>
To: david@...son.dropbear.id.au, aik@...abs.ru, dwmw2@...radead.org
Cc: alex.williamson@...hat.com, iommu@...ts.linux-foundation.org,
benh@...nel.crashing.org, qemu-devel@...gnu.org,
joerg.roedel@....com, kvm@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH 1/2] Isolation groups
Signed-off-by: Alex Williamson <alex.williamson@...hat.com>
---
drivers/base/Kconfig | 10 +
drivers/base/Makefile | 1
drivers/base/base.h | 5
drivers/base/isolation.c | 798 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/device.h | 4
include/linux/isolation.h | 138 ++++++++
6 files changed, 956 insertions(+), 0 deletions(-)
create mode 100644 drivers/base/isolation.c
create mode 100644 include/linux/isolation.h
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 7be9f79..e98a5f3 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -189,4 +189,14 @@ config DMA_SHARED_BUFFER
APIs extension; the file's descriptor can then be passed on to other
driver.
+config ISOLATION_GROUPS
+ bool "Enable Isolation Group API"
+ default n
+ depends on EXPERIMENTAL && IOMMU_API
+ help
+ This option enables grouping of devices into Isolation Groups
+ which may be used by other subsystems to perform quirks across
+ sets of devices as well as userspace drivers for guaranteeing
+ devices are isolated from the rest of the system.
+
endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 610f999..047b5f9 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_MODULES) += module.o
endif
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
obj-$(CONFIG_REGMAP) += regmap/
+obj-$(CONFIG_ISOLATION_GROUPS) += isolation.o
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/base.h b/drivers/base/base.h
index b858dfd..376758a 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -1,4 +1,5 @@
#include <linux/notifier.h>
+#include <linux/isolation.h>
/**
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
@@ -108,6 +109,10 @@ extern int driver_probe_device(struct device_driver *drv, struct device *dev);
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
+ if (isolation_group_managed(to_isolation_group(dev)) &&
+ !isolation_group_manager_driver(to_isolation_group(dev), drv))
+ return 0;
+
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
diff --git a/drivers/base/isolation.c b/drivers/base/isolation.c
new file mode 100644
index 0000000..c01365c
--- /dev/null
+++ b/drivers/base/isolation.c
@@ -0,0 +1,798 @@
+/*
+ * Isolation group interface
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@...hat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/iommu.h>
+#include <linux/isolation.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/uuid.h>
+
+static struct kset *isolation_kset;
+/* XXX add more complete locking, maybe rcu */
+static DEFINE_MUTEX(isolation_lock);
+static LIST_HEAD(isolation_groups);
+static LIST_HEAD(isolation_notifiers);
+
+/* Keep these private */
+struct isolation_manager_driver {
+ struct device_driver *drv;
+ struct list_head list;
+};
+
+struct isolation_manager {
+ struct list_head drivers;
+ /* Likely need managers to register some callbacks */
+};
+
+struct isolation_group {
+ struct list_head list;
+ struct list_head devices;
+ struct kobject kobj;
+ struct kobject *devices_kobj;
+ struct iommu_domain *iommu_domain;
+ struct iommu_ops *iommu_ops;
+ struct isolation_manager *manager;
+ uuid_le uuid;
+};
+
+struct isolation_device {
+ struct device *dev;
+ struct list_head list;
+};
+
+struct isolation_notifier {
+ struct bus_type *bus;
+ struct notifier_block nb;
+ struct blocking_notifier_head notifier;
+ struct list_head list;
+ int refcnt;
+};
+
+struct iso_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct isolation_group *group, char *buf);
+ ssize_t (*store)(struct isolation_group *group,
+ const char *buf, size_t count);
+};
+
+#define to_iso_attr(_attr) container_of(_attr, struct iso_attribute, attr)
+#define to_iso_group(_kobj) container_of(_kobj, struct isolation_group, kobj)
+
+static ssize_t iso_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct iso_attribute *iso_attr = to_iso_attr(attr);
+ struct isolation_group *group = to_iso_group(kobj);
+ ssize_t ret = -EIO;
+
+ if (iso_attr->show)
+ ret = iso_attr->show(group, buf);
+ return ret;
+}
+
+static ssize_t iso_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iso_attribute *iso_attr = to_iso_attr(attr);
+ struct isolation_group *group = to_iso_group(kobj);
+ ssize_t ret = -EIO;
+
+ if (iso_attr->store)
+ ret = iso_attr->store(group, buf, count);
+ return ret;
+}
+
+static void iso_release(struct kobject *kobj)
+{
+ struct isolation_group *group = to_iso_group(kobj);
+ kfree(group);
+}
+
+static const struct sysfs_ops iso_sysfs_ops = {
+ .show = iso_attr_show,
+ .store = iso_attr_store,
+};
+
+static struct kobj_type iso_ktype = {
+ .sysfs_ops = &iso_sysfs_ops,
+ .release = iso_release,
+};
+
+/*
+ * Create an isolation group. Isolation group "providers" register a
+ * notifier for their bus(es) and call this to create a new isolation
+ * group.
+ */
+struct isolation_group *isolation_create_group(void)
+{
+ struct isolation_group *group, *tmp;
+ int ret;
+
+ if (unlikely(!isolation_kset))
+ return ERR_PTR(-ENODEV);
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_lock(&isolation_lock);
+
+ /*
+ * Use a UUID for group identification simply because it seems wrong
+ * to expose it as a kernel pointer (%p). Neither is user friendly.
+ * Mostly only expect userspace to do anything with this.
+ */
+new_uuid:
+ uuid_le_gen(&group->uuid);
+
+ /* Unlikely, but nothing prevents duplicates */
+ list_for_each_entry(tmp, &isolation_groups, list)
+ if (memcmp(&group->uuid, &tmp->uuid, sizeof(group->uuid)) == 0)
+ goto new_uuid;
+
+ INIT_LIST_HEAD(&group->devices);
+ group->kobj.kset = isolation_kset;
+
+ ret = kobject_init_and_add(&group->kobj, &iso_ktype, NULL,
+ "%pUl", &group->uuid);
+ if (ret) {
+ kfree(group);
+ mutex_unlock(&isolation_lock);
+ return ERR_PTR(ret);
+ }
+
+ /* Add a subdirectory to save links for devices withing the group. */
+ group->devices_kobj = kobject_create_and_add("devices", &group->kobj);
+ if (!group->devices_kobj) {
+ kobject_put(&group->kobj);
+ mutex_unlock(&isolation_lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ list_add(&group->list, &isolation_groups);
+
+ mutex_unlock(&isolation_lock);
+
+ return group;
+}
+
+/*
+ * Free isolation group. XXX need to cleanup/reject based on manager status.
+ */
+int isolation_free_group(struct isolation_group *group)
+{
+ mutex_lock(&isolation_lock);
+
+ if (!list_empty(&group->devices)) {
+ mutex_unlock(&isolation_lock);
+ return -EBUSY;
+ }
+
+ list_del(&group->list);
+ kobject_put(group->devices_kobj);
+ kobject_put(&group->kobj);
+
+ mutex_unlock(&isolation_lock);
+ return 0;
+}
+
+/*
+ * Add a device to an isolation group. Isolation groups start empty and
+ * must be told about the devices they contain. Expect this to be called
+ * from isolation group providers via notifier.
+ */
+int isolation_group_add_dev(struct isolation_group *group, struct device *dev)
+{
+ struct isolation_device *device;
+ int ret = 0;
+
+ mutex_lock(&isolation_lock);
+
+ if (dev->isolation_group) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ device = kzalloc(sizeof(*device), GFP_KERNEL);
+ if (!device) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ device->dev = dev;
+
+ /* Cross link the device in sysfs */
+ ret = sysfs_create_link(&dev->kobj, &group->kobj,
+ "isolation_group");
+ if (ret) {
+ kfree(device);
+ goto out;
+ }
+
+ ret = sysfs_create_link(group->devices_kobj, &dev->kobj,
+ kobject_name(&dev->kobj));
+ if (ret) {
+ sysfs_remove_link(&dev->kobj, "isolation_group");
+ kfree(device);
+ goto out;
+ }
+
+ list_add(&device->list, &group->devices);
+
+ dev->isolation_group = group;
+
+ if (group->iommu_domain) {
+ ret = group->iommu_ops->attach_dev(group->iommu_domain, dev);
+ if (ret) {
+ /* XXX fail device? */
+ }
+ }
+
+ /* XXX signal manager? */
+out:
+ mutex_unlock(&isolation_lock);
+
+ return 0;
+}
+
+/*
+ * Remove a device from an isolation group, likely because the device
+ * went away.
+ */
+int isolation_group_del_dev(struct device *dev)
+{
+ struct isolation_group *group = dev->isolation_group;
+ struct isolation_device *device;
+
+ if (!group)
+ return -ENODEV;
+
+ mutex_lock(&isolation_lock);
+
+ list_for_each_entry(device, &group->devices, list) {
+ if (device->dev == dev) {
+ /* XXX signal manager? */
+
+ if (group->iommu_domain)
+ group->iommu_ops->detach_dev(
+ group->iommu_domain, dev);
+ list_del(&device->list);
+ kfree(device);
+ dev->isolation_group = NULL;
+ sysfs_remove_link(group->devices_kobj,
+ kobject_name(&dev->kobj));
+ sysfs_remove_link(&dev->kobj, "isolation_group");
+ break;
+ }
+ }
+
+ mutex_unlock(&isolation_lock);
+
+ return 0;
+}
+
+/*
+ * Filter and call out to our own notifier chains
+ */
+static int isolation_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct isolation_notifier *notifier =
+ container_of(nb, struct isolation_notifier, nb);
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (!dev->isolation_group)
+ blocking_notifier_call_chain(¬ifier->notifier,
+ ISOLATION_NOTIFY_ADD_DEVICE, dev);
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (dev->isolation_group)
+ blocking_notifier_call_chain(¬ifier->notifier,
+ ISOLATION_NOTIFY_DEL_DEVICE, dev);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Wrapper for manual playback of BUS_NOTIFY_ADD_DEVICE when we add
+ * a new notifier.
+ */
+static int isolation_do_add_notify(struct device *dev, void *data)
+{
+ struct notifier_block *nb = data;
+
+ if (!dev->isolation_group)
+ nb->notifier_call(nb, ISOLATION_NOTIFY_ADD_DEVICE, dev);
+
+ return 0;
+}
+
+/*
+ * Register a notifier. This is for isolation group providers. It's
+ * possible we could have multiple isolation group providers for the same
+ * bus type, so we create a struct isolation_notifier per bus_type, each
+ * with a notifier block. Providers are therefore welcome to register
+ * as many buses as they can handle.
+ */
+int isolation_register_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+ struct isolation_notifier *notifier;
+ bool found = false;
+ int ret;
+
+ mutex_lock(&isolation_lock);
+ list_for_each_entry(notifier, &isolation_notifiers, list) {
+ if (notifier->bus == bus) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
+ if (!notifier) {
+ mutex_unlock(&isolation_lock);
+ return -ENOMEM;
+ }
+ notifier->bus = bus;
+ notifier->nb.notifier_call = isolation_bus_notify;
+ BLOCKING_INIT_NOTIFIER_HEAD(¬ifier->notifier);
+ bus_register_notifier(bus, ¬ifier->nb);
+ list_add(¬ifier->list, &isolation_notifiers);
+ }
+
+ ret = blocking_notifier_chain_register(¬ifier->notifier, nb);
+ if (ret) {
+ if (notifier->refcnt == 0) {
+ bus_unregister_notifier(bus, ¬ifier->nb);
+ list_del(¬ifier->list);
+ kfree(notifier);
+ }
+ mutex_unlock(&isolation_lock);
+ return ret;
+ }
+ notifier->refcnt++;
+
+ mutex_unlock(&isolation_lock);
+
+ bus_for_each_dev(bus, NULL, nb, isolation_do_add_notify);
+
+ return 0;
+}
+
+/*
+ * Unregister...
+ */
+int isolation_unregister_notifier(struct bus_type *bus,
+ struct notifier_block *nb)
+{
+ struct isolation_notifier *notifier;
+ bool found = false;
+ int ret;
+
+ mutex_lock(&isolation_lock);
+ list_for_each_entry(notifier, &isolation_notifiers, list) {
+ if (notifier->bus == bus) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ mutex_unlock(&isolation_lock);
+ return -ENODEV;
+ }
+
+ ret = blocking_notifier_chain_unregister(¬ifier->notifier, nb);
+ if (ret) {
+ mutex_unlock(&isolation_lock);
+ return ret;
+ }
+
+ /* Free at nobody left in the notifier block */
+ if (--notifier->refcnt == 0) {
+ bus_unregister_notifier(bus, ¬ifier->nb);
+ list_del(¬ifier->list);
+ kfree(notifier);
+ }
+
+ mutex_unlock(&isolation_lock);
+
+ return 0;
+}
+
+/*
+ * Set iommu_ops on an isolation group. Hopefully all DMA will eventually
+ * be done through isolation groups, for now, we just provide another
+ * interface to the iommu api.
+ */
+int isolation_group_set_iommu_ops(struct isolation_group *group,
+ struct iommu_ops *iommu_ops)
+{
+ mutex_lock(&isolation_lock);
+
+ if (group->iommu_ops) {
+ mutex_unlock(&isolation_lock);
+ return -EBUSY;
+ }
+
+ group->iommu_ops = iommu_ops;
+
+ mutex_unlock(&isolation_lock);
+
+ return 0;
+}
+
+/*
+ * Attach all the devices for a group to the specified iommu domain.
+ */
+static int __isolation_group_domain_attach_devs(struct iommu_domain *domain,
+ struct list_head *devices)
+{
+ struct isolation_device *device;
+ struct device *dev;
+ int ret = 0;
+
+ list_for_each_entry(device, devices, list) {
+ dev = device->dev;
+
+ ret = domain->ops->attach_dev(domain, dev);
+ if (ret) {
+ list_for_each_entry_continue_reverse(device,
+ devices, list) {
+ dev = device->dev;
+ domain->ops->detach_dev(domain, dev);
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * And detach...
+ */
+static void __isolation_group_domain_detach_devs(struct iommu_domain *domain,
+ struct list_head *devices)
+{
+ struct isolation_device *device;
+ struct device *dev;
+
+ list_for_each_entry(device, devices, list) {
+ dev = device->dev;
+
+ domain->ops->detach_dev(domain, dev);
+ }
+}
+
+/*
+ * Initialize the iommu domain for an isolation group. This is to ease the
+ * transition to using isolation groups and expected to be used by current
+ * users of the iommu api for now.
+ */
+int isolation_group_init_iommu_domain(struct isolation_group *group)
+{
+ int ret;
+
+ mutex_lock(&isolation_lock);
+
+ if (!group->iommu_ops || list_empty(&group->devices)) {
+ mutex_unlock(&isolation_lock);
+ return -EINVAL;
+ }
+
+ if (group->iommu_domain) {
+ mutex_unlock(&isolation_lock);
+ return -EBUSY;
+ }
+
+ group->iommu_domain = kzalloc(sizeof(struct iommu_domain), GFP_KERNEL);
+ if (!group->iommu_domain) {
+ mutex_unlock(&isolation_lock);
+ return -ENOMEM;
+ }
+
+ group->iommu_domain->ops = group->iommu_ops;
+
+ ret = group->iommu_ops->domain_init(group->iommu_domain);
+ if (ret) {
+ kfree(group->iommu_domain);
+ group->iommu_domain = NULL;
+ mutex_unlock(&isolation_lock);
+ return ret;
+ }
+
+ /* Automatically attach all the devices in the group. */
+ ret = __isolation_group_domain_attach_devs(group->iommu_domain,
+ &group->devices);
+ if (ret) {
+ group->iommu_ops->domain_destroy(group->iommu_domain);
+ kfree(group->iommu_domain);
+ group->iommu_domain = NULL;
+ mutex_unlock(&isolation_lock);
+ return ret;
+ }
+
+ mutex_unlock(&isolation_lock);
+ return 0;
+}
+
+/*
+ * And free...
+ * Note just below, we add the ability to add another group to an iommu
+ * domain, so we don't always destroy and free the domain here.
+ */
+void isolation_group_free_iommu_domain(struct isolation_group *group)
+{
+ struct isolation_group *tmp;
+ bool inuse = false;
+
+ if (!group->iommu_domain || !group->iommu_ops)
+ return;
+
+ mutex_lock(&isolation_lock);
+
+ __isolation_group_domain_detach_devs(group->iommu_domain,
+ &group->devices);
+
+ list_for_each_entry(tmp, &isolation_groups, list) {
+ if (tmp == group)
+ continue;
+ if (tmp->iommu_domain == group->iommu_domain) {
+ inuse = true;
+ break;
+ }
+ }
+
+ if (!inuse) {
+ group->iommu_ops->domain_destroy(group->iommu_domain);
+ kfree(group->iommu_domain);
+ }
+
+ group->iommu_domain = NULL;
+
+ mutex_unlock(&isolation_lock);
+}
+
+/*
+ * This handles the VFIO "merge" interface, allowing us to manage multiple
+ * isolation groups with a single domain. We just rely on attach_dev to
+ * tell us whether this is ok.
+ */
+int isolation_group_iommu_domain_add_group(struct isolation_group *groupa,
+ struct isolation_group *groupb)
+{
+ int ret;
+
+ if (!groupa->iommu_domain ||
+ groupb->iommu_domain || list_empty(&groupb->devices))
+ return -EINVAL;
+
+ if (groupa->iommu_ops != groupb->iommu_ops)
+ return -EIO;
+
+ mutex_lock(&isolation_lock);
+
+ ret = __isolation_group_domain_attach_devs(groupa->iommu_domain,
+ &groupb->devices);
+ if (ret) {
+ mutex_unlock(&isolation_lock);
+ return ret;
+ }
+
+ groupb->iommu_domain = groupa->iommu_domain;
+
+ mutex_unlock(&isolation_lock);
+
+ return 0;
+}
+
+/*
+ * XXX page mapping/unmapping and more iommu api wrappers
+ */
+
+/*
+ * Do we have a group manager? Group managers restrict what drivers are
+ * allowed to attach to devices. A group is "locked" when all of the devices
+ * for a group belong to group manager drivers (or no driver at all). At
+ * that point, the group manager can initialize the iommu. vfio is an example
+ * of a group manager and vfio-pci is an exanple of a driver that a group
+ * manager may add as a "managed" driver. Note that we don't gate iommu
+ * manipulation on being managed because we want to use it for regular DMA
+ * at some point as well.
+ */
+bool isolation_group_managed(struct isolation_group *group)
+{
+ return group->manager != NULL;
+}
+
+/*
+ * Initialize the group manager struct. At this point the isolation group
+ * becomes "managed".
+ */
+int isolation_group_init_manager(struct isolation_group *group)
+{
+ mutex_lock(&isolation_lock);
+
+ if (group->manager) {
+ mutex_unlock(&isolation_lock);
+ return -EBUSY;
+ }
+
+ group->manager = kzalloc(sizeof(struct isolation_manager), GFP_KERNEL);
+ if (!group->manager) {
+ mutex_unlock(&isolation_lock);
+ return -ENOMEM;
+ }
+
+ mutex_unlock(&isolation_lock);
+
+ return 0;
+}
+
+/*
+ * And free... cleanup registerd managed drivers too.
+ */
+void isolation_group_free_manager(struct isolation_group *group)
+{
+ struct isolation_manager_driver *driver, *tmp;
+
+ if (!group->manager)
+ return;
+
+ mutex_lock(&isolation_lock);
+
+ list_for_each_entry_safe(driver, tmp, &group->manager->drivers, list) {
+ list_del(&driver->list);
+ kfree(driver);
+ }
+
+ kfree(group->manager);
+ group->manager = NULL;
+ mutex_unlock(&isolation_lock);
+}
+
+/*
+ * Add a managed driver. Drivers added here are the only ones that will
+ * be allowed to attach to a managed isolation group.
+ */
+int isolation_group_manager_add_driver(struct isolation_group *group,
+ struct device_driver *drv)
+{
+ struct isolation_manager_driver *driver;
+
+ if (!group->manager)
+ return -EINVAL;
+
+ driver = kzalloc(sizeof(*driver), GFP_KERNEL);
+ if (!driver)
+ return -ENOMEM;
+
+ driver->drv = drv;
+
+ mutex_lock(&isolation_lock);
+
+ list_add(&driver->list, &group->manager->drivers);
+
+ mutex_unlock(&isolation_lock);
+
+ return 0;
+}
+
+/*
+ * And remove a driver. Don't really expect to need this much.
+ */
+void isolation_group_manager_del_driver(struct isolation_group *group,
+ struct device_driver *drv)
+{
+ struct isolation_manager_driver *driver;
+
+ if (!group->manager)
+ return;
+
+ mutex_lock(&isolation_lock);
+
+ list_for_each_entry(driver, &group->manager->drivers, list) {
+ if (driver->drv == drv) {
+ list_del(&driver->list);
+ kfree(driver);
+ break;
+ }
+ }
+
+ mutex_unlock(&isolation_lock);
+}
+
+/*
+ * Test whether a driver is a "managed" driver for the group. This allows
+ * is to preempt normal driver matching and only let our drivers in.
+ */
+bool isolation_group_manager_driver(struct isolation_group *group,
+ struct device_driver *drv)
+{
+ struct isolation_manager_driver *driver;
+ bool found = false;
+
+ if (!group->manager)
+ return found;
+
+ mutex_lock(&isolation_lock);
+
+ list_for_each_entry(driver, &group->manager->drivers, list) {
+ if (driver->drv == drv) {
+ found = true;
+ break;
+ }
+ }
+
+ mutex_unlock(&isolation_lock);
+
+ return found;
+}
+
+/*
+ * Does the group manager have control of all of the devices in the group?
+ * We consider driver-less devices to be ok here as they don't do DMA and
+ * don't present any interfaces to subvert the rest of the group.
+ */
+bool isolation_group_manager_locked(struct isolation_group *group)
+{
+ struct isolation_device *device;
+ struct isolation_manager_driver *driver;
+ bool found, locked = true;
+
+ if (!group->manager)
+ return false;
+
+ mutex_lock(&isolation_lock);
+
+ list_for_each_entry(device, &group->devices, list) {
+ found = false;
+
+ if (!device->dev->driver)
+ continue;
+
+ list_for_each_entry(driver, &group->manager->drivers, list) {
+ if (device->dev->driver == driver->drv) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ locked = false;
+ break;
+ }
+ }
+
+ mutex_unlock(&isolation_lock);
+
+ return locked;
+}
+
+static int __init isolation_init(void)
+{
+ isolation_kset = kset_create_and_add("isolation", NULL, NULL);
+
+ WARN(!isolation_kset, "Failed to initialize isolation group kset\n");
+
+ return isolation_kset ? 0 : -1;
+}
+subsys_initcall(isolation_init);
diff --git a/include/linux/device.h b/include/linux/device.h
index b63fb39..5805c56 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -663,6 +663,10 @@ struct device {
struct device_dma_parameters *dma_parms;
+#ifdef CONFIG_ISOLATION_GROUPS
+ struct isolation_group *isolation_group;
+#endif
+
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
diff --git a/include/linux/isolation.h b/include/linux/isolation.h
new file mode 100644
index 0000000..1d87caf
--- /dev/null
+++ b/include/linux/isolation.h
@@ -0,0 +1,138 @@
+/*
+ * Isolation group interface
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@...hat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _LINUX_ISOLATION_H
+#define _LINUX_ISOLATION_H
+
+#define ISOLATION_NOTIFY_ADD_DEVICE 1
+#define ISOLATION_NOTIFY_DEL_DEVICE 2
+
+#ifdef CONFIG_ISOLATION_GROUPS
+
+extern struct isolation_group *isolation_create_group(void);
+extern int isolation_free_group(struct isolation_group *group);
+extern int isolation_group_add_dev(struct isolation_group *group,
+ struct device *dev);
+extern int isolation_group_del_dev(struct device *dev);
+extern int isolation_register_notifier(struct bus_type *bus,
+ struct notifier_block *nb);
+extern int isolation_unregister_notifier(struct bus_type *bus,
+ struct notifier_block *nb);
+extern int isolation_group_set_iommu_ops(struct isolation_group *group,
+ struct iommu_ops *iommu_ops);
+extern int isolation_group_init_iommu_domain(struct isolation_group *group);
+extern void isolation_group_free_iommu_domain(struct isolation_group *group);
+extern int isolation_group_iommu_domain_add_group(
+ struct isolation_group *groupa, struct isolation_group *groupb);
+extern bool isolation_group_managed(struct isolation_group *group);
+extern int isolation_group_init_manager(struct isolation_group *group);
+extern void isolation_group_free_manager(struct isolation_group *group);
+extern int isolation_group_manager_add_driver(struct isolation_group *group,
+ struct device_driver *drv);
+extern void isolation_group_manager_del_driver(struct isolation_group *group,
+ struct device_driver *drv);
+extern bool isolation_group_manager_driver(struct isolation_group *group,
+ struct device_driver *drv);
+extern bool isolation_group_manager_locked(struct isolation_group *group);
+
+#define to_isolation_group(dev) ((dev)->isolation_group)
+
+#else /* CONFIG_ISOLATION_GROUPS */
+
+static inline struct isolation_group *isolation_create_group(void)
+{
+ return NULL;
+}
+
+static inline int isolation_free_group(struct isolation_group *group)
+{
+ return 0;
+}
+
+static inline int isolation_group_add_dev(struct isolation_group *group,
+ struct device *dev)
+{
+ return 0;
+}
+
+static inline int isolation_group_del_dev(struct device *dev)
+{
+ return 0;
+}
+
+static int isolation_register_notifier(struct bus_type *bus,
+ struct notifier_block *nb)
+{
+ return 0;
+}
+
+static int isolation_unregister_notifier(struct bus_type *bus,
+ struct notifier_block *nb)
+{
+ return 0;
+}
+
+static int isolation_group_set_iommu_ops(struct isolation_group *group,
+ struct iommu_ops *iommu_ops)
+{
+ return -ENOSYS;
+}
+
+static int isolation_group_init_iommu_domain(struct isolation_group *group)
+{
+ return -ENOSYS;
+}
+
+static void isolation_group_free_iommu_domain(struct isolation_group *group)
+{
+}
+
+static int isolation_group_iommu_domain_add_group(
+ struct isolation_group *groupa, struct isolation_group *groupb)
+{
+ return -ENOSYS;
+}
+
+static int isolation_group_init_manager(struct isolation_group *group)
+{
+ return -ENOSYS;
+}
+
+static void isolation_group_free_manager(struct isolation_group *group)
+{
+}
+
+static int isolation_group_manager_add_driver(struct isolation_group *group,
+ struct device_driver *drv)
+{
+ return -ENOSYS;
+}
+
+static void isolation_group_manager_del_driver(struct isolation_group *group,
+ struct device_driver *drv)
+{
+}
+
+static bool isolation_group_manager_locked(struct isolation_group *group)
+{
+ return false;
+}
+
+#define to_isolation_group(dev) (NULL)
+
+#define isolation_group_managed(group) (false)
+
+#define isolation_group_manager_driver(group, drv) (false)
+
+#endif /* CONFIG_ISOLATION_GROUPS */
+
+#endif /* _LINUX_ISOLATION_H */
--
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