From: Joerg Roedel Date: Mon, 23 Jul 2012 10:17:00 +0000 (+0200) Subject: Merge branches 'iommu/fixes', 'x86/amd', 'groups', 'arm/tegra' and 'api/domain-attr... X-Git-Url: https://git.stricted.de/?p=GitHub%2Fmt8127%2Fandroid_kernel_alcatel_ttab.git;a=commitdiff_plain;h=395e51f18d3b26619c1c462b7a1c0226846ac0a9 Merge branches 'iommu/fixes', 'x86/amd', 'groups', 'arm/tegra' and 'api/domain-attr' into next Conflicts: drivers/iommu/iommu.c include/linux/iommu.h --- 395e51f18d3b26619c1c462b7a1c0226846ac0a9 diff --cc drivers/iommu/amd_iommu.c index 625626391f2d,3f365ab9f7c7,584ea85ab2f0,49172393d6ec,625626391f2d,259a6beddece..6d1cbdfc9b2a --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@@@@@@ -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); + + dev_data = get_dev_data(dev); + + - - - if (!dev_data->passthrough) - - - dev->archdata.dma_ops = &amd_iommu_dma_ops; - - - else - - - dev->archdata.dma_ops = &nommu_dma_ops; + ++++ dev->archdata.dma_ops = &amd_iommu_dma_ops; + + break; case BUS_NOTIFY_DEL_DEVICE: diff --cc drivers/iommu/iommu.c index 8b9ded88e6f5,8b9ded88e6f5,8b9ded88e6f5,0e928acd7dcf,8b9ded88e6f5,ed5e0a553ca7..ddbdacad7768 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@@@@@@ -26,49 -26,49 -26,49 -26,524 -26,49 -26,49 +26,524 @@@@@@@ #include #include #include +++ ++#include +++ ++#include +++ ++#include +++ ++ +++ ++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; } +++ ++EXPORT_SYMBOL_GPL(iommu_group_set_name); +++ + -static int remove_iommu_group(struct device *dev) +++ ++/** +++ ++ * 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) +++ + { - unsigned int groupid; +++ ++ 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; +++ ++ } +++ + - if (iommu_device_group(dev, &groupid) == 0) - device_remove_file(dev, &dev_attr_iommu_group); +++ ++ 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 iommu_device_notifier(struct notifier_block *nb, - unsigned long action, void *data) +++ ++/** +++ ++ * 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); + --- - static int remove_iommu_group(struct device *dev) +++ ++static int add_iommu_group(struct device *dev, void *data) +{ --- - unsigned int groupid; +++ ++ struct iommu_ops *ops = data; +++ ++ +++ ++ if (!ops->add_device) +++ ++ return -ENODEV; + --- - if (iommu_device_group(dev, &groupid) == 0) --- - device_remove_file(dev, &dev_attr_iommu_group); +++ ++ WARN_ON(dev->iommu_group); +++ ++ +++ ++ ops->add_device(dev); + + return 0; +} + --- - static int iommu_device_notifier(struct notifier_block *nb, --- - unsigned long action, void *data) +++ ++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 3f3d09d560ea,3f3d09d560ea,3f3d09d560ea,ecd679043d77,541d210cb421,96e73d56451a..4ba325ab6262 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@@@@@@ -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; ++++ + struct page *page; ++++ + unsigned int *cnt; ++++ - if (as->pdir_page) - return 0; ++++ + /* ++++ + * 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); @@@@@@@ -802,17 -802,17 -802,17 -802,17 -835,13 -802,22 +835,18 @@@@@@@ found 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 include/linux/iommu.h index 450293f6d68b,450293f6d68b,450293f6d68b,a71df92be992,450293f6d68b,f7df4aa527f3..54d6d690073c --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@@@@@@ -59,7 -59,7 -59,7 -60,9 -59,7 -71,8 +72,10 @@@@@@@ enum iommu_attr * @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 { @@@@@@@ -75,7 -75,7 -75,7 -78,8 -75,7 -88,11 +91,13 @@@@@@@ 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 */