Skip to content

Commit

Permalink
iommu: Make sure a device is always attached to a domain
Browse files Browse the repository at this point in the history
Make use of the default domain and re-attach a device to it
when it is detached from another domain. Also enforce that a
device has to be in the default domain before it can be
attached to a different domain.

Signed-off-by: Joerg Roedel <jroedel@suse.de>
  • Loading branch information
Joerg Roedel committed Jun 9, 2015
1 parent 426a273 commit e39cb8a
Showing 1 changed file with 76 additions and 8 deletions.
84 changes: 76 additions & 8 deletions drivers/iommu/iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct iommu_group {
char *name;
int id;
struct iommu_domain *default_domain;
struct iommu_domain *domain;
};

struct iommu_device {
Expand All @@ -78,6 +79,12 @@ struct iommu_group_attribute iommu_group_attr_##_name = \

static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
unsigned type);
static int __iommu_attach_device(struct iommu_domain *domain,
struct device *dev);
static int __iommu_attach_group(struct iommu_domain *domain,
struct iommu_group *group);
static void __iommu_detach_group(struct iommu_domain *domain,
struct iommu_group *group);

static ssize_t iommu_group_attr_show(struct kobject *kobj,
struct attribute *__attr, char *buf)
Expand Down Expand Up @@ -376,6 +383,8 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev)

mutex_lock(&group->mutex);
list_add_tail(&device->list, &group->devices);
if (group->domain)
__iommu_attach_device(group->domain, dev);
mutex_unlock(&group->mutex);

/* Notify any listeners about change to group. */
Expand Down Expand Up @@ -455,19 +464,30 @@ static int iommu_group_device_count(struct iommu_group *group)
* 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 *))
static 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;
}
return ret;
}


int iommu_group_for_each_dev(struct iommu_group *group, void *data,
int (*fn)(struct device *, void *))
{
int ret;

mutex_lock(&group->mutex);
ret = __iommu_group_for_each_dev(group, data, fn);
mutex_unlock(&group->mutex);

return ret;
}
EXPORT_SYMBOL_GPL(iommu_group_for_each_dev);
Expand Down Expand Up @@ -727,6 +747,7 @@ static struct iommu_group *iommu_group_get_for_pci_dev(struct pci_dev *pdev)
*/
group->default_domain = __iommu_domain_alloc(pdev->dev.bus,
IOMMU_DOMAIN_DMA);
group->domain = group->default_domain;
}

return group;
Expand Down Expand Up @@ -1012,7 +1033,7 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
if (iommu_group_device_count(group) != 1)
goto out_unlock;

ret = __iommu_attach_device(domain, dev);
ret = __iommu_attach_group(domain, group);

out_unlock:
mutex_unlock(&group->mutex);
Expand Down Expand Up @@ -1047,7 +1068,7 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
goto out_unlock;
}

__iommu_detach_device(domain, dev);
__iommu_detach_group(domain, group);

out_unlock:
mutex_unlock(&group->mutex);
Expand All @@ -1072,10 +1093,31 @@ static int iommu_group_do_attach_device(struct device *dev, void *data)
return __iommu_attach_device(domain, dev);
}

static int __iommu_attach_group(struct iommu_domain *domain,
struct iommu_group *group)
{
int ret;

if (group->default_domain && group->domain != group->default_domain)
return -EBUSY;

ret = __iommu_group_for_each_dev(group, domain,
iommu_group_do_attach_device);
if (ret == 0)
group->domain = domain;

return ret;
}

int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
{
return iommu_group_for_each_dev(group, domain,
iommu_group_do_attach_device);
int ret;

mutex_lock(&group->mutex);
ret = __iommu_attach_group(domain, group);
mutex_unlock(&group->mutex);

return ret;
}
EXPORT_SYMBOL_GPL(iommu_attach_group);

Expand All @@ -1088,9 +1130,35 @@ static int iommu_group_do_detach_device(struct device *dev, void *data)
return 0;
}

static void __iommu_detach_group(struct iommu_domain *domain,
struct iommu_group *group)
{
int ret;

if (!group->default_domain) {
__iommu_group_for_each_dev(group, domain,
iommu_group_do_detach_device);
group->domain = NULL;
return;
}

if (group->domain == group->default_domain)
return;

/* Detach by re-attaching to the default domain */
ret = __iommu_group_for_each_dev(group, group->default_domain,
iommu_group_do_attach_device);
if (ret != 0)
WARN_ON(1);
else
group->domain = group->default_domain;
}

void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
{
iommu_group_for_each_dev(group, domain, iommu_group_do_detach_device);
mutex_lock(&group->mutex);
__iommu_detach_group(domain, group);
mutex_unlock(&group->mutex);
}
EXPORT_SYMBOL_GPL(iommu_detach_group);

Expand Down

0 comments on commit e39cb8a

Please sign in to comment.