iommu: Make sure a device is always attached to a domain

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>
This commit is contained in:
Joerg Roedel 2015-05-28 18:41:31 +02:00
parent 426a273834
commit e39cb8a3aa

View File

@ -52,6 +52,7 @@ struct iommu_group {
char *name; char *name;
int id; int id;
struct iommu_domain *default_domain; struct iommu_domain *default_domain;
struct iommu_domain *domain;
}; };
struct iommu_device { struct iommu_device {
@ -78,6 +79,12 @@ struct iommu_group_attribute iommu_group_attr_##_name = \
static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus, static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
unsigned type); 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, static ssize_t iommu_group_attr_show(struct kobject *kobj,
struct attribute *__attr, char *buf) struct attribute *__attr, char *buf)
@ -376,6 +383,8 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev)
mutex_lock(&group->mutex); mutex_lock(&group->mutex);
list_add_tail(&device->list, &group->devices); list_add_tail(&device->list, &group->devices);
if (group->domain)
__iommu_attach_device(group->domain, dev);
mutex_unlock(&group->mutex); mutex_unlock(&group->mutex);
/* Notify any listeners about change to group. */ /* Notify any listeners about change to group. */
@ -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 * The group->mutex is held across callbacks, which will block calls to
* iommu_group_add/remove_device. * iommu_group_add/remove_device.
*/ */
int iommu_group_for_each_dev(struct iommu_group *group, void *data, static int __iommu_group_for_each_dev(struct iommu_group *group, void *data,
int (*fn)(struct device *, void *)) int (*fn)(struct device *, void *))
{ {
struct iommu_device *device; struct iommu_device *device;
int ret = 0; int ret = 0;
mutex_lock(&group->mutex);
list_for_each_entry(device, &group->devices, list) { list_for_each_entry(device, &group->devices, list) {
ret = fn(device->dev, data); ret = fn(device->dev, data);
if (ret) if (ret)
break; 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); mutex_unlock(&group->mutex);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(iommu_group_for_each_dev); EXPORT_SYMBOL_GPL(iommu_group_for_each_dev);
@ -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, group->default_domain = __iommu_domain_alloc(pdev->dev.bus,
IOMMU_DOMAIN_DMA); IOMMU_DOMAIN_DMA);
group->domain = group->default_domain;
} }
return group; return group;
@ -1012,7 +1033,7 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
if (iommu_group_device_count(group) != 1) if (iommu_group_device_count(group) != 1)
goto out_unlock; goto out_unlock;
ret = __iommu_attach_device(domain, dev); ret = __iommu_attach_group(domain, group);
out_unlock: out_unlock:
mutex_unlock(&group->mutex); mutex_unlock(&group->mutex);
@ -1047,7 +1068,7 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
goto out_unlock; goto out_unlock;
} }
__iommu_detach_device(domain, dev); __iommu_detach_group(domain, group);
out_unlock: out_unlock:
mutex_unlock(&group->mutex); mutex_unlock(&group->mutex);
@ -1072,10 +1093,31 @@ static int iommu_group_do_attach_device(struct device *dev, void *data)
return __iommu_attach_device(domain, dev); 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) int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
{ {
return iommu_group_for_each_dev(group, domain, int ret;
iommu_group_do_attach_device);
mutex_lock(&group->mutex);
ret = __iommu_attach_group(domain, group);
mutex_unlock(&group->mutex);
return ret;
} }
EXPORT_SYMBOL_GPL(iommu_attach_group); EXPORT_SYMBOL_GPL(iommu_attach_group);
@ -1088,9 +1130,35 @@ static int iommu_group_do_detach_device(struct device *dev, void *data)
return 0; 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) 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); EXPORT_SYMBOL_GPL(iommu_detach_group);