@@ -301,7 +301,7 @@ struct arm_smmu_group_cfg {
struct arm_smmu_master {
struct device_node *of_node;
- struct rb_node node;
+ struct list_head list;
struct arm_smmu_master_cfg cfg;
};
@@ -372,8 +372,8 @@ struct arm_smmu_domain {
static struct iommu_ops arm_smmu_ops;
-static DEFINE_SPINLOCK(arm_smmu_devices_lock);
-static LIST_HEAD(arm_smmu_devices);
+static DEFINE_RWLOCK(arm_smmu_masters_lock);
+static LIST_HEAD(arm_smmu_masters);
struct arm_smmu_option_prop {
u32 opt;
@@ -417,111 +417,20 @@ static struct device_node *dev_get_dev_node(struct device *dev)
return dev->of_node;
}
-static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
- struct device_node *dev_node)
+static struct arm_smmu_master *find_smmu_master(struct device_node *dev_node)
{
- struct rb_node *node = smmu->masters.rb_node;
-
- while (node) {
- struct arm_smmu_master *master;
-
- master = container_of(node, struct arm_smmu_master, node);
-
- if (dev_node < master->of_node)
- node = node->rb_left;
- else if (dev_node > master->of_node)
- node = node->rb_right;
- else
- return master;
- }
-
- return NULL;
-}
-
-static int insert_smmu_master(struct arm_smmu_device *smmu,
- struct arm_smmu_master *master)
-{
- struct rb_node **new, *parent;
-
- new = &smmu->masters.rb_node;
- parent = NULL;
- while (*new) {
- struct arm_smmu_master *this
- = container_of(*new, struct arm_smmu_master, node);
-
- parent = *new;
- if (master->of_node < this->of_node)
- new = &((*new)->rb_left);
- else if (master->of_node > this->of_node)
- new = &((*new)->rb_right);
- else
- return -EEXIST;
- }
-
- rb_link_node(&master->node, parent, new);
- rb_insert_color(&master->node, &smmu->masters);
- return 0;
-}
-
-static int register_smmu_master(struct arm_smmu_device *smmu,
- struct of_phandle_args *masterspec)
-{
- int i;
struct arm_smmu_master *master;
- master = find_smmu_master(smmu, masterspec->np);
- if (master) {
- dev_err(smmu->dev,
- "rejecting multiple registrations for master device %s\n",
- masterspec->np->name);
- return -EBUSY;
- }
+ read_lock(&arm_smmu_masters_lock);
+ list_for_each_entry(master, &arm_smmu_masters, list)
+ if (master->of_node == dev_node)
+ goto out_unlock;
- if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
- dev_err(smmu->dev,
- "reached maximum number (%d) of stream IDs for master device %s\n",
- MAX_MASTER_STREAMIDS, masterspec->np->name);
- return -ENOSPC;
- }
+ master = NULL;
- master = devm_kzalloc(smmu->dev, sizeof(*master), GFP_KERNEL);
- if (!master)
- return -ENOMEM;
-
- master->of_node = masterspec->np;
- master->cfg.num_streamids = masterspec->args_count;
-
- for (i = 0; i < master->cfg.num_streamids; ++i) {
- u16 streamid = masterspec->args[i];
-
- if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
- (streamid >= smmu->num_mapping_groups)) {
- dev_err(smmu->dev,
- "stream ID for master device %s greater than maximum allowed (%d)\n",
- masterspec->np->name, smmu->num_mapping_groups);
- return -ERANGE;
- }
- master->cfg.streamids[i].id = streamid;
- /* leave .mask 0; we don't currently share SMRs */
- }
- return insert_smmu_master(smmu, master);
-}
-
-static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
-{
- struct arm_smmu_device *smmu;
- struct arm_smmu_master *master = NULL;
- struct device_node *dev_node = dev_get_dev_node(dev);
-
- spin_lock(&arm_smmu_devices_lock);
- list_for_each_entry(smmu, &arm_smmu_devices, list) {
- master = find_smmu_master(smmu, dev_node);
- if (master)
- break;
- }
- spin_unlock(&arm_smmu_devices_lock);
-
- return master ? smmu : NULL;
+out_unlock:
+ read_unlock(&arm_smmu_masters_lock);
+ return master;
}
static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1327,36 +1236,26 @@ static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
return 0;
}
-static int arm_smmu_init_platform_device(struct arm_smmu_device *smmu,
- struct device *dev)
-{
- struct arm_smmu_master *master;
-
- master = find_smmu_master(smmu, dev_get_dev_node(dev));
- dev->archdata.iommu = &master->cfg;
-
- return 0;
-}
-
static int arm_smmu_add_device(struct device *dev)
{
struct iommu_group *group;
- struct arm_smmu_device *smmu;
- int ret;
+ struct arm_smmu_master *master;
if (dev->archdata.iommu)
return -EEXIST;
- smmu = find_smmu_for_device(dev);
- if (!smmu)
+ master = find_smmu_master(dev_get_dev_node(dev));
+ if (!master)
return -ENODEV;
- if (dev_is_pci(dev))
- ret = arm_smmu_init_pci_device(smmu, to_pci_dev(dev));
- else
- ret = arm_smmu_init_platform_device(smmu, dev);
- if (ret)
- return ret;
+ if (dev_is_pci(dev)) {
+ int ret = arm_smmu_init_pci_device(master->cfg.smmu,
+ to_pci_dev(dev));
+ if (ret)
+ return ret;
+ } else {
+ dev->archdata.iommu = &master->cfg;
+ }
group = iommu_group_get_for_dev(dev);
if (IS_ERR(group))
@@ -1752,19 +1651,79 @@ MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
static void arm_smmu_remove_mmu_masters(struct arm_smmu_device *smmu)
{
- struct arm_smmu_master *master;
- struct rb_node *node, *tmp;
+ struct arm_smmu_master *master, *tmp;
- node = rb_first(&smmu->masters);
- while (node) {
- tmp = node;
- node = rb_next(node);
- master = container_of(tmp, struct arm_smmu_master, node);
+ write_lock(&arm_smmu_masters_lock);
+ list_for_each_entry_safe(master, tmp, &arm_smmu_masters, list) {
+ if (master->cfg.smmu != smmu)
+ continue;
- rb_erase(tmp, &smmu->masters);
+ list_del(&master->list);
of_node_put(master->of_node);
devm_kfree(smmu->dev, master);
}
+ write_unlock(&arm_smmu_masters_lock);
+}
+
+static int insert_smmu_master(struct arm_smmu_master *master)
+{
+ struct arm_smmu_master *tmp;
+ int ret = -EEXIST;
+
+ write_lock(&arm_smmu_masters_lock);
+ list_for_each_entry(tmp, &arm_smmu_masters, list)
+ if (tmp->of_node == master->of_node)
+ goto out_unlock;
+
+ ret = 0;
+ list_add(&master->list, &arm_smmu_masters);
+out_unlock:
+ write_unlock(&arm_smmu_masters_lock);
+ return ret;
+}
+
+static int register_smmu_master(struct arm_smmu_device *smmu,
+ struct of_phandle_args *masterspec)
+{
+ int i;
+ struct arm_smmu_master *master;
+
+ master = find_smmu_master(masterspec->np);
+ if (master) {
+ dev_err(smmu->dev,
+ "rejecting multiple registrations for master device %s\n",
+ masterspec->np->name);
+ return -EBUSY;
+ }
+
+ if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
+ dev_err(smmu->dev,
+ "reached maximum number (%d) of stream IDs for master device %s\n",
+ MAX_MASTER_STREAMIDS, masterspec->np->name);
+ return -ENOSPC;
+ }
+
+ master = devm_kzalloc(smmu->dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ master->of_node = masterspec->np;
+ master->cfg.num_streamids = masterspec->args_count;
+
+ for (i = 0; i < master->cfg.num_streamids; ++i) {
+ u16 streamid = masterspec->args[i];
+
+ if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
+ (streamid >= smmu->num_mapping_groups)) {
+ dev_err(smmu->dev,
+ "stream ID for master device %s greater than maximum allowed (%d)\n",
+ masterspec->np->name, smmu->num_mapping_groups);
+ return -ERANGE;
+ }
+ master->cfg.streamids[i].id = streamid;
+ /* leave .mask 0; we don't currently share SMRs */
+ }
+ return insert_smmu_master(master);
}
static int arm_smmu_probe_mmu_masters(struct arm_smmu_device *smmu)
@@ -1880,11 +1839,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, smmu);
arm_smmu_probe_mmu_masters(smmu);
- INIT_LIST_HEAD(&smmu->list);
- spin_lock(&arm_smmu_devices_lock);
- list_add(&smmu->list, &arm_smmu_devices);
- spin_unlock(&arm_smmu_devices_lock);
-
arm_smmu_device_reset(smmu);
return 0;
@@ -1903,9 +1857,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
return -ENODEV;
arm_smmu_remove_mmu_masters(smmu);
- spin_lock(&arm_smmu_devices_lock);
- list_del(&smmu->list);
- spin_unlock(&arm_smmu_devices_lock);
if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
dev_err(&pdev->dev, "removing device with active domains!\n");
With the preceding data reshuffles allowing SMMU instances to be looked up directly wherever needed, we can now wipe out the last vestiges of the global SMMU list. With mmu-masters lookup now a one-time thing outside of any real hotpath, condense our stash of those out of the SMMU instance data down to a single shared list, where they may await their eventual deprecation. This also seems like a good opportunity to move the parts only relevant to the probe/remove routines down to live in that area of the code. Signed-off-by: Robin Murphy <robin.murphy@arm.com> --- drivers/iommu/arm-smmu.c | 231 +++++++++++++++++++---------------------------- 1 file changed, 91 insertions(+), 140 deletions(-)