All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexey Kardashevskiy <aik@ozlabs.ru>
To: David Gibson <david@gibson.dropbear.id.au>,
	agraf@suse.de, crosthwaite.peter@gmail.com
Cc: qemu-ppc@nongnu.org, qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [RFC for-2.7 01/11] qdt: IEEE1275-style device tree utility code
Date: Thu, 21 Apr 2016 16:01:16 +1000	[thread overview]
Message-ID: <57186CAC.2080100@ozlabs.ru> (raw)
In-Reply-To: <1461119601-4936-2-git-send-email-david@gibson.dropbear.id.au>

On 04/20/2016 12:33 PM, David Gibson wrote:
> A number of guests supported by qemu use IEEE12750 (Open Firmware)
> style device trees for hardware discovery.  In some cases (mac99,
> pseries) they get this device tree via calls to an in-guest Open
> Firmware implementation, in others (many ppc and arm embedded
> machines) they consume the flattened ("fdt" or "dtb") format described
> in ePAPR amongst other places.  In some cases this device tree is
> constructed in guest firmware, in others by qemu and in some cases
> it's a hybrid.
>
> The upshot is that a number of qemu machine types need to construct
> and/or manipulate device trees.  Particularly with the pseries machine
> type, the complexity of manipulation it needs has been gradually
> increasing over time.
>
> For now, these machine types have generally worked with the device
> tree in the flattened format, using either libfdt directly or the
> wrappers in device_tree.c.  However, the fdt format was designed
> primarily for transferring device trees between components, not for
> runtime manipulation:
>      * fdt provides no persistent handles on nodes
>           Nodes are referenced using offsets in the stream which can be
>           altered by insertions or deletions elsewhere in the tree
>      * libfdt operations can fail
>           At any time the fdt lives in a limited size buffer, and
>           operations can fail if it fills.  This can be handled by
>           resizing the buffer, but that means logic to catch this case
>           on essentially every fdt operation, which is fiddly (in
>           practice we usually just allocate a buffer with plenty of
>           space and treat failures as fatal errors).
>      * fdt manipulation is slow
>           This probably isn't a problem in practice, but because it
>           uses memmove() liberally, even trivial operations on an fdt
>           are usually O(n) in the size of the whole tree.  This can
>           often make the obvious way of constructing the tree O(n^2) or
>           worse.  This could cause noticeable slow downs if someone
>           builds a guest with hundreds or thousands of devices, which
>           is an unusual but not unreasonable use case.
>
> This patch introduces a new utility library "qdt" for runtime
> manipulation of device trees.  The intention is that machine type code
> can use these routines to construct the device tree conveniently,
> using a pointer-based representation doesn't have the limitations
> above.  They can then use qdt_flatten() to convert the completed tree
> to fdt format as a single O(n) operation to pass to the guest.
>
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>


With few comments,


Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>




> ---
>   include/qemu/qdt.h | 102 +++++++++++++++++++++
>   util/Makefile.objs |   1 +
>   util/qdt.c         | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 365 insertions(+)
>   create mode 100644 include/qemu/qdt.h
>   create mode 100644 util/qdt.c
>
> diff --git a/include/qemu/qdt.h b/include/qemu/qdt.h
> new file mode 100644
> index 0000000..bff7143
> --- /dev/null
> +++ b/include/qemu/qdt.h
> @@ -0,0 +1,102 @@
> +/*
> + * Functions for manipulating IEEE1275 (Open Firmware) style device
> + * trees.
> + *
> + * Copyright David Gibson, Red Hat Inc. 2016
> + *
> + * This work is licensed unter the GNU GPL version 2 or (at your
> + * option) any later version.
> + */
> +#ifndef QEMU_QDT_H__
> +#define QEMU_QDT_H__
> +
> +#include <string.h>
> +#include <stdint.h>
> +#include <glib.h>
> +#include "qemu/queue.h"
> +
> +typedef struct QDTProperty QDTProperty;
> +typedef struct QDTNode QDTNode;
> +
> +struct QDTProperty {
> +    gchar *name;
> +    QTAILQ_ENTRY(QDTProperty) list;
> +    gsize len;
> +    uint8_t val[];
> +};
> +
> +struct QDTNode {
> +    gchar *name;
> +    QDTNode *parent;
> +    QTAILQ_HEAD(, QDTProperty) properties;
> +    QTAILQ_HEAD(, QDTNode) children;
> +    QTAILQ_ENTRY(QDTNode) sibling;
> +};
> +
> +/*
> + * Node functions
> + */
> +
> +QDTNode *qdt_new_node(const gchar *name);
> +QDTNode *qdt_get_node_relative(QDTNode *node, const gchar *path);
> +QDTNode *qdt_get_node(QDTNode *root, const gchar *path);


Do you really need both qdt_get_node_relative and qdt_get_node? It would 
make sense (a little) if qdt_get_node() accepted path without leading "/" 
but it does not.


> +QDTNode *qdt_add_subnode(QDTNode *parent, const gchar *name);
> +
> +static inline QDTNode *qdt_new_tree(void)
> +{
> +    return qdt_new_node("");
> +}
> +
> +/*
> + * Property functions
> + */
> +
> +QDTProperty *qdt_new_property(const gchar *name, gconstpointer val, gsize len);
> +const QDTProperty *qdt_getprop(const QDTNode *node, const gchar *name);
> +void qdt_delprop(QDTNode *node, const gchar *name);
> +const QDTProperty *qdt_setprop(QDTNode *node, const gchar *name,
> +                               gconstpointer val, gsize len);
> +const QDTProperty *qdt_setprop_cells_(QDTNode *node, const gchar *name,
> +                                      const uint32_t *val, gsize len);
> +const QDTProperty *qdt_setprop_u64s_(QDTNode *node, const char *name,


s/char/gchar/ ?


> +                                     const uint64_t *val, gsize len);
> +const QDTProperty *qdt_setprop_string(QDTNode *node, const gchar *name,
> +                                      const gchar *val);
> +void qdt_set_phandle(QDTNode *node, uint32_t phandle);
> +
> +#define qdt_setprop_bytes(node, name, ...)                              \
> +    ({                                                                  \
> +        uint8_t vals[] = { __VA_ARGS__ };                               \
> +        qdt_setprop((node), (name), vals, sizeof(vals));                \
> +    })
> +#define qdt_setprop_cells(node, name, ...)                              \
> +    ({                                                                  \
> +        uint32_t vals[] = { __VA_ARGS__ };                              \
> +        qdt_setprop_cells_((node), (name),                              \
> +                           vals, sizeof(vals) / sizeof(vals[0]));       \


Nit: there is ARRAY_SIZE(x).


> +    })
> +#define qdt_setprop_u64s(node, name, ...)                               \
> +    ({                                                                  \
> +        uint64_t vals[] = { __VA_ARGS__ };                              \
> +        qdt_setprop_u64s_((node), (name),                               \
> +                          vals, sizeof(vals) / sizeof(vals[0]));        \
> +    })
> +static inline const QDTProperty *qdt_setprop_empty(QDTNode *node,
> +                                                   const gchar *name)
> +{
> +    return qdt_setprop_bytes(node, name);
> +}
> +static inline const QDTProperty *qdt_setprop_dup(QDTNode *node,
> +                                                 const gchar *name,
> +                                                 const QDTProperty *oldprop)

Out of curiosity - when could I possibly want to use this?


> +{
> +    return qdt_setprop(node, name, oldprop->val, oldprop->len);
> +}
> +
> +/*
> + * Whole tree functions
> + */
> +
> +void *qdt_flatten(QDTNode *root, gsize bufsize, Error **errp);
> +
> +#endif /* QEMU_QDT_H__ */
> diff --git a/util/Makefile.objs b/util/Makefile.objs
> index a8a777e..f1d639f 100644
> --- a/util/Makefile.objs
> +++ b/util/Makefile.objs
> @@ -32,3 +32,4 @@ util-obj-y += buffer.o
>   util-obj-y += timed-average.o
>   util-obj-y += base64.o
>   util-obj-y += log.o
> +util-obj-y += qdt.o
> diff --git a/util/qdt.c b/util/qdt.c
> new file mode 100644
> index 0000000..e3a449a
> --- /dev/null
> +++ b/util/qdt.c
> @@ -0,0 +1,262 @@
> +/*
> + * Functions for manipulating IEEE1275 (Open Firmware) style device
> + * trees.
> + *
> + * Copyright David Gibson, Red Hat Inc. 2016
> + *
> + * This work is licensed unter the GNU GPL version 2 or (at your
> + * option) any later version.
> + */
> +
> +#include <libfdt.h>
> +#include <stdbool.h>
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/qdt.h"
> +#include "qemu/error-report.h"
> +
> +/*
> + * Node functions
> + */
> +
> +QDTNode *qdt_new_node(const gchar *name)
> +{
> +    QDTNode *node = g_new0(QDTNode, 1);
> +
> +    g_assert(!strchr(name, '/'));
> +
> +    node->name = g_strdup(name);
> +    QTAILQ_INIT(&node->properties);
> +    QTAILQ_INIT(&node->children);
> +
> +    return node;
> +}
> +
> +static QDTNode *get_subnode(QDTNode *parent, const gchar *name, size_t namelen)
> +{
> +    QDTNode *child;
> +
> +    g_assert(!memchr(name, '/', namelen));
> +
> +    QTAILQ_FOREACH(child, &parent->children, sibling) {
> +        if ((strlen(child->name) == namelen)
> +            && (memcmp(child->name, name, namelen) == 0)) {
> +            return child;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +QDTNode *qdt_get_node_relative(QDTNode *node, const gchar *path)
> +{
> +    const gchar *slash;
> +    gsize seglen;
> +
> +    do {
> +        slash = strchr(path, '/');
> +        seglen = slash ? slash - path : strlen(path);
> +
> +        node = get_subnode(node, path, seglen);
> +        path += seglen + 1;
> +    } while (node && slash);
> +
> +    return node;
> +}
> +
> +QDTNode *qdt_get_node(QDTNode *root, const gchar *path)
> +{
> +    g_assert(!root->parent);
> +    g_assert(path[0] == '/');
> +    return qdt_get_node_relative(root, path + 1);
> +}
> +
> +QDTNode *qdt_add_subnode(QDTNode *parent, const gchar *name)
> +{
> +    QDTNode *new = qdt_new_node(name);
> +
> +    new->parent = parent;
> +    QTAILQ_INSERT_TAIL(&parent->children, new, sibling);
> +    return new;
> +}
> +
> +/*
> + * Property functions
> + */
> +
> +QDTProperty *qdt_new_property(const gchar *name, gconstpointer val, gsize len)
> +{
> +    QDTProperty *prop = g_malloc0(sizeof(*prop) + len);
> +
> +    prop->name = g_strdup(name);
> +    prop->len = len;
> +    memcpy(prop->val, val, len);
> +    return prop;
> +}
> +
> +static QDTProperty *getprop_(const QDTNode *node, const gchar *name)
> +{
> +    QDTProperty *prop;
> +
> +    QTAILQ_FOREACH(prop, &node->properties, list) {
> +        if (strcmp(prop->name, name) == 0) {
> +            return prop;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +const QDTProperty *qdt_getprop(const QDTNode *node, const gchar *name)
> +{
> +    return getprop_(node, name);
> +}
> +
> +void qdt_delprop(QDTNode *node, const gchar *name)
> +{
> +    QDTProperty *prop = getprop_(node, name);
> +
> +    if (prop) {
> +        QTAILQ_REMOVE(&node->properties, prop, list);
> +        g_free(prop->name);
> +        g_free(prop);
> +    }
> +}
> +
> +const QDTProperty *qdt_setprop(QDTNode *node, const gchar *name,
> +                               gconstpointer val, gsize len)
> +{
> +    QDTProperty *prop;
> +
> +    qdt_delprop(node, name);
> +
> +    prop = g_malloc0(sizeof(*prop) + len);
> +    prop->name = g_strdup(name);
> +    prop->len = len;
> +    memcpy(prop->val, val, len);
> +    QTAILQ_INSERT_TAIL(&node->properties, prop, list);
> +    return prop;
> +}
> +
> +const QDTProperty *qdt_setprop_string(QDTNode *node, const gchar *name,
> +                                      const gchar *val)
> +{
> +    return qdt_setprop(node, name, val, strlen(val) + 1);
> +}
> +
> +const QDTProperty *qdt_setprop_cells_(QDTNode *node, const gchar *name,
> +                                      const uint32_t *val, gsize len)
> +{
> +    uint32_t swapval[len];
> +    gsize i;
> +
> +    for (i = 0; i < len; i++) {
> +        swapval[i] = cpu_to_fdt32(val[i]);
> +    }
> +    return qdt_setprop(node, name, swapval, sizeof(swapval));
> +}
> +
> +const QDTProperty *qdt_setprop_u64s_(QDTNode *node, const char *name,
> +                                     const uint64_t *val, gsize len)
> +{
> +    uint64_t swapval[len];
> +    gsize i;
> +
> +    for (i = 0; i < len; i++) {
> +        swapval[i] = cpu_to_fdt64(val[i]);
> +    }
> +    return qdt_setprop(node, name, swapval, sizeof(swapval));
> +}
> +
> +void qdt_set_phandle(QDTNode *node, uint32_t phandle)
> +{
> +    g_assert((phandle != 0) && (phandle != (uint32_t)-1));
> +    qdt_setprop_cells(node, "linux,phandle", phandle);
> +    qdt_setprop_cells(node, "phandle", phandle);
> +}
> +
> +/*
> + * Whole tree functions
> + */
> +
> +static void qdt_flatten_node(void *fdt, QDTNode *node, Error **errp)
> +{
> +    QDTProperty *prop;
> +    QDTNode *subnode;
> +    Error *local_err = NULL;
> +    int ret;
> +
> +    ret = fdt_begin_node(fdt, node->name);
> +    if (ret < 0) {
> +        error_setg(errp, "Error flattening device tree: fdt_begin_node(): %s",
> +                   fdt_strerror(ret));
> +        return;
> +    }
> +
> +    QTAILQ_FOREACH(prop, &node->properties, list) {
> +        ret = fdt_property(fdt, prop->name, prop->val, prop->len);
> +        if (ret < 0) {
> +            error_setg(errp, "Error flattening device tree: fdt_property(): %s",
> +                       fdt_strerror(ret));
> +            return;
> +        }
> +    }
> +
> +    QTAILQ_FOREACH(subnode, &node->children, sibling) {
> +        qdt_flatten_node(fdt, subnode, &local_err);
> +        if (local_err) {
> +            error_propagate(errp, local_err);
> +            return;
> +        }
> +    }
> +
> +    ret = fdt_end_node(fdt);
> +    if (ret < 0) {
> +        error_setg(errp, "Error flattening device tree: fdt_end_node(): %s",
> +                   fdt_strerror(ret));
> +        return;
> +    }
> +}
> +
> +void *qdt_flatten(QDTNode *root, gsize bufsize, Error **errp)
> +{
> +    void *fdt = g_malloc0(bufsize);
> +    Error *local_err = NULL;
> +    int ret;
> +
> +    assert(!root->parent); /* Should be a root node */
> +
> +    ret = fdt_create(fdt, bufsize);
> +    if (ret < 0) {
> +        error_setg(errp, "Error flattening device tree: fdt_create(): %s",
> +                   fdt_strerror(ret));
> +        goto fail;
> +    }
> +
> +    ret = fdt_finish_reservemap(fdt);
> +    if (ret < 0) {
> +        error_setg(errp,
> +                   "Error flattening device tree: fdt_finish_reservemap(): %s",
> +                   fdt_strerror(ret));
> +        goto fail;
> +    }
> +
> +    qdt_flatten_node(fdt, root, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        goto fail;
> +    }
> +
> +    ret = fdt_finish(fdt);
> +    if (ret < 0) {
> +        error_setg(errp, "Error flattening device tree: fdt_finish(): %s",
> +                   fdt_strerror(ret));
> +        goto fail;
> +    }
> +
> +    return fdt;
> +
> +fail:
> +    g_free(fdt);
> +    return NULL;
> +}
>


-- 
Alexey

  reply	other threads:[~2016-04-21  6:01 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-20  2:33 [Qemu-devel] [RFC for-2.7 00/11] A new infrastructure for guest device trees David Gibson
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 01/11] qdt: IEEE1275-style device tree utility code David Gibson
2016-04-21  6:01   ` Alexey Kardashevskiy [this message]
2016-04-22  4:15     ` David Gibson
2016-04-26 11:00   ` Thomas Huth
2016-04-27  6:02     ` David Gibson
2016-04-27  6:43       ` Markus Armbruster
2016-04-27  7:06         ` Thomas Huth
2016-04-27  7:28           ` Markus Armbruster
2016-04-27  7:56             ` Thomas Huth
2016-04-27  8:36               ` Markus Armbruster
2016-04-27 23:49             ` David Gibson
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 02/11] pseries: Split device tree construction from device tree load David Gibson
2016-04-20 18:15   ` Thomas Huth
2016-04-21  5:31   ` Alexey Kardashevskiy
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 03/11] pseries: Remove rtas_addr and fdt_addr fields from machinestate David Gibson
2016-04-20 18:19   ` Thomas Huth
2016-04-21  5:32   ` Alexey Kardashevskiy
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 04/11] pseries: Make spapr_create_fdt_skel() get information from machine state David Gibson
2016-04-21  5:32   ` Alexey Kardashevskiy
2016-04-26 17:41   ` Thomas Huth
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 05/11] pseries: Build device tree only at reset time David Gibson
2016-04-21  5:32   ` Alexey Kardashevskiy
2016-04-26 18:13   ` Thomas Huth
2016-04-27  6:07     ` David Gibson
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 06/11] pseries: Consolidate RTAS loading David Gibson
2016-04-21  5:32   ` Alexey Kardashevskiy
2016-04-27  9:12   ` Thomas Huth
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 07/11] pseries: Move adding of fdt reserve map entries David Gibson
2016-04-21  5:14   ` Alexey Kardashevskiy
2016-04-21  5:52     ` David Gibson
2016-04-21  6:03       ` Alexey Kardashevskiy
2016-04-22  4:22         ` David Gibson
2016-04-27  9:19   ` Thomas Huth
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 08/11] pseries: Start using qdt library for building device tree David Gibson
2016-04-21  4:04   ` Alexey Kardashevskiy
2016-04-27  6:13     ` David Gibson
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 09/11] pseries: Consolidate construction of /chosen device tree node David Gibson
2016-04-21  5:32   ` Alexey Kardashevskiy
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 10/11] pseries: Consolidate construction of /rtas " David Gibson
2016-04-21  5:32   ` Alexey Kardashevskiy
2016-04-20  2:33 ` [Qemu-devel] [RFC for-2.7 11/11] pseries: Remove unused callbacks from sPAPR VIO bus state David Gibson
2016-04-21  5:31   ` Alexey Kardashevskiy
2016-04-27  6:22     ` David Gibson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=57186CAC.2080100@ozlabs.ru \
    --to=aik@ozlabs.ru \
    --cc=agraf@suse.de \
    --cc=crosthwaite.peter@gmail.com \
    --cc=david@gibson.dropbear.id.au \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-ppc@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.