[PATCH v3 31/33] hw/core/fdt_generic_util: Add blockdev binding support

Ruslan Ruslichenko posted 33 patches 1 week, 1 day ago
Maintainers: Peter Maydell <peter.maydell@linaro.org>, Paolo Bonzini <pbonzini@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, Alistair Francis <alistair.francis@wdc.com>, David Gibson <david@gibson.dropbear.id.au>, Peter Xu <peterx@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>
[PATCH v3 31/33] hw/core/fdt_generic_util: Add blockdev binding support
Posted by Ruslan Ruslichenko 1 week, 1 day ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

The path add possibility to bind to QEMU block backends
via the blockdev-node-name property.

This enables support for the modern -blockdev command line option.

Since the generic FDT initialization traverses the device tree
inside  QEMU coroutines, realizing block devices immediately
causes conflict: QEMU's block subsystem and AIO (Asynchronous I/O)
are not compatible with being invoked from within these setup
coroutines. To resolve this block device initialization are
moved to the latest stage outside of co-routing context.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/core/fdt_generic_util.c | 77 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)

diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index ae54ada11d..7d074745b5 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -69,6 +69,8 @@ static void fdt_get_irq_info_from_intc(FDTMachineInfo *fdti, qemu_irq *ret,
                                        char *intc_node_path,
                                        uint32_t *cells, uint32_t num_cells,
                                        uint32_t max, Error **errp);
+static bool fdt_attach_blockdev(FDTMachineInfo *fdti,
+                                const char *node_path, Object *dev);
 
 typedef struct QEMUIRQSharedState {
     qemu_irq sink;
@@ -173,6 +175,19 @@ static void fdt_init_deferred(FDTMachineInfo *fdti)
 {
     while (fdti->deferred) {
         FDTDeferredNode *dnode = fdti->deferred;
+        int length = 0;
+
+        int offset = fdt_path_offset(fdti->fdt, dnode->node_path);
+        if (offset < 0) {
+            error_report("%s Couldn't find node %s: %s", __func__,
+                         dnode->node_path, fdt_strerror(offset));
+        }
+        const char *blockdev = fdt_stringlist_get(fdti->fdt, offset,
+                                              "blockdev-node-name", 0, &length);
+
+        if (blockdev && object_property_find(OBJECT(dnode->dev), "drive")) {
+            fdt_attach_blockdev(fdti, dnode->node_path, OBJECT(dnode->dev));
+        }
 
         fdt_debug("FDT: Deferred realize node: %s\n",
                  dnode->node_path);
@@ -212,6 +227,8 @@ FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
     current_machine->smp.cpus = fdt_generic_num_cpus;
     current_machine->smp.max_cpus = fdt_generic_num_cpus;
 
+    bdrv_drain_all();
+
     fdt_debug("FDT: Device tree scan complete\n");
     return fdti;
 }
@@ -707,6 +724,47 @@ static void fdt_init_qdev_array_prop(Object *obj,
     fdt_debug_np("set property %s propname to <list>\n", propname);
 }
 
+/*
+ * Try to attach by matching drive created by '-blockdev node-name=LABEL'
+ * iff the FDT node contains property 'blockdev-node-name=LABEL'.
+ *
+ * Return false unless the given node_path has the property.
+ *
+ */
+static bool fdt_attach_blockdev(FDTMachineInfo *fdti,
+                                const char *node_path, Object *dev)
+{
+    static const char propname[] = "blockdev-node-name";
+    const char *label;
+
+    /* Inspect FDT node for blockdev-only binding */
+    label = qemu_fdt_getprop(fdti->fdt, node_path, propname,
+                             NULL, NULL);
+
+    /* Skip legacy node */
+    if (!label) {
+        return false;
+    }
+
+    /*
+     * Missing matching bdev is not an error: attachment is optional.
+     *
+     * error_setg() aborts, never returns: 'return false' is just sanity.
+     */
+    if (!label[0]) {
+        error_setg(&error_abort, "FDT-node '%s': property '%s' = <empty>",
+                   node_path, propname);
+        return false;
+    }
+
+    if (!bdrv_find_node(label)) {
+        return false;
+    }
+
+    object_property_set_str(OBJECT(dev), "drive", label, NULL);
+    return true;
+}
+
 static void fdt_init_qdev_properties(char *node_path, FDTMachineInfo *fdti,
                                      Object *dev)
 {
@@ -1048,6 +1106,7 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
 {
     Object *dev, *parent;
     char *parent_node_path;
+    ObjectProperty *p;
 
     if (!compat) {
         return 1;
@@ -1086,12 +1145,30 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
 
     fdt_init_qdev_properties(node_path, fdti, dev);
 
+    /*
+     * In case device need to be attached to block backend,
+     * defer realize to latest stage, outside of co-routines,
+     * which are not compatible with AIO used by block subsystem
+     */
+    p = object_property_find(dev, "drive");
+    if (p && !strcmp(p->type, "str")) {
+        FDTDeferredNode *dnode = g_new0(FDTDeferredNode, 1);
+        *dnode = (FDTDeferredNode) {
+                .dev = DEVICE(dev),
+                .node_path = g_strdup(node_path),
+                .next = fdti->deferred
+        };
+        fdti->deferred = dnode;
+        goto exit;
+    }
+
     fdt_init_device_realize(fdti, node_path, dev);
 
     fdt_parse_node_reg_prop(fdti, node_path, dev);
 
     fdt_parse_node_irq_prop(fdti, node_path, dev);
 
+exit:
     g_free(parent_node_path);
 
     return 0;
-- 
2.43.0