]> git.proxmox.com Git - mirror_ubuntu-disco-kernel.git/commitdiff
Merge branches 'iommu/fixes', 'x86/amd', 'groups', 'arm/tegra' and 'api/domain-attr...
authorJoerg Roedel <joerg.roedel@amd.com>
Mon, 23 Jul 2012 10:17:00 +0000 (12:17 +0200)
committerJoerg 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

1  2  3  4  5  6 
drivers/iommu/amd_iommu.c
drivers/iommu/amd_iommu_v2.c
drivers/iommu/intel-iommu.c
drivers/iommu/iommu.c
drivers/iommu/tegra-smmu.c
drivers/pci/pci.c
drivers/pci/quirks.c
include/linux/device.h
include/linux/iommu.h
include/linux/pci.h

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:
      
Simple merge
Simple merge
index 8b9ded88e6f5322c18c02ec6e685b05d8decfd29,8b9ded88e6f5322c18c02ec6e685b05d8decfd29,8b9ded88e6f5322c18c02ec6e685b05d8decfd29,0e928acd7dcff2cf3e65800fc1a2e55516edb6ed,8b9ded88e6f5322c18c02ec6e685b05d8decfd29,ed5e0a553ca78a6c2a9330fd0b65d1f4078fb348..ddbdacad7768e4c530a2e12657dc953a763325cc
      #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);
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)
Simple merge
Simple merge
Simple merge
index 450293f6d68b6a9bfdbd74fdc3304a63c122505a,450293f6d68b6a9bfdbd74fdc3304a63c122505a,450293f6d68b6a9bfdbd74fdc3304a63c122505a,a71df92be992196e767d0d292248e1b8f5a84393,450293f6d68b6a9bfdbd74fdc3304a63c122505a,f7df4aa527f3f15f3937f71d0071053562d346e7..54d6d690073c1cb5119671b99df70af3ddfc615d
@@@@@@@ -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 {
                                    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 */
Simple merge