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
next prev parent 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).