iommu/dart: Fix apple_dart_device_group for PCI groups

pci_device_group() can return an already existing IOMMU group if the PCI
device's pagetables have to be shared with another one due to bus
toplogy, isolation features and/or DMA alias quirks.
apple_dart_device_group() however assumes that the group has just been
created and overwrites its iommudata which will eventually lead to
apple_dart_release_group leaving stale entries in sid2group.
Fix that by merging the iommudata if the returned group already exists.

Fixes: f0b636804c ("iommu/dart: Clear sid2group entry when a group is freed")
Signed-off-by: Sven Peter <sven@svenpeter.dev>
Reviewed-by: Eric Curtin <ecurtin@redhat.com>
Link: https://lore.kernel.org/r/20230128113532.94651-1-sven@svenpeter.dev
Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
Sven Peter 2023-01-28 12:35:32 +01:00 committed by Joerg Roedel
parent d8bcc870d9
commit cf5c1c87c2

View File

@ -835,6 +835,29 @@ static void apple_dart_release_group(void *iommu_data)
mutex_unlock(&apple_dart_groups_lock);
}
static int apple_dart_merge_master_cfg(struct apple_dart_master_cfg *dst,
struct apple_dart_master_cfg *src)
{
/*
* We know that this function is only called for groups returned from
* pci_device_group and that all Apple Silicon platforms never spread
* PCIe devices from the same bus across multiple DARTs such that we can
* just assume that both src and dst only have the same single DART.
*/
if (src->stream_maps[1].dart)
return -EINVAL;
if (dst->stream_maps[1].dart)
return -EINVAL;
if (src->stream_maps[0].dart != dst->stream_maps[0].dart)
return -EINVAL;
bitmap_or(dst->stream_maps[0].sidmap,
dst->stream_maps[0].sidmap,
src->stream_maps[0].sidmap,
dst->stream_maps[0].dart->num_streams);
return 0;
}
static struct iommu_group *apple_dart_device_group(struct device *dev)
{
int i, sid;
@ -876,14 +899,28 @@ static struct iommu_group *apple_dart_device_group(struct device *dev)
if (!group)
goto out;
group_master_cfg = kmemdup(cfg, sizeof(*group_master_cfg), GFP_KERNEL);
if (!group_master_cfg) {
iommu_group_put(group);
goto out;
}
group_master_cfg = iommu_group_get_iommudata(group);
if (group_master_cfg) {
int ret;
iommu_group_set_iommudata(group, group_master_cfg,
apple_dart_release_group);
ret = apple_dart_merge_master_cfg(group_master_cfg, cfg);
if (ret) {
dev_err(dev, "Failed to merge DART IOMMU grups.\n");
iommu_group_put(group);
res = ERR_PTR(ret);
goto out;
}
} else {
group_master_cfg = kmemdup(cfg, sizeof(*group_master_cfg),
GFP_KERNEL);
if (!group_master_cfg) {
iommu_group_put(group);
goto out;
}
iommu_group_set_iommudata(group, group_master_cfg,
apple_dart_release_group);
}
for_each_stream_map(i, cfg, stream_map)
for_each_set_bit(sid, stream_map->sidmap, stream_map->dart->num_streams)