@@ -21,6 +21,12 @@
#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)
@@ -43,7 +49,7 @@ void __init iommu_set_ops(const struct iommu_ops *ops)
int __init iommu_hardware_setup(void)
{
- struct dt_device_node *np;
+ struct dt_device_node *np, *tmp;
int rc;
unsigned int num_iommus = 0;
@@ -52,6 +58,21 @@ int __init iommu_hardware_setup(void)
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
@@ -62,7 +83,38 @@ int __init iommu_hardware_setup(void)
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)
@@ -44,7 +44,11 @@ struct device_desc {
enum device_class class;
/* List of devices supported by this driver */
const struct dt_device_match *dt_match;
- /* Device initialization */
+ /*
+ * Device initialization.
+ *
+ * -EAGAIN is used to indicate that device probing is deferred.
+ */
int (*init)(struct dt_device_node *dev, const void *data);
};
@@ -92,6 +92,13 @@ struct dt_device_node {
/* IOMMU specific fields */
bool is_protected;
+ /*
+ * The main purpose of this list is to link the structure in the list
+ * of devices assigned to domain.
+ *
+ * Boot code (iommu_hardware_setup) re-uses this list to link the structure
+ * in the list of devices for which driver requested deferred probing.
+ */
struct list_head domain_list;
struct device dev;