iommu: Resolve fwspec ops automatically

There's no real need for callers to resolve ops from a fwnode in order
to then pass both to iommu_fwspec_init() - it's simpler and more sensible
for that to resolve the ops itself. This in turn means we can centralise
the notion of checking for a present driver, and enforce that fwspecs
aren't allocated unless and until we know they will be usable.

Also use this opportunity to modernise with some "new" helpers that
arrived shortly after this code was first written; the generic
fwnode_handle_get() clears up that ugly get/put mismatch, while
of_fwnode_handle() can now abstract those open-coded dereferences.

Tested-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Link: https://lore.kernel.org/r/0e2727adeb8cd73274425322f2f793561bdc927e.1719919669.git.robin.murphy@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
Robin Murphy 2024-07-02 12:40:48 +01:00 committed by Will Deacon
parent e7acc36f26
commit 3f7c320916
11 changed files with 30 additions and 61 deletions

View File

@ -1221,10 +1221,10 @@ static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node)
static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node, static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
u32 streamid) u32 streamid)
{ {
const struct iommu_ops *ops;
struct fwnode_handle *iort_fwnode; struct fwnode_handle *iort_fwnode;
if (!node) /* If there's no SMMU driver at all, give up now */
if (!node || !iort_iommu_driver_enabled(node->type))
return -ENODEV; return -ENODEV;
iort_fwnode = iort_get_fwnode(node); iort_fwnode = iort_get_fwnode(node);
@ -1232,19 +1232,10 @@ static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
return -ENODEV; return -ENODEV;
/* /*
* If the ops look-up fails, this means that either * If the SMMU drivers are enabled but not loaded/probed
* the SMMU drivers have not been probed yet or that * yet, this will defer.
* the SMMU drivers are not built in the kernel;
* Depending on whether the SMMU drivers are built-in
* in the kernel or not, defer the IOMMU configuration
* or just abort it.
*/ */
ops = iommu_ops_from_fwnode(iort_fwnode); return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode);
if (!ops)
return iort_iommu_driver_enabled(node->type) ?
-EPROBE_DEFER : -ENODEV;
return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode, ops);
} }
struct iort_pci_alias_info { struct iort_pci_alias_info {

View File

@ -1577,12 +1577,11 @@ int acpi_dma_get_range(struct device *dev, const struct bus_dma_region **map)
#ifdef CONFIG_IOMMU_API #ifdef CONFIG_IOMMU_API
int acpi_iommu_fwspec_init(struct device *dev, u32 id, int acpi_iommu_fwspec_init(struct device *dev, u32 id,
struct fwnode_handle *fwnode, struct fwnode_handle *fwnode)
const struct iommu_ops *ops)
{ {
int ret; int ret;
ret = iommu_fwspec_init(dev, fwnode, ops); ret = iommu_fwspec_init(dev, fwnode);
if (ret) if (ret)
return ret; return ret;
@ -1639,8 +1638,7 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
#else /* !CONFIG_IOMMU_API */ #else /* !CONFIG_IOMMU_API */
int acpi_iommu_fwspec_init(struct device *dev, u32 id, int acpi_iommu_fwspec_init(struct device *dev, u32 id,
struct fwnode_handle *fwnode, struct fwnode_handle *fwnode)
const struct iommu_ops *ops)
{ {
return -ENODEV; return -ENODEV;
} }

View File

@ -307,21 +307,14 @@ void __init acpi_viot_init(void)
static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu, static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
u32 epid) u32 epid)
{ {
const struct iommu_ops *ops; if (!viommu || !IS_ENABLED(CONFIG_VIRTIO_IOMMU))
if (!viommu)
return -ENODEV; return -ENODEV;
/* We're not translating ourself */ /* We're not translating ourself */
if (device_match_fwnode(dev, viommu->fwnode)) if (device_match_fwnode(dev, viommu->fwnode))
return -EINVAL; return -EINVAL;
ops = iommu_ops_from_fwnode(viommu->fwnode); return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode);
if (!ops)
return IS_ENABLED(CONFIG_VIRTIO_IOMMU) ?
-EPROBE_DEFER : -ENODEV;
return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, ops);
} }
static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data) static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)

View File

@ -178,8 +178,7 @@ static int arm_smmu_register_legacy_master(struct device *dev,
it.cur_count = 1; it.cur_count = 1;
} }
err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode, err = iommu_fwspec_init(dev, NULL);
&arm_smmu_ops);
if (err) if (err)
return err; return err;

View File

@ -17,6 +17,8 @@ static inline const struct iommu_ops *dev_iommu_ops(struct device *dev)
return dev->iommu->iommu_dev->ops; return dev->iommu->iommu_dev->ops;
} }
const struct iommu_ops *iommu_ops_from_fwnode(const struct fwnode_handle *fwnode);
int iommu_group_replace_domain(struct iommu_group *group, int iommu_group_replace_domain(struct iommu_group *group,
struct iommu_domain *new_domain); struct iommu_domain *new_domain);

View File

@ -2822,11 +2822,14 @@ const struct iommu_ops *iommu_ops_from_fwnode(const struct fwnode_handle *fwnode
return ops; return ops;
} }
int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode)
const struct iommu_ops *ops)
{ {
const struct iommu_ops *ops = iommu_ops_from_fwnode(iommu_fwnode);
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
if (!ops)
return -EPROBE_DEFER;
if (fwspec) if (fwspec)
return ops == fwspec->ops ? 0 : -EINVAL; return ops == fwspec->ops ? 0 : -EINVAL;
@ -2838,7 +2841,7 @@ int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
if (!fwspec) if (!fwspec)
return -ENOMEM; return -ENOMEM;
of_node_get(to_of_node(iommu_fwnode)); fwnode_handle_get(iommu_fwnode);
fwspec->iommu_fwnode = iommu_fwnode; fwspec->iommu_fwnode = iommu_fwnode;
fwspec->ops = ops; fwspec->ops = ops;
dev_iommu_fwspec_set(dev, fwspec); dev_iommu_fwspec_set(dev, fwspec);

View File

@ -412,7 +412,7 @@ static int mtk_iommu_v1_create_mapping(struct device *dev,
return -EINVAL; return -EINVAL;
} }
ret = iommu_fwspec_init(dev, &args->np->fwnode, &mtk_iommu_v1_ops); ret = iommu_fwspec_init(dev, of_fwnode_handle(args->np));
if (ret) if (ret)
return ret; return ret;

View File

@ -21,26 +21,19 @@ static int of_iommu_xlate(struct device *dev,
struct of_phandle_args *iommu_spec) struct of_phandle_args *iommu_spec)
{ {
const struct iommu_ops *ops; const struct iommu_ops *ops;
struct fwnode_handle *fwnode = &iommu_spec->np->fwnode;
int ret; int ret;
ops = iommu_ops_from_fwnode(fwnode); if (!of_device_is_available(iommu_spec->np))
if ((ops && !ops->of_xlate) ||
!of_device_is_available(iommu_spec->np))
return -ENODEV; return -ENODEV;
ret = iommu_fwspec_init(dev, fwnode, ops); ret = iommu_fwspec_init(dev, of_fwnode_handle(iommu_spec->np));
if (ret == -EPROBE_DEFER)
return driver_deferred_probe_check_state(dev);
if (ret) if (ret)
return ret; return ret;
/*
* The otherwise-empty fwspec handily serves to indicate the specific
* IOMMU device we're waiting for, which will be useful if we ever get
* a proper probe-ordering dependency mechanism in future.
*/
if (!ops)
return driver_deferred_probe_check_state(dev);
if (!try_module_get(ops->owner)) ops = dev_iommu_fwspec_get(dev)->ops;
if (!ops->of_xlate || !try_module_get(ops->owner))
return -ENODEV; return -ENODEV;
ret = ops->of_xlate(dev, iommu_spec); ret = ops->of_xlate(dev, iommu_spec);

View File

@ -837,7 +837,7 @@ static int tegra_smmu_configure(struct tegra_smmu *smmu, struct device *dev,
const struct iommu_ops *ops = smmu->iommu.ops; const struct iommu_ops *ops = smmu->iommu.ops;
int err; int err;
err = iommu_fwspec_init(dev, &dev->of_node->fwnode, ops); err = iommu_fwspec_init(dev, of_fwnode_handle(dev->of_node));
if (err < 0) { if (err < 0) {
dev_err(dev, "failed to initialize fwspec: %d\n", err); dev_err(dev, "failed to initialize fwspec: %d\n", err);
return err; return err;

View File

@ -736,8 +736,7 @@ struct iommu_ops;
bool acpi_dma_supported(const struct acpi_device *adev); bool acpi_dma_supported(const struct acpi_device *adev);
enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev); enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev);
int acpi_iommu_fwspec_init(struct device *dev, u32 id, int acpi_iommu_fwspec_init(struct device *dev, u32 id,
struct fwnode_handle *fwnode, struct fwnode_handle *fwnode);
const struct iommu_ops *ops);
int acpi_dma_get_range(struct device *dev, const struct bus_dma_region **map); int acpi_dma_get_range(struct device *dev, const struct bus_dma_region **map);
int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr, int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr,
const u32 *input_id); const u32 *input_id);

View File

@ -1005,11 +1005,9 @@ struct iommu_mm_data {
struct list_head sva_handles; struct list_head sva_handles;
}; };
int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode);
const struct iommu_ops *ops);
void iommu_fwspec_free(struct device *dev); void iommu_fwspec_free(struct device *dev);
int iommu_fwspec_add_ids(struct device *dev, const u32 *ids, int num_ids); int iommu_fwspec_add_ids(struct device *dev, const u32 *ids, int num_ids);
const struct iommu_ops *iommu_ops_from_fwnode(const struct fwnode_handle *fwnode);
static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev) static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
{ {
@ -1315,8 +1313,7 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
} }
static inline int iommu_fwspec_init(struct device *dev, static inline int iommu_fwspec_init(struct device *dev,
struct fwnode_handle *iommu_fwnode, struct fwnode_handle *iommu_fwnode)
const struct iommu_ops *ops)
{ {
return -ENODEV; return -ENODEV;
} }
@ -1331,12 +1328,6 @@ static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
return -ENODEV; return -ENODEV;
} }
static inline
const struct iommu_ops *iommu_ops_from_fwnode(const struct fwnode_handle *fwnode)
{
return NULL;
}
static inline int static inline int
iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features feat) iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features feat)
{ {