mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
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:
@@ -835,6 +835,29 @@ static void apple_dart_release_group(void *iommu_data)
|
|||||||
mutex_unlock(&apple_dart_groups_lock);
|
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)
|
static struct iommu_group *apple_dart_device_group(struct device *dev)
|
||||||
{
|
{
|
||||||
int i, sid;
|
int i, sid;
|
||||||
@@ -876,7 +899,20 @@ static struct iommu_group *apple_dart_device_group(struct device *dev)
|
|||||||
if (!group)
|
if (!group)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
group_master_cfg = kmemdup(cfg, sizeof(*group_master_cfg), GFP_KERNEL);
|
group_master_cfg = iommu_group_get_iommudata(group);
|
||||||
|
if (group_master_cfg) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
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) {
|
if (!group_master_cfg) {
|
||||||
iommu_group_put(group);
|
iommu_group_put(group);
|
||||||
goto out;
|
goto out;
|
||||||
@@ -884,6 +920,7 @@ static struct iommu_group *apple_dart_device_group(struct device *dev)
|
|||||||
|
|
||||||
iommu_group_set_iommudata(group, group_master_cfg,
|
iommu_group_set_iommudata(group, group_master_cfg,
|
||||||
apple_dart_release_group);
|
apple_dart_release_group);
|
||||||
|
}
|
||||||
|
|
||||||
for_each_stream_map(i, cfg, stream_map)
|
for_each_stream_map(i, cfg, stream_map)
|
||||||
for_each_set_bit(sid, stream_map->sidmap, stream_map->dart->num_streams)
|
for_each_set_bit(sid, stream_map->sidmap, stream_map->dart->num_streams)
|
||||||
|
|||||||
Reference in New Issue
Block a user