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:
parent
d8bcc870d9
commit
cf5c1c87c2
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user