summary | shortlog | log | commit | commitdiff | tree
raw | combined (merge: 28a33cb 2c9195e 2c0ae17 7d43c2e 8ce44a2 3177bb7)
raw | combined (merge: 28a33cb 2c9195e 2c0ae17 7d43c2e 8ce44a2 3177bb7)
author | Joerg Roedel <joerg.roedel@amd.com> | |
Mon, 23 Jul 2012 10:17:00 +0000 (12:17 +0200) | ||
committer | Joerg Roedel <joerg.roedel@amd.com> | |
Mon, 23 Jul 2012 10:17:00 +0000 (12:17 +0200) |
Conflicts:
drivers/iommu/iommu.c
include/linux/iommu.h
drivers/iommu/iommu.c
include/linux/iommu.h
1 | 2 | 3 | 4 | 5 | 6 | |||
---|---|---|---|---|---|---|---|---|
drivers/iommu/amd_iommu.c | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
drivers/iommu/amd_iommu_v2.c | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
drivers/iommu/intel-iommu.c | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
drivers/iommu/iommu.c | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
drivers/iommu/tegra-smmu.c | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
drivers/pci/pci.c | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
drivers/pci/quirks.c | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
include/linux/device.h | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
include/linux/iommu.h | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
include/linux/pci.h | patch | | diff1 | | diff2 | | diff3 | | diff4 | | diff5 | | diff6 | | blob | history |
diff --cc drivers/iommu/amd_iommu.c
index 625626391f2d39d3802710a1677ac49ef3181c9a,3f365ab9f7c7a22eec9488dd2868ffba7551501f,584ea85ab2f06aaf421fe840ae3c5a97932f3a58,49172393d6ecfd756f22c00d0b4b4ce7b2bac434,625626391f2d39d3802710a1677ac49ef3181c9a,259a6beddece2f54f5378bf3997902612d86f703..6d1cbdfc9b2a1af4a583460e8c4a5e5c80aabdfc
@@@@@@@ -2269,13 -2281,10 -2268,13 -2314,6 -2269,13 -2267,6 +2327,10 @@@@@@@ static int device_change_notifier(struc
list_add_tail(&dma_domain->list, &iommu_pd_list);
spin_unlock_irqrestore(&iommu_pd_list_lock, flags);
- - - if (!dev_data->passthrough)
- - - dev->archdata.dma_ops = &amd_iommu_dma_ops;
- - - else
- - - dev->archdata.dma_ops = &nommu_dma_ops;
+ + dev_data = get_dev_data(dev);
+ +
+ ++++ dev->archdata.dma_ops = &amd_iommu_dma_ops;
+ +
break;
case BUS_NOTIFY_DEL_DEVICE:
diff --cc drivers/iommu/amd_iommu_v2.c
Simple merge
diff --cc drivers/iommu/intel-iommu.c
Simple merge
diff --cc drivers/iommu/iommu.c
index 8b9ded88e6f5322c18c02ec6e685b05d8decfd29,8b9ded88e6f5322c18c02ec6e685b05d8decfd29,8b9ded88e6f5322c18c02ec6e685b05d8decfd29,0e928acd7dcff2cf3e65800fc1a2e55516edb6ed,8b9ded88e6f5322c18c02ec6e685b05d8decfd29,ed5e0a553ca78a6c2a9330fd0b65d1f4078fb348..ddbdacad7768e4c530a2e12657dc953a763325cc
+++ b/drivers/iommu/iommu.c
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/iommu.h>
+++ ++#include <linux/idr.h>
+++ ++#include <linux/notifier.h>
+++ ++#include <linux/err.h>
+++ ++
+++ ++static struct kset *iommu_group_kset;
+++ ++static struct ida iommu_group_ida;
+++ ++static struct mutex iommu_group_mutex;
+++ ++
+++ ++struct iommu_group {
+++ ++ struct kobject kobj;
+++ ++ struct kobject *devices_kobj;
+++ ++ struct list_head devices;
+++ ++ struct mutex mutex;
+++ ++ struct blocking_notifier_head notifier;
+++ ++ void *iommu_data;
+++ ++ void (*iommu_data_release)(void *iommu_data);
+++ ++ char *name;
+++ ++ int id;
+++ ++};
+++ ++
+++ ++struct iommu_device {
+++ ++ struct list_head list;
+++ ++ struct device *dev;
+++ ++ char *name;
+++ ++};
+++ ++
+++ ++struct iommu_group_attribute {
+++ ++ struct attribute attr;
+++ ++ ssize_t (*show)(struct iommu_group *group, char *buf);
+++ ++ ssize_t (*store)(struct iommu_group *group,
+++ ++ const char *buf, size_t count);
+++ ++};
+++ ++
+++ ++#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \
+++ ++struct iommu_group_attribute iommu_group_attr_##_name = \
+++ ++ __ATTR(_name, _mode, _show, _store)
+++ ++
+++ ++#define to_iommu_group_attr(_attr) \
+++ ++ container_of(_attr, struct iommu_group_attribute, attr)
+++ ++#define to_iommu_group(_kobj) \
+++ ++ container_of(_kobj, struct iommu_group, kobj)
--- --static ssize_t show_iommu_group(struct device *dev,
--- -- struct device_attribute *attr, char *buf)
+++ ++static ssize_t iommu_group_attr_show(struct kobject *kobj,
+++ ++ struct attribute *__attr, char *buf)
{
--- -- unsigned int groupid;
+++ ++ struct iommu_group_attribute *attr = to_iommu_group_attr(__attr);
+++ ++ struct iommu_group *group = to_iommu_group(kobj);
+++ ++ ssize_t ret = -EIO;
--- -- if (iommu_device_group(dev, &groupid))
--- -- return 0;
+++ ++ if (attr->show)
+++ ++ ret = attr->show(group, buf);
+++ ++ return ret;
+++ ++}
+++ +
- return sprintf(buf, "%u", groupid);
+++ ++static ssize_t iommu_group_attr_store(struct kobject *kobj,
+++ ++ struct attribute *__attr,
+++ ++ const char *buf, size_t count)
+++ ++{
+++ ++ struct iommu_group_attribute *attr = to_iommu_group_attr(__attr);
+++ ++ struct iommu_group *group = to_iommu_group(kobj);
+++ ++ ssize_t ret = -EIO;
+++ ++
+++ ++ if (attr->store)
+++ ++ ret = attr->store(group, buf, count);
+++ ++ return ret;
+++ + }
-static DEVICE_ATTR(iommu_group, S_IRUGO, show_iommu_group, NULL);
+++ +
-static int add_iommu_group(struct device *dev, void *data)
+++ ++static const struct sysfs_ops iommu_group_sysfs_ops = {
+++ ++ .show = iommu_group_attr_show,
+++ ++ .store = iommu_group_attr_store,
+++ ++};
+
--- - return sprintf(buf, "%u", groupid);
+++ ++static int iommu_group_create_file(struct iommu_group *group,
+++ ++ struct iommu_group_attribute *attr)
+++ ++{
+++ ++ return sysfs_create_file(&group->kobj, &attr->attr);
+}
--- - static DEVICE_ATTR(iommu_group, S_IRUGO, show_iommu_group, NULL);
+
--- - static int add_iommu_group(struct device *dev, void *data)
+++ ++static void iommu_group_remove_file(struct iommu_group *group,
+++ ++ struct iommu_group_attribute *attr)
+++ + {
- unsigned int groupid;
+++ ++ sysfs_remove_file(&group->kobj, &attr->attr);
+++ ++}
+++ ++
+++ ++static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf)
+++ ++{
+++ ++ return sprintf(buf, "%s\n", group->name);
+++ ++}
+++ ++
+++ ++static IOMMU_GROUP_ATTR(name, S_IRUGO, iommu_group_show_name, NULL);
+++ +
- if (iommu_device_group(dev, &groupid) == 0)
- return device_create_file(dev, &dev_attr_iommu_group);
+++ ++static void iommu_group_release(struct kobject *kobj)
+++ ++{
+++ ++ struct iommu_group *group = to_iommu_group(kobj);
+++ ++
+++ ++ if (group->iommu_data_release)
+++ ++ group->iommu_data_release(group->iommu_data);
+++ ++
+++ ++ mutex_lock(&iommu_group_mutex);
+++ ++ ida_remove(&iommu_group_ida, group->id);
+++ ++ mutex_unlock(&iommu_group_mutex);
+++ ++
+++ ++ kfree(group->name);
+++ ++ kfree(group);
+++ ++}
+++ ++
+++ ++static struct kobj_type iommu_group_ktype = {
+++ ++ .sysfs_ops = &iommu_group_sysfs_ops,
+++ ++ .release = iommu_group_release,
+++ ++};
+++ ++
+++ ++/**
+++ ++ * iommu_group_alloc - Allocate a new group
+++ ++ * @name: Optional name to associate with group, visible in sysfs
+++ ++ *
+++ ++ * This function is called by an iommu driver to allocate a new iommu
+++ ++ * group. The iommu group represents the minimum granularity of the iommu.
+++ ++ * Upon successful return, the caller holds a reference to the supplied
+++ ++ * group in order to hold the group until devices are added. Use
+++ ++ * iommu_group_put() to release this extra reference count, allowing the
+++ ++ * group to be automatically reclaimed once it has no devices or external
+++ ++ * references.
+++ ++ */
+++ ++struct iommu_group *iommu_group_alloc(void)
+++ ++{
+++ ++ struct iommu_group *group;
+++ ++ int ret;
+++ ++
+++ ++ group = kzalloc(sizeof(*group), GFP_KERNEL);
+++ ++ if (!group)
+++ ++ return ERR_PTR(-ENOMEM);
+++ ++
+++ ++ group->kobj.kset = iommu_group_kset;
+++ ++ mutex_init(&group->mutex);
+++ ++ INIT_LIST_HEAD(&group->devices);
+++ ++ BLOCKING_INIT_NOTIFIER_HEAD(&group->notifier);
+++ ++
+++ ++ mutex_lock(&iommu_group_mutex);
+++ ++
+++ ++again:
+++ ++ if (unlikely(0 == ida_pre_get(&iommu_group_ida, GFP_KERNEL))) {
+++ ++ kfree(group);
+++ ++ mutex_unlock(&iommu_group_mutex);
+++ ++ return ERR_PTR(-ENOMEM);
+++ ++ }
+++ ++
+++ ++ if (-EAGAIN == ida_get_new(&iommu_group_ida, &group->id))
+++ ++ goto again;
+++ ++
+++ ++ mutex_unlock(&iommu_group_mutex);
+++ ++
+++ ++ ret = kobject_init_and_add(&group->kobj, &iommu_group_ktype,
+++ ++ NULL, "%d", group->id);
+++ ++ if (ret) {
+++ ++ mutex_lock(&iommu_group_mutex);
+++ ++ ida_remove(&iommu_group_ida, group->id);
+++ ++ mutex_unlock(&iommu_group_mutex);
+++ ++ kfree(group);
+++ ++ return ERR_PTR(ret);
+++ ++ }
+++ ++
+++ ++ group->devices_kobj = kobject_create_and_add("devices", &group->kobj);
+++ ++ if (!group->devices_kobj) {
+++ ++ kobject_put(&group->kobj); /* triggers .release & free */
+++ ++ return ERR_PTR(-ENOMEM);
+++ ++ }
+++ ++
+++ ++ /*
+++ ++ * The devices_kobj holds a reference on the group kobject, so
+++ ++ * as long as that exists so will the group. We can therefore
+++ ++ * use the devices_kobj for reference counting.
+++ ++ */
+++ ++ kobject_put(&group->kobj);
+++ ++
+++ ++ return group;
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_alloc);
+++ ++
+++ ++/**
+++ ++ * iommu_group_get_iommudata - retrieve iommu_data registered for a group
+++ ++ * @group: the group
+++ ++ *
+++ ++ * iommu drivers can store data in the group for use when doing iommu
+++ ++ * operations. This function provides a way to retrieve it. Caller
+++ ++ * should hold a group reference.
+++ ++ */
+++ ++void *iommu_group_get_iommudata(struct iommu_group *group)
+++ ++{
+++ ++ return group->iommu_data;
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_get_iommudata);
+++ ++
+++ ++/**
+++ ++ * iommu_group_set_iommudata - set iommu_data for a group
+++ ++ * @group: the group
+++ ++ * @iommu_data: new data
+++ ++ * @release: release function for iommu_data
+++ ++ *
+++ ++ * iommu drivers can store data in the group for use when doing iommu
+++ ++ * operations. This function provides a way to set the data after
+++ ++ * the group has been allocated. Caller should hold a group reference.
+++ ++ */
+++ ++void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data,
+++ ++ void (*release)(void *iommu_data))
+{
--- - unsigned int groupid;
+++ ++ group->iommu_data = iommu_data;
+++ ++ group->iommu_data_release = release;
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_set_iommudata);
+
--- - if (iommu_device_group(dev, &groupid) == 0)
--- - return device_create_file(dev, &dev_attr_iommu_group);
+++ ++/**
+++ ++ * iommu_group_set_name - set name for a group
+++ ++ * @group: the group
+++ ++ * @name: name
+++ ++ *
+++ ++ * Allow iommu driver to set a name for a group. When set it will
+++ ++ * appear in a name attribute file under the group in sysfs.
+++ ++ */
+++ ++int iommu_group_set_name(struct iommu_group *group, const char *name)
+++ ++{
+++ ++ int ret;
+++ ++
+++ ++ if (group->name) {
+++ ++ iommu_group_remove_file(group, &iommu_group_attr_name);
+++ ++ kfree(group->name);
+++ ++ group->name = NULL;
+++ ++ if (!name)
+++ ++ return 0;
+++ ++ }
+++ ++
+++ ++ group->name = kstrdup(name, GFP_KERNEL);
+++ ++ if (!group->name)
+++ ++ return -ENOMEM;
+++ ++
+++ ++ ret = iommu_group_create_file(group, &iommu_group_attr_name);
+++ ++ if (ret) {
+++ ++ kfree(group->name);
+++ ++ group->name = NULL;
+++ ++ return ret;
+++ ++ }
return 0;
}
-static int remove_iommu_group(struct device *dev)
+++ ++EXPORT_SYMBOL_GPL(iommu_group_set_name);
+++ +
- unsigned int groupid;
+++ ++/**
+++ ++ * iommu_group_add_device - add a device to an iommu group
+++ ++ * @group: the group into which to add the device (reference should be held)
+++ ++ * @dev: the device
+++ ++ *
+++ ++ * This function is called by an iommu driver to add a device into a
+++ ++ * group. Adding a device increments the group reference count.
+++ ++ */
+++ ++int iommu_group_add_device(struct iommu_group *group, struct device *dev)
+++ + {
- if (iommu_device_group(dev, &groupid) == 0)
- device_remove_file(dev, &dev_attr_iommu_group);
+++ ++ int ret, i = 0;
+++ ++ struct iommu_device *device;
+++ ++
+++ ++ device = kzalloc(sizeof(*device), GFP_KERNEL);
+++ ++ if (!device)
+++ ++ return -ENOMEM;
+++ ++
+++ ++ device->dev = dev;
+++ ++
+++ ++ ret = sysfs_create_link(&dev->kobj, &group->kobj, "iommu_group");
+++ ++ if (ret) {
+++ ++ kfree(device);
+++ ++ return ret;
+++ ++ }
+++ ++
+++ ++ device->name = kasprintf(GFP_KERNEL, "%s", kobject_name(&dev->kobj));
+++ ++rename:
+++ ++ if (!device->name) {
+++ ++ sysfs_remove_link(&dev->kobj, "iommu_group");
+++ ++ kfree(device);
+++ ++ return -ENOMEM;
+++ ++ }
+++ +
-static int iommu_device_notifier(struct notifier_block *nb,
- unsigned long action, void *data)
+++ ++ ret = sysfs_create_link_nowarn(group->devices_kobj,
+++ ++ &dev->kobj, device->name);
+++ ++ if (ret) {
+++ ++ kfree(device->name);
+++ ++ if (ret == -EEXIST && i >= 0) {
+++ ++ /*
+++ ++ * Account for the slim chance of collision
+++ ++ * and append an instance to the name.
+++ ++ */
+++ ++ device->name = kasprintf(GFP_KERNEL, "%s.%d",
+++ ++ kobject_name(&dev->kobj), i++);
+++ ++ goto rename;
+++ ++ }
+++ ++
+++ ++ sysfs_remove_link(&dev->kobj, "iommu_group");
+++ ++ kfree(device);
+++ ++ return ret;
+++ ++ }
+++ ++
+++ ++ kobject_get(group->devices_kobj);
+++ ++
+++ ++ dev->iommu_group = group;
+++ ++
+++ ++ mutex_lock(&group->mutex);
+++ ++ list_add_tail(&device->list, &group->devices);
+++ ++ mutex_unlock(&group->mutex);
+++ +
+++ ++ /* Notify any listeners about change to group. */
+++ ++ blocking_notifier_call_chain(&group->notifier,
+++ ++ IOMMU_GROUP_NOTIFY_ADD_DEVICE, dev);
+++ + return 0;
+++ + }
+++ ++EXPORT_SYMBOL_GPL(iommu_group_add_device);
+++ +
--- - static int remove_iommu_group(struct device *dev)
+++ ++/**
+++ ++ * iommu_group_remove_device - remove a device from it's current group
+++ ++ * @dev: device to be removed
+++ ++ *
+++ ++ * This function is called by an iommu driver to remove the device from
+++ ++ * it's current group. This decrements the iommu group reference count.
+++ ++ */
+++ ++void iommu_group_remove_device(struct device *dev)
+++ ++{
+++ ++ struct iommu_group *group = dev->iommu_group;
+++ ++ struct iommu_device *tmp_device, *device = NULL;
+++ ++
+++ ++ /* Pre-notify listeners that a device is being removed. */
+++ ++ blocking_notifier_call_chain(&group->notifier,
+++ ++ IOMMU_GROUP_NOTIFY_DEL_DEVICE, dev);
+++ ++
+++ ++ mutex_lock(&group->mutex);
+++ ++ list_for_each_entry(tmp_device, &group->devices, list) {
+++ ++ if (tmp_device->dev == dev) {
+++ ++ device = tmp_device;
+++ ++ list_del(&device->list);
+++ ++ break;
+++ ++ }
+++ ++ }
+++ ++ mutex_unlock(&group->mutex);
+++ ++
+++ ++ if (!device)
+++ ++ return;
+++ ++
+++ ++ sysfs_remove_link(group->devices_kobj, device->name);
+++ ++ sysfs_remove_link(&dev->kobj, "iommu_group");
+++ ++
+++ ++ kfree(device->name);
+++ ++ kfree(device);
+++ ++ dev->iommu_group = NULL;
+++ ++ kobject_put(group->devices_kobj);
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_remove_device);
+++ ++
+++ ++/**
+++ ++ * iommu_group_for_each_dev - iterate over each device in the group
+++ ++ * @group: the group
+++ ++ * @data: caller opaque data to be passed to callback function
+++ ++ * @fn: caller supplied callback function
+++ ++ *
+++ ++ * This function is called by group users to iterate over group devices.
+++ ++ * Callers should hold a reference count to the group during callback.
+++ ++ * The group->mutex is held across callbacks, which will block calls to
+++ ++ * iommu_group_add/remove_device.
+++ ++ */
+++ ++int iommu_group_for_each_dev(struct iommu_group *group, void *data,
+++ ++ int (*fn)(struct device *, void *))
+++ ++{
+++ ++ struct iommu_device *device;
+++ ++ int ret = 0;
+++ ++
+++ ++ mutex_lock(&group->mutex);
+++ ++ list_for_each_entry(device, &group->devices, list) {
+++ ++ ret = fn(device->dev, data);
+++ ++ if (ret)
+++ ++ break;
+++ ++ }
+++ ++ mutex_unlock(&group->mutex);
+++ ++ return ret;
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_for_each_dev);
+++ ++
+++ ++/**
+++ ++ * iommu_group_get - Return the group for a device and increment reference
+++ ++ * @dev: get the group that this device belongs to
+++ ++ *
+++ ++ * This function is called by iommu drivers and users to get the group
+++ ++ * for the specified device. If found, the group is returned and the group
+++ ++ * reference in incremented, else NULL.
+++ ++ */
+++ ++struct iommu_group *iommu_group_get(struct device *dev)
+++ ++{
+++ ++ struct iommu_group *group = dev->iommu_group;
+++ ++
+++ ++ if (group)
+++ ++ kobject_get(group->devices_kobj);
+++ ++
+++ ++ return group;
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_get);
+++ ++
+++ ++/**
+++ ++ * iommu_group_put - Decrement group reference
+++ ++ * @group: the group to use
+++ ++ *
+++ ++ * This function is called by iommu drivers and users to release the
+++ ++ * iommu group. Once the reference count is zero, the group is released.
+++ ++ */
+++ ++void iommu_group_put(struct iommu_group *group)
+++ ++{
+++ ++ if (group)
+++ ++ kobject_put(group->devices_kobj);
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_put);
+++ ++
+++ ++/**
+++ ++ * iommu_group_register_notifier - Register a notifier for group changes
+++ ++ * @group: the group to watch
+++ ++ * @nb: notifier block to signal
+++ ++ *
+++ ++ * This function allows iommu group users to track changes in a group.
+++ ++ * See include/linux/iommu.h for actions sent via this notifier. Caller
+++ ++ * should hold a reference to the group throughout notifier registration.
+++ ++ */
+++ ++int iommu_group_register_notifier(struct iommu_group *group,
+++ ++ struct notifier_block *nb)
+++ ++{
+++ ++ return blocking_notifier_chain_register(&group->notifier, nb);
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_register_notifier);
+++ ++
+++ ++/**
+++ ++ * iommu_group_unregister_notifier - Unregister a notifier
+++ ++ * @group: the group to watch
+++ ++ * @nb: notifier block to signal
+++ ++ *
+++ ++ * Unregister a previously registered group notifier block.
+++ ++ */
+++ ++int iommu_group_unregister_notifier(struct iommu_group *group,
+++ ++ struct notifier_block *nb)
+++ ++{
+++ ++ return blocking_notifier_chain_unregister(&group->notifier, nb);
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_unregister_notifier);
+++ ++
+++ ++/**
+++ ++ * iommu_group_id - Return ID for a group
+++ ++ * @group: the group to ID
+++ ++ *
+++ ++ * Return the unique ID for the group matching the sysfs group number.
+++ ++ */
+++ ++int iommu_group_id(struct iommu_group *group)
+++ ++{
+++ ++ return group->id;
+++ ++}
+++ ++EXPORT_SYMBOL_GPL(iommu_group_id);
+
--- - unsigned int groupid;
+++ ++static int add_iommu_group(struct device *dev, void *data)
+{
--- - if (iommu_device_group(dev, &groupid) == 0)
--- - device_remove_file(dev, &dev_attr_iommu_group);
+++ ++ struct iommu_ops *ops = data;
+++ ++
+++ ++ if (!ops->add_device)
+++ ++ return -ENODEV;
+
--- - static int iommu_device_notifier(struct notifier_block *nb,
--- - unsigned long action, void *data)
+++ ++ WARN_ON(dev->iommu_group);
+++ ++
+++ ++ ops->add_device(dev);
+
+ return 0;
+}
+
+++ ++static int iommu_bus_notifier(struct notifier_block *nb,
+++ ++ unsigned long action, void *data)
{
struct device *dev = data;
+++ ++ struct iommu_ops *ops = dev->bus->iommu_ops;
+++ ++ struct iommu_group *group;
+++ ++ unsigned long group_action = 0;
+++ ++
+++ ++ /*
+++ ++ * ADD/DEL call into iommu driver ops if provided, which may
+++ ++ * result in ADD/DEL notifiers to group->notifier
+++ ++ */
+++ ++ if (action == BUS_NOTIFY_ADD_DEVICE) {
+++ ++ if (ops->add_device)
+++ ++ return ops->add_device(dev);
+++ ++ } else if (action == BUS_NOTIFY_DEL_DEVICE) {
+++ ++ if (ops->remove_device && dev->iommu_group) {
+++ ++ ops->remove_device(dev);
+++ ++ return 0;
+++ ++ }
+++ ++ }
+++ ++
+++ ++ /*
+++ ++ * Remaining BUS_NOTIFYs get filtered and republished to the
+++ ++ * group, if anyone is listening
+++ ++ */
+++ ++ group = iommu_group_get(dev);
+++ ++ if (!group)
+++ ++ return 0;
+++ ++
+++ ++ switch (action) {
+++ ++ case BUS_NOTIFY_BIND_DRIVER:
+++ ++ group_action = IOMMU_GROUP_NOTIFY_BIND_DRIVER;
+++ ++ break;
+++ ++ case BUS_NOTIFY_BOUND_DRIVER:
+++ ++ group_action = IOMMU_GROUP_NOTIFY_BOUND_DRIVER;
+++ ++ break;
+++ ++ case BUS_NOTIFY_UNBIND_DRIVER:
+++ ++ group_action = IOMMU_GROUP_NOTIFY_UNBIND_DRIVER;
+++ ++ break;
+++ ++ case BUS_NOTIFY_UNBOUND_DRIVER:
+++ ++ group_action = IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER;
+++ ++ break;
+++ ++ }
--- -- if (action == BUS_NOTIFY_ADD_DEVICE)
--- -- return add_iommu_group(dev, NULL);
--- -- else if (action == BUS_NOTIFY_DEL_DEVICE)
--- -- return remove_iommu_group(dev);
+++ ++ if (group_action)
+++ ++ blocking_notifier_call_chain(&group->notifier,
+++ ++ group_action, dev);
+++ ++ iommu_group_put(group);
return 0;
}
@@@@@@@ -336,11 -336,11 -336,11 -850,15 -336,11 -336,44 +850,48 @@@@@@@ size_t iommu_unmap(struct iommu_domain
}
EXPORT_SYMBOL_GPL(iommu_unmap);
--- --int iommu_device_group(struct device *dev, unsigned int *groupid)
+++ ++static int __init iommu_init(void)
{
--- -- if (iommu_present(dev->bus) && dev->bus->iommu_ops->device_group)
--- -- return dev->bus->iommu_ops->device_group(dev, groupid);
+++ ++ iommu_group_kset = kset_create_and_add("iommu_groups",
+++ ++ NULL, kernel_kobj);
+++ ++ ida_init(&iommu_group_ida);
+++ ++ mutex_init(&iommu_group_mutex);
+++ ++
+++ ++ BUG_ON(!iommu_group_kset);
+++ +
- return -ENODEV;
+++ ++ return 0;
+++ + }
-EXPORT_SYMBOL_GPL(iommu_device_group);
+++ ++subsys_initcall(iommu_init);
+++++
+++++ int iommu_domain_get_attr(struct iommu_domain *domain,
+++++ enum iommu_attr attr, void *data)
+++++ {
+++++ struct iommu_domain_geometry *geometry;
+++++ int ret = 0;
+++++
+++++ switch (attr) {
+++++ case DOMAIN_ATTR_GEOMETRY:
+++++ geometry = data;
+++++ *geometry = domain->geometry;
+++++
+++++ break;
+++++ default:
+++++ if (!domain->ops->domain_get_attr)
+++++ return -EINVAL;
+++++
+++++ ret = domain->ops->domain_get_attr(domain, attr, data);
+++++ }
+++++
+++++ return ret;
+++++ }
+++++ EXPORT_SYMBOL_GPL(iommu_domain_get_attr);
+++++
+++++ int iommu_domain_set_attr(struct iommu_domain *domain,
+++++ enum iommu_attr attr, void *data)
+++++ {
+++++ if (!domain->ops->domain_set_attr)
+++++ return -EINVAL;
+
--- - return -ENODEV;
+++++ return domain->ops->domain_set_attr(domain, attr, data);
+ }
--- - EXPORT_SYMBOL_GPL(iommu_device_group);
+++++ EXPORT_SYMBOL_GPL(iommu_domain_set_attr);
diff --cc drivers/iommu/tegra-smmu.c
index 3f3d09d560ea3c6ce5e3bc7d019e377ac9e63dfe,3f3d09d560ea3c6ce5e3bc7d019e377ac9e63dfe,3f3d09d560ea3c6ce5e3bc7d019e377ac9e63dfe,ecd679043d7740e6883aae9cbee68da6321fedc1,541d210cb4216aab2b3624a19cee810902277098,96e73d56451a01aa34e76dd693443fdc810ece9d..4ba325ab626249c2aa2aee1271a6d0c68ec20d9a
@@@@@@@ -541,29 -541,29 -541,29 -541,29 -557,38 -541,29 +557,38 @@@@@@@ static inline void put_signature(struc
*/
static int alloc_pdir(struct smmu_as *as)
{
---- - unsigned long *pdir;
---- - int pdn;
++++ + unsigned long *pdir, flags;
++++ + int pdn, err = 0;
u32 val;
struct smmu_device *smmu = as->smmu;
- if (as->pdir_page)
- return 0;
++++ + struct page *page;
++++ + unsigned int *cnt;
++++
++++ + /*
++++ + * do the allocation, then grab as->lock
++++ + */
++++ + cnt = devm_kzalloc(smmu->dev,
++++ + sizeof(cnt[0]) * SMMU_PDIR_COUNT,
++++ + GFP_KERNEL);
++++ + page = alloc_page(GFP_KERNEL | __GFP_DMA);
---- if (as->pdir_page)
---- return 0;
- as->pte_count = devm_kzalloc(smmu->dev,
- sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_KERNEL);
- if (!as->pte_count) {
- dev_err(smmu->dev,
- "failed to allocate smmu_device PTE cunters\n");
- return -ENOMEM;
++++ + spin_lock_irqsave(&as->lock, flags);
+
---- as->pte_count = devm_kzalloc(smmu->dev,
--- sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_ATOMIC);
- sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_KERNEL);
---- if (!as->pte_count) {
---- dev_err(smmu->dev,
---- "failed to allocate smmu_device PTE cunters\n");
---- return -ENOMEM;
++++ + if (as->pdir_page) {
++++ + /* We raced, free the redundant */
++++ + err = -EAGAIN;
++++ + goto err_out;
}
--- as->pdir_page = alloc_page(GFP_ATOMIC | __GFP_DMA);
- - as->pdir_page = alloc_page(GFP_KERNEL | __GFP_DMA);
---- - if (!as->pdir_page) {
---- - dev_err(smmu->dev,
---- - "failed to allocate smmu_device page directory\n");
---- - devm_kfree(smmu->dev, as->pte_count);
---- - as->pte_count = NULL;
---- - return -ENOMEM;
++++ +
++++ + if (!page || !cnt) {
++++ + dev_err(smmu->dev, "failed to allocate at %s\n", __func__);
++++ + err = -ENOMEM;
++++ + goto err_out;
}
++++ +
++++ + as->pdir_page = page;
++++ + as->pte_count = cnt;
++++ +
SetPageReserved(as->pdir_page);
pdir = page_address(as->pdir_page);
SMMU_MK_PDIR(as->pdir_page, as->pdir_attr), SMMU_PTB_DATA);
FLUSH_SMMU_REGS(smmu);
---- - spin_unlock(&smmu->lock);
++++ + spin_unlock_irqrestore(&smmu->lock, flags);
---- - spin_unlock_irqrestore(&as->lock, flags);
domain->priv = as;
+++++ domain->geometry.aperture_start = smmu->iovmm_base;
+++++ domain->geometry.aperture_end = smmu->iovmm_base +
+++++ smmu->page_count * SMMU_PAGE_SIZE - 1;
+++++ domain->geometry.force_aperture = true;
+++++
dev_dbg(smmu->dev, "smmu_as@%p\n", as);
---- - return 0;
---- -err_alloc_pdir:
---- - spin_unlock_irqrestore(&as->lock, flags);
---- - return -ENODEV;
++++ + return 0;
}
static void smmu_iommu_domain_destroy(struct iommu_domain *domain)
diff --cc drivers/pci/pci.c
Simple merge
diff --cc drivers/pci/quirks.c
Simple merge
diff --cc include/linux/device.h
Simple merge
diff --cc include/linux/iommu.h
index 450293f6d68b6a9bfdbd74fdc3304a63c122505a,450293f6d68b6a9bfdbd74fdc3304a63c122505a,450293f6d68b6a9bfdbd74fdc3304a63c122505a,a71df92be992196e767d0d292248e1b8f5a84393,450293f6d68b6a9bfdbd74fdc3304a63c122505a,f7df4aa527f3f15f3937f71d0071053562d346e7..54d6d690073c1cb5119671b99df70af3ddfc615d
+++ b/include/linux/iommu.h
* @unmap: unmap a physically contiguous memory region from an iommu domain
* @iova_to_phys: translate iova to physical address
* @domain_has_cap: domain capabilities query
----- * @commit: commit iommu domain
+++ ++ * @add_device: add device to iommu grouping
+++ ++ * @remove_device: remove device from iommu grouping
+++++ * @domain_get_attr: Query domain attributes
+++++ * @domain_set_attr: Change domain attributes
* @pgsize_bitmap: bitmap of supported page sizes
*/
struct iommu_ops {
unsigned long iova);
int (*domain_has_cap)(struct iommu_domain *domain,
unsigned long cap);
+++ ++ int (*add_device)(struct device *dev);
+++ ++ void (*remove_device)(struct device *dev);
+ int (*device_group)(struct device *dev, unsigned int *groupid);
+++++ int (*domain_get_attr)(struct iommu_domain *domain,
+++++ enum iommu_attr attr, void *data);
+++++ int (*domain_set_attr)(struct iommu_domain *domain,
+++++ enum iommu_attr attr, void *data);
unsigned long pgsize_bitmap;
};
@@@@@@@ -97,8 -97,8 -97,8 -108,30 -97,8 -114,13 +126,35 @@@@@@@ extern int iommu_domain_has_cap(struct
unsigned long cap);
extern void iommu_set_fault_handler(struct iommu_domain *domain,
iommu_fault_handler_t handler, void *token);
--- --extern int iommu_device_group(struct device *dev, unsigned int *groupid);
+++ ++
+++ ++extern int iommu_attach_group(struct iommu_domain *domain,
+++ ++ struct iommu_group *group);
+++ ++extern void iommu_detach_group(struct iommu_domain *domain,
+++ ++ struct iommu_group *group);
+++ ++extern struct iommu_group *iommu_group_alloc(void);
+++ ++extern void *iommu_group_get_iommudata(struct iommu_group *group);
+++ ++extern void iommu_group_set_iommudata(struct iommu_group *group,
+++ ++ void *iommu_data,
+++ ++ void (*release)(void *iommu_data));
+++ ++extern int iommu_group_set_name(struct iommu_group *group, const char *name);
+++ ++extern int iommu_group_add_device(struct iommu_group *group,
+++ ++ struct device *dev);
+++ ++extern void iommu_group_remove_device(struct device *dev);
+++ ++extern int iommu_group_for_each_dev(struct iommu_group *group, void *data,
+++ ++ int (*fn)(struct device *, void *));
+++ ++extern struct iommu_group *iommu_group_get(struct device *dev);
+++ ++extern void iommu_group_put(struct iommu_group *group);
+++ ++extern int iommu_group_register_notifier(struct iommu_group *group,
+++ ++ struct notifier_block *nb);
+++ ++extern int iommu_group_unregister_notifier(struct iommu_group *group,
+++ ++ struct notifier_block *nb);
+++ ++extern int iommu_group_id(struct iommu_group *group);
+++ +
+++++ extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr,
+++++ void *data);
+++++ extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr,
+++++ void *data);
+
/**
* report_iommu_fault() - report about an IOMMU fault to the IOMMU framework
* @domain: the iommu domain where the fault has happened
@@@@@@@ -197,11 -197,11 -197,11 -231,75 -197,11 -219,23 +254,88 @@@@@@@ static inline void iommu_set_fault_hand
{
}
--- --static inline int iommu_device_group(struct device *dev, unsigned int *groupid)
+++ ++int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
+++ ++{
+++ ++ return -ENODEV;
+++ ++}
+++ ++
+++ ++void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
+++ ++{
+++ ++}
+++ ++
+++ ++struct iommu_group *iommu_group_alloc(void)
+++ ++{
+++ ++ return ERR_PTR(-ENODEV);
+++ ++}
+++ ++
+++ ++void *iommu_group_get_iommudata(struct iommu_group *group)
+++ ++{
+++ ++ return NULL;
+++ ++}
+++ ++
+++ ++void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data,
+++ ++ void (*release)(void *iommu_data))
+++ ++{
+++ ++}
+++ ++
+++ ++int iommu_group_set_name(struct iommu_group *group, const char *name)
+++ ++{
+++ ++ return -ENODEV;
+++ ++}
+++ ++
+++ ++int iommu_group_add_device(struct iommu_group *group, struct device *dev)
+++ ++{
+++ ++ return -ENODEV;
+++ ++}
+++ ++
+++ ++void iommu_group_remove_device(struct device *dev)
+++ ++{
+++ ++}
+++ ++
+++ ++int iommu_group_for_each_dev(struct iommu_group *group, void *data,
+++ ++ int (*fn)(struct device *, void *))
+++ ++{
+++ ++ return -ENODEV;
+++ ++}
+++ ++
+++ ++struct iommu_group *iommu_group_get(struct device *dev)
+++ ++{
+++ ++ return NULL;
+++ ++}
+++ ++
+++ ++void iommu_group_put(struct iommu_group *group)
+++ ++{
+++ ++}
+++ ++
+++ ++int iommu_group_register_notifier(struct iommu_group *group,
+++ ++ struct notifier_block *nb)
+{
+ return -ENODEV;
+}
+
+++ ++int iommu_group_unregister_notifier(struct iommu_group *group,
+++ ++ struct notifier_block *nb)
+++ ++{
+++ ++ return 0;
+++ ++}
+++ ++
+++ ++int iommu_group_id(struct iommu_group *group)
+++ + {
+++ + return -ENODEV;
+++ + }
+++++
+++++ static inline int iommu_domain_get_attr(struct iommu_domain *domain,
+++++ enum iommu_attr attr, void *data)
+++++ {
+++++ return -EINVAL;
+++++ }
+++++
+++++ static inline int iommu_domain_set_attr(struct iommu_domain *domain,
+++++ enum iommu_attr attr, void *data)
+++++ {
+++++ return -EINVAL;
+++++ }
+++++
#endif /* CONFIG_IOMMU_API */
#endif /* __LINUX_IOMMU_H */
diff --cc include/linux/pci.h
Simple merge