#include <asm/device.h>
+/*
+ * Deferred probe list is used to keep track of devices for which driver
+ * requested deferred probing (returned -EAGAIN).
+ */
+static __initdata LIST_HEAD(deferred_probe_list);
+
static const struct iommu_ops *iommu_ops;
const struct iommu_ops *iommu_get_ops(void)
int __init iommu_hardware_setup(void)
{
- struct dt_device_node *np;
+ struct dt_device_node *np, *tmp;
int rc;
unsigned int num_iommus = 0;
rc = device_init(np, DEVICE_IOMMU, NULL);
if ( !rc )
num_iommus++;
+ else if ( rc == -EAGAIN )
+ {
+ /*
+ * Nobody should use device's domain_list at such early stage,
+ * so we can re-use it to link the device in the deferred list to
+ * avoid introducing extra list_head field in struct dt_device_node.
+ */
+ ASSERT(list_empty(&np->domain_list));
+
+ /*
+ * Driver requested deferred probing, so add this device to
+ * the deferred list for further processing.
+ */
+ list_add(&np->domain_list, &deferred_probe_list);
+ }
/*
* Ignore the following error codes:
* - EBADF: Indicate the current is not an IOMMU
return rc;
}
- return ( num_iommus > 0 ) ? 0 : -ENODEV;
+ /* Return immediately if there are no initialized devices. */
+ if ( !num_iommus )
+ return list_empty(&deferred_probe_list) ? -ENODEV : -EAGAIN;
+
+ rc = 0;
+
+ /*
+ * Process devices in the deferred list if it is not empty.
+ * Check that at least one device is initialized at each loop, otherwise
+ * we may get an infinite loop. Also stop processing if we got an error
+ * other than -EAGAIN.
+ */
+ while ( !list_empty(&deferred_probe_list) && num_iommus )
+ {
+ num_iommus = 0;
+
+ list_for_each_entry_safe ( np, tmp, &deferred_probe_list, domain_list )
+ {
+ rc = device_init(np, DEVICE_IOMMU, NULL);
+ if ( !rc )
+ {
+ num_iommus++;
+
+ /* Remove initialized device from the deferred list. */
+ list_del_init(&np->domain_list);
+ }
+ else if ( rc != -EAGAIN )
+ return rc;
+ }
+ }
+
+ return rc;
}
void __hwdom_init arch_iommu_check_autotranslated_hwdom(struct domain *d)