diff options
| author | Linux Build Service Account <lnxbuild@quicinc.com> | 2017-05-30 16:04:25 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-05-30 16:04:24 -0700 |
| commit | 4cc716041e2eedc07824e725966dbff29abd7fb6 (patch) | |
| tree | 81d1d1eb2103c6fcce983725bf4cd687f406616c | |
| parent | 22d89182eaf9ce50c14199e796abc8c4a89d13ff (diff) | |
| parent | 3b3b2f468caca6415b72de0df3c37552db33bf55 (diff) | |
Merge "iommu/arm-smmu: Optimized IOMMUS property parsing"
| -rw-r--r-- | drivers/iommu/arm-smmu.c | 153 |
1 files changed, 114 insertions, 39 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index dc44b40a85f3..065379c1397f 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -497,6 +497,19 @@ struct static_cbndx_entry { u8 type; }; +struct arm_iommus_node { + struct device_node *master; + struct list_head list; + struct list_head iommuspec_list; +}; + +struct arm_iommus_spec { + struct of_phandle_args iommu_spec; + struct list_head list; +}; + +static LIST_HEAD(iommus_nodes); + static int arm_smmu_enable_clocks_atomic(struct arm_smmu_device *smmu); static void arm_smmu_disable_clocks_atomic(struct arm_smmu_device *smmu); static void arm_smmu_prepare_pgtable(void *addr, void *cookie); @@ -683,39 +696,40 @@ static int register_smmu_master(struct arm_smmu_device *smmu, return insert_smmu_master(smmu, master); } -static int arm_smmu_parse_iommus_properties(struct arm_smmu_device *smmu, - int *num_masters) +static int arm_smmu_parse_iommus_properties(struct arm_smmu_device *smmu) { - struct of_phandle_args iommuspec; - struct device_node *master; - - *num_masters = 0; + struct arm_iommus_node *node, *nex; - for_each_node_with_property(master, "iommus") { - int arg_ind = 0; - struct iommus_entry *entry, *n; + list_for_each_entry_safe(node, nex, &iommus_nodes, list) { + struct iommus_entry *entry, *next; + struct arm_iommus_spec *iommuspec_node, *n; LIST_HEAD(iommus); + int node_found = 0; - while (!of_parse_phandle_with_args( - master, "iommus", "#iommu-cells", - arg_ind, &iommuspec)) { - if (iommuspec.np != smmu->dev->of_node) { - arg_ind++; + list_for_each_entry_safe(iommuspec_node, n, + &node->iommuspec_list, list) { + if (iommuspec_node->iommu_spec.np != smmu->dev->of_node) continue; - } + + /* + * Since each master node will have iommu spec(s) of the + * same device, we can delete this master node after + * the devices are registered. + */ + node_found = 1; list_for_each_entry(entry, &iommus, list) - if (entry->node == master) + if (entry->node == node->master) break; if (&entry->list == &iommus) { entry = devm_kzalloc(smmu->dev, sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; - entry->node = master; + entry->node = node->master; list_add(&entry->list, &iommus); } - switch (iommuspec.args_count) { + switch (iommuspec_node->iommu_spec.args_count) { case 0: /* * For pci-e devices the SIDs are provided @@ -725,25 +739,29 @@ static int arm_smmu_parse_iommus_properties(struct arm_smmu_device *smmu, case 1: entry->num_sids++; entry->streamids[entry->num_sids - 1] - = iommuspec.args[0]; + = iommuspec_node->iommu_spec.args[0]; break; default: BUG(); } - arg_ind++; + list_del(&iommuspec_node->list); + kfree(iommuspec_node); } - list_for_each_entry_safe(entry, n, &iommus, list) { + list_for_each_entry_safe(entry, next, &iommus, list) { int rc = register_smmu_master(smmu, entry); - if (rc) { + + if (rc) dev_err(smmu->dev, "Couldn't register %s\n", - entry->node->name); - } else { - (*num_masters)++; - } + entry->node->name); list_del(&entry->list); devm_kfree(smmu->dev, entry); } + + if (node_found) { + list_del(&node->list); + kfree(node); + } } return 0; @@ -3973,7 +3991,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) struct arm_smmu_device *smmu; struct device *dev = &pdev->dev; struct rb_node *node; - int num_irqs, i, err, num_masters; + int num_irqs, i, err; smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); if (!smmu) { @@ -4036,33 +4054,33 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) } i = 0; - smmu->masters = RB_ROOT; - err = arm_smmu_parse_iommus_properties(smmu, &num_masters); - if (err) - goto out_put_masters; - - dev_dbg(dev, "registered %d master devices\n", num_masters); err = arm_smmu_parse_impl_def_registers(smmu); if (err) - goto out_put_masters; + goto out; err = arm_smmu_init_regulators(smmu); if (err) - goto out_put_masters; + goto out; err = arm_smmu_init_clocks(smmu); if (err) - goto out_put_masters; + goto out; err = arm_smmu_init_bus_scaling(pdev, smmu); if (err) - goto out_put_masters; + goto out; parse_driver_options(smmu); err = arm_smmu_enable_clocks(smmu); if (err) + goto out; + + /* No probe deferral occurred! Proceed with iommu property parsing. */ + smmu->masters = RB_ROOT; + err = arm_smmu_parse_iommus_properties(smmu); + if (err) goto out_put_masters; smmu->sec_id = msm_dev_to_device_id(dev); @@ -4122,7 +4140,7 @@ out_put_masters: = container_of(node, struct arm_smmu_master, node); of_node_put(master->of_node); } - +out: return err; } @@ -4173,6 +4191,58 @@ static int arm_smmu_device_remove(struct platform_device *pdev) return 0; } +static void arm_smmu_free_master_nodes(void) +{ + struct arm_iommus_node *node, *nex; + struct arm_iommus_spec *entry, *n; + + list_for_each_entry_safe(node, nex, &iommus_nodes, list) { + list_for_each_entry_safe(entry, n, + &node->iommuspec_list, list) { + list_del(&entry->list); + kfree(entry); + } + list_del(&node->list); + kfree(node); + } +} + +static int arm_smmu_get_master_nodes(void) +{ + struct arm_iommus_node *node; + struct device_node *master; + struct of_phandle_args iommuspec; + struct arm_iommus_spec *entry; + + for_each_node_with_property(master, "iommus") { + int arg_ind = 0; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + goto release_memory; + node->master = master; + list_add(&node->list, &iommus_nodes); + + INIT_LIST_HEAD(&node->iommuspec_list); + + while (!of_parse_phandle_with_args(master, "iommus", + "#iommu-cells", arg_ind, &iommuspec)) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + goto release_memory; + entry->iommu_spec = iommuspec; + list_add(&entry->list, &node->iommuspec_list); + arg_ind++; + } + } + + return 0; + +release_memory: + arm_smmu_free_master_nodes(); + return -ENOMEM; +} + static struct platform_driver arm_smmu_driver = { .driver = { .name = "arm-smmu", @@ -4198,10 +4268,15 @@ static int __init arm_smmu_init(void) of_node_put(np); - ret = platform_driver_register(&arm_smmu_driver); + ret = arm_smmu_get_master_nodes(); if (ret) return ret; + ret = platform_driver_register(&arm_smmu_driver); + if (ret) { + arm_smmu_free_master_nodes(); + return ret; + } /* Oh, for a proper bus abstraction */ if (!iommu_present(&platform_bus_type)) bus_set_iommu(&platform_bus_type, &arm_smmu_ops); |
