{"id":814465,"url":"http://patchwork.ozlabs.org/api/patches/814465/?format=json","web_url":"http://patchwork.ozlabs.org/project/sparclinux/patch/62f2be29-f0e7-98f0-6104-714a8fa2ea1e@oracle.com/","project":{"id":10,"url":"http://patchwork.ozlabs.org/api/projects/10/?format=json","name":"Linux SPARC Development ","link_name":"sparclinux","list_id":"sparclinux.vger.kernel.org","list_email":"sparclinux@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<62f2be29-f0e7-98f0-6104-714a8fa2ea1e@oracle.com>","list_archive_url":null,"date":"2017-09-16T04:46:08","name":"Expose mdesc to sysfs","commit_ref":null,"pull_url":null,"state":"changes-requested","archived":false,"hash":"1d653928d08cd979332c3de3a167747db1166c3b","submitter":{"id":70919,"url":"http://patchwork.ozlabs.org/api/people/70919/?format=json","name":"Eric Saint Etienne","email":"eric.saint.etienne@oracle.com"},"delegate":{"id":34,"url":"http://patchwork.ozlabs.org/api/users/34/?format=json","username":"davem","first_name":"David","last_name":"Miller","email":"davem@davemloft.net"},"mbox":"http://patchwork.ozlabs.org/project/sparclinux/patch/62f2be29-f0e7-98f0-6104-714a8fa2ea1e@oracle.com/mbox/","series":[{"id":3415,"url":"http://patchwork.ozlabs.org/api/series/3415/?format=json","web_url":"http://patchwork.ozlabs.org/project/sparclinux/list/?series=3415","date":"2017-09-16T04:46:08","name":"Expose mdesc to sysfs","version":1,"mbox":"http://patchwork.ozlabs.org/series/3415/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/814465/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/814465/checks/","tags":{},"related":[],"headers":{"Return-Path":"<sparclinux-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":"ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=sparclinux-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xvKV16FBKz9t16\n\tfor <patchwork-incoming@ozlabs.org>;\n\tSat, 16 Sep 2017 14:46:17 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751211AbdIPEqR (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tSat, 16 Sep 2017 00:46:17 -0400","from userp1040.oracle.com ([156.151.31.81]:28136 \"EHLO\n\tuserp1040.oracle.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1751066AbdIPEqQ (ORCPT\n\t<rfc822; sparclinux@vger.kernel.org>); Sat, 16 Sep 2017 00:46:16 -0400","from aserv0021.oracle.com (aserv0021.oracle.com [141.146.126.233])\n\tby userp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2)\n\twith ESMTP id v8G4kC6K026711\n\t(version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256\n\tverify=OK); Sat, 16 Sep 2017 04:46:13 GMT","from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236])\n\tby aserv0021.oracle.com (8.14.4/8.14.4) with ESMTP id\n\tv8G4kCF3019771\n\t(version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256\n\tverify=OK); Sat, 16 Sep 2017 04:46:12 GMT","from ubhmp0003.oracle.com (ubhmp0003.oracle.com [156.151.24.56])\n\tby aserv0122.oracle.com (8.14.4/8.14.4) with ESMTP id v8G4kBhL022810; \n\tSat, 16 Sep 2017 04:46:11 GMT","from [10.175.193.35] (/10.175.193.35)\n\tby default (Oracle Beehive Gateway v4.0)\n\twith ESMTP ; Sat, 16 Sep 2017 04:46:11 +0000"],"To":"\"David S . Miller\" <davem@davemloft.net>","Cc":"sparclinux@vger.kernel.org","From":"Eric Saint Etienne <eric.saint.etienne@oracle.com>","Subject":"[PATCH] Expose mdesc to sysfs","Organization":"Oracle","Message-ID":"<62f2be29-f0e7-98f0-6104-714a8fa2ea1e@oracle.com>","Date":"Sat, 16 Sep 2017 05:46:08 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101\n\tThunderbird/52.3.0","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8; format=flowed","Content-Transfer-Encoding":"8bit","Content-Language":"en-US","X-Source-IP":"aserv0021.oracle.com [141.146.126.233]","Sender":"sparclinux-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<sparclinux.vger.kernel.org>","X-Mailing-List":"sparclinux@vger.kernel.org"},"content":"To get a snapshot as complete as possible of what the system is composed of,\nthis commit adds the ability to browse machine description from sysfs.\n\nThis commit eases porting of user-space utilities that exist on other \narchitectures\nlike lshw for instance.\n\nWhenever the HV changes machine description content, sysfs will be \nautopmatically\nre-populated. This is done almost atomically by swapping the old mdesc \nsysfs folder\nwith the new one. While the tree under the new folder is being created, \nreading from\n/sys/firmware/mdesc will return EAGAIN. That way userspace programs will \nnot see\nstale content in /sys/firmware/mdesc\n\nSigned-off-by: Eric Saint Etienne <eric.saint.etienne@oracle.com>\nReviewed-by: Dave Aldridge <david.j.aldridge@oracle.com>\n\n---\n  arch/sparc/Kconfig              |    8 +\n  arch/sparc/kernel/Makefile      |    2 +\n  arch/sparc/kernel/mdesc.c       |    7 +\n  arch/sparc/kernel/mdesc_sysfs.c |  830 \n+++++++++++++++++++++++++++++++++++++++\n  4 files changed, 847 insertions(+), 0 deletions(-)\n  create mode 100644 arch/sparc/kernel/mdesc_sysfs.c\n\n+}\n+\n+static void __exit mdesc_sysfs_cleanup(void)\n+{\n+    if (mdesc_kobj) {\n+        kobject_del(mdesc_kobj);\n+        kobject_put(mdesc_kobj);\n+    }\n+}\n+\n+module_init(mdesc_sysfs_init);\n+module_exit(mdesc_sysfs_cleanup);","diff":"diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig\nindex cac67aa..5ecdebf 100644\n--- a/arch/sparc/Kconfig\n+++ b/arch/sparc/Kconfig\n@@ -508,6 +508,14 @@ config UBOOT_ENTRY_ADDR\n  endmenu\n  endif\n\n+config MDESC_SYSFS\n+    bool \"Maps machine description to sysfs\"\n+    depends on SPARC64\n+    default y\n+    ---help---\n+      If you say Y here, the directed acyclic graph described in the \nmachine\n+      description will be expanded and made available in \n/sys/firmware/mdesc\n+\n  endmenu\n\n  menu \"Bus options (PCI etc.)\"\ndiff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile\nindex 5e6463d..213179e 100644\n--- a/arch/sparc/kernel/Makefile\n+++ b/arch/sparc/kernel/Makefile\n@@ -122,6 +122,8 @@ obj-$(CONFIG_SPARC64)    += $(pc--y)\n\n  obj-$(CONFIG_UPROBES)    += uprobes.o\n\n+obj-$(CONFIG_MDESC_SYSFS) += mdesc_sysfs.o\n+\n  obj-$(CONFIG_SPARC64)    += jump_label.o\n\n  obj-$(CONFIG_SPARC64)    += kexec_shim.o\ndiff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c\nindex 1fbf6ff..2e999fe 100644\n--- a/arch/sparc/kernel/mdesc.c\n+++ b/arch/sparc/kernel/mdesc.c\n@@ -92,6 +92,10 @@ struct md_node_ops {\n      mdesc_node_match_f node_match;\n  };\n\n+#ifdef MDESC_SYSFS\n+int mdesc_sysfs_populate(void);\n+#endif\n+\n  static int get_vdev_port_node_info(struct mdesc_handle *md, u64 node,\n      union md_node_info *node_info);\n  static bool vdev_port_node_match(union md_node_info *a_node_info,\n@@ -506,6 +510,9 @@ void mdesc_update(void)\n          cur_mdesc = hp;\n          spin_unlock_irqrestore(&mdesc_lock, flags);\n\n+#ifdef MDESC_SYSFS\n+        mdesc_sysfs_populate();\n+#endif\n          mdesc_notify_clients(orig_hp, hp);\n\n          spin_lock_irqsave(&mdesc_lock, flags);\ndiff --git a/arch/sparc/kernel/mdesc_sysfs.c \nb/arch/sparc/kernel/mdesc_sysfs.c\nnew file mode 100644\nindex 0000000..6fb8713\n--- /dev/null\n+++ b/arch/sparc/kernel/mdesc_sysfs.c\n@@ -0,0 +1,830 @@\n+/*\n+ * mdesc_sysfs.c - Maps machine description DAG to sysfs\n+ *\n+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License version\n+ * 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful, but\n+ * WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ * General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; see the file COPYING.  If not, write to\n+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,\n+ * USA.\n+ *\n+ */\n+\n+#include <linux/init.h>\n+#include <linux/module.h>\n+#include <linux/slab.h>\n+#include <asm/mdesc.h>\n+\n+#define MDESC_SYSFS_TOO_LARGE \"Error: Not enough space to output the \ndata, retrieve it from /dev/mdesc instead.\"\n+\n+/*\n+ * Machine Description is described in slides 37-39 here:\n+ * \nhttp://www.oracle.com/technetwork/systems/opensparc/2008-oct-opensparc-slide-cast-09-mw-1539018.html\n+ *\n+ * It is composed of three main sections decribed by the following \nmdesc header.\n+ * Since these blocks are contiguous, describing their size is \nsufficient to\n+ * precisely locate them within the mdesc blob.\n+ *\n+ * - The NODE block contains the nodes of the directed acyclic graph (DAG)\n+ * - The NAME block contains names of properties referred to by the DAG \nnodes\n+ * - The DATA block contains values for the properties: strings and \nbinary data\n+ *\n+ * A public specification of mdesc is available online at:\n+ * https://kenai.com/downloads/hypervisor/hypervisor-api-3.0draft7.pdf\n+ */\n+struct mdesc_hdr {\n+    u32    version; /* Transport version */\n+    u32    node_sz; /* node block size */\n+    u32    name_sz; /* name block size */\n+    u32    data_sz; /* data block size */\n+} __aligned(16)__;\n+\n+struct mdesc_elem {\n+    u8    tag;\n+#define MD_LIST_END    0x00\n+#define MD_NODE        0x4e\n+#define MD_NODE_END    0x45\n+#define MD_NOOP        0x20\n+#define MD_PROP_ARC    0x61\n+#define MD_PROP_VAL    0x76\n+#define MD_PROP_STR    0x73\n+#define MD_PROP_DATA    0x64\n+    u8    name_len;\n+    u16    resv;\n+    u32    name_offset;\n+    union {\n+        struct {\n+            u32    data_len;\n+            u32    data_offset;\n+        } data;\n+        u64    val;\n+    } d;\n+};\n+\n+struct mdesc_mem_ops {\n+    struct mdesc_handle *(*alloc)(unsigned int mdesc_size);\n+    void (*free)(struct mdesc_handle *handle);\n+};\n+\n+struct mdesc_handle {\n+    struct list_head     list;\n+    struct mdesc_mem_ops *mops;\n+    void                 *self_base;\n+    atomic_t             refcnt;\n+    unsigned int         handle_size;\n+    struct mdesc_hdr     mdesc;\n+};\n+\n+/*\n+ * The following 3 functions return a pointer of the right type to the\n+ * 3 main mdesc blocks\n+ */\n+\n+static inline struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)\n+{\n+    return (struct mdesc_elem *) (mdesc + 1);\n+}\n+\n+static inline void *name_block(struct mdesc_hdr *mdesc)\n+{\n+    return ((void *) node_block(mdesc)) + mdesc->node_sz;\n+}\n+\n+static inline void *data_block(struct mdesc_hdr *mdesc)\n+{\n+    return ((void *) name_block(mdesc)) + mdesc->name_sz;\n+}\n+\n+/*\n+ * Dynamically allocates the right amount of characters to format according\n+ * to the printf-like format specified in \"fmt\".\n+ * \"*buf\" is allocated and filled.\n+ *\n+ * Return 0 upon success\n+ */\n+static int alloc_sprintf(char **buf, const char *fmt, ...)\n+{\n+    va_list args;\n+    size_t len, actual;\n+\n+    va_start(args, fmt);\n+    len = vsnprintf(NULL, 0, fmt, args);\n+    va_end(args);\n+\n+    *buf = kmalloc(len+1, GFP_KERNEL);\n+    if (!*buf)\n+        return -ENOMEM;\n+\n+    va_start(args, fmt);\n+    actual = vscnprintf(*buf, len + 1, fmt, args);\n+    va_end(args);\n+\n+    if (actual != len) {\n+        kfree(*buf);\n+        *buf = NULL;\n+        return -ENOMEM;\n+    }\n+\n+    return 0;\n+}\n+\n+/*\n+ * Flags used with the following formatting functions that modify the way\n+ * node names are formatted\n+ */\n+#define FORMAT_APPEND    0x01\n+#define FORMAT_USE_INDEX 0x02\n+\n+/*\n+ * Take an arc node in \"ep\" and format it using the \"id\" field as to avoid\n+ * collision when adding to sysfs.\n+ * Ex: multiple \"cpu\" nodes in the same folder will be named: \"0\", \"1\"...\n+ *\n+ * When FORMAT_APPEND is used, the names will become: \"cpu0\", \"cpu1\"...\n+ * The formatted string is found in *formatted, to be freed by the caller\n+ *\n+ * Return 0 upon success\n+ */\n+static int format_using_id(struct mdesc_handle *hp,\n+               struct mdesc_elem *ep,\n+               char const *name,\n+               char **formatted,\n+               unsigned int flags,\n+               unsigned int index,\n+               unsigned int total)\n+{\n+    const u64 *prop_id = mdesc_get_property(hp, ep->d.val, \"id\", NULL);\n+\n+    if (!prop_id) {\n+        pr_err(\"mdesc %s doesn't have a property \\\"id\\\"\\n\", name);\n+        return -ENOENT;\n+    }\n+\n+    if (flags & FORMAT_APPEND)\n+        return alloc_sprintf(formatted, \"%s%lld\", name, *prop_id);\n+\n+    return alloc_sprintf(formatted, \"%lld\", *prop_id);\n+}\n+\n+/*\n+ * Take an arc node in \"ep\" and format it using the \"name\" field as to \navoid\n+ * collision when adding to sysfs.\n+ * Ex: multiple \"virtual-device\" nodes in the same folder will be named:\n+ * \"disk\", \"network\"...\n+ *\n+ * When FORMAT_APPEND is used: \"virtual-device-disk\", \nvirtual-device-network\"...\n+ *\n+ * Because the \"name\" field is not guaranteed to be unique, one can use the\n+ * flag FORMAT_USE_INDEX: \"virtual-device0-disk\", \nvirtual-device1-network\"...\n+ * In this case \"index\" is used.\n+ *\n+ * The formatted string is found in *formatted, to be freed by the caller\n+ *\n+ * Return 0 upon success\n+ */\n+static int format_using_name(struct mdesc_handle *hp,\n+                 struct mdesc_elem *ep,\n+                 const char *name,\n+                 char **formatted,\n+                 unsigned int flags,\n+                 unsigned int index,\n+                 unsigned int total)\n+{\n+    const char *prop_name = mdesc_get_property(hp, ep->d.val, \"name\", \nNULL);\n+\n+    if (!prop_name) {\n+        pr_err(\"mdesc %s doesn't have a property \\\"name\\\"\\n\", name);\n+        return -ENOENT;\n+    }\n+\n+    if (flags & FORMAT_APPEND) {\n+        if (flags & FORMAT_USE_INDEX)\n+            return alloc_sprintf(formatted, \"%s%d_%s\",\n+                         name, index, prop_name);\n+        else\n+            return alloc_sprintf(formatted, \"%s_%s\",\n+                         name, prop_name);\n+    }\n+\n+    if (flags & FORMAT_USE_INDEX)\n+        return alloc_sprintf(formatted, \"%s%d\", prop_name, index);\n+    else\n+        return alloc_sprintf(formatted, \"%s\", prop_name);\n+}\n+\n+/*\n+ * Default formatting used in format_name() for arcs nodes.\n+ * Called when we find multiple arcs with the same name for which no\n+ * formatting is specified in format_nodes array.\n+ * This default formatting guarantees uniqueness because it is using \nthe node\n+ * index in the mdesc nodes array\n+ */\n+static char *format_name_fallback(struct mdesc_handle *hp,\n+                  struct mdesc_elem *ep,\n+                  const char *name,\n+                  unsigned int    index,\n+                  unsigned int    total)\n+{\n+    char *formatted = NULL;\n+    int err;\n+\n+    err = alloc_sprintf(&formatted, \"%s_0x%x\", name, ep->d.val);\n+    return err ? NULL : formatted;\n+}\n+\n+struct format_node {\n+    char    *name;\n+    int    (*format)(struct mdesc_handle*, struct mdesc_elem*, const char*,\n+            char**, unsigned int, unsigned int, unsigned int);\n+    unsigned int flags;\n+};\n+\n+struct format_node format_nodes[] = {\n+#define FORMAT_APPEND_USE_INDEX (FORMAT_APPEND | FORMAT_USE_INDEX)\n+    { \"virtual-device\",       format_using_name, FORMAT_APPEND_USE_INDEX },\n+    { \"domain-services-port\", format_using_id,   FORMAT_APPEND },\n+    { \"virtual-device-port\",  format_using_id,   FORMAT_APPEND },\n+    { \"channel-endpoint\",     format_using_id,   FORMAT_APPEND },\n+    { \"cpu\",                  format_using_id,   FORMAT_APPEND },\n+    { NULL, NULL, 0 }\n+};\n+\n+/*\n+ * Format the name of an arc node according to the table \"format_nodes\".\n+ * If no entry is found for the arc node in \"ep\", it falls back to using\n+ * format_name_fallback()\n+ * Returns the formatted string, to be freed by the caller\n+ */\n+static char *format_name(struct mdesc_handle *hp,\n+             struct mdesc_elem *ep,\n+             const char *name,\n+             int    index,\n+             int    total)\n+{\n+    struct format_node *fn;\n+    char   *formatted = NULL;\n+    bool   found = false;\n+    int    err = 0;\n+\n+    for (fn = format_nodes; fn->name; fn++) {\n+        if (strcmp(fn->name, name))\n+            continue;\n+        err = fn->format(hp, ep, name, &formatted,\n+                 fn->flags, index, total);\n+        found = true;\n+        break;\n+    }\n+\n+    if (!found && (total != 1)) {\n+        /*\n+         * there's no handler for this node and we've got more\n+         * than one so we format it by appending the index\n+         * (by order of appearance)\n+         */\n+        err = alloc_sprintf(&formatted, \"%s%lld\", name, index);\n+    }\n+\n+    if (err)\n+        return format_name_fallback(hp, ep, name, index, total);\n+\n+    return formatted;\n+}\n+\n+/*\n+ * Takes an arc node in \"ep\" and create a folder in sysfs with the name as\n+ * provided by format_name() which uses the format_nodes array as input\n+ * \"total\" is the number of arcs nodes with the same type (ex: cpu)\n+ * \"index\" is the occurrence number of the arc node (ex: cpu number 32)\n+ * Fills *kobj with the kobject\n+ * Return 0 upon success\n+ */\n+static int create_sysfs_folder(struct mdesc_handle *hp,\n+                   struct mdesc_elem *ep,\n+                   const char *name,\n+                   struct kobject **kobj,\n+                   struct kobject *parent_kobj,\n+                   unsigned int index,\n+                   unsigned int total)\n+{\n+    char *formatted;\n+    int  err = 0;\n+\n+    formatted = format_name(hp, ep, name, index, total);\n+    if (!formatted)\n+        formatted = (char *) name;\n+\n+    *kobj = kobject_create_and_add(formatted, parent_kobj);\n+\n+    err = (*kobj == NULL) ? -ENOMEM : 0;\n+    if (err)\n+        pr_err(\"mdesc_sysfs: unable to create sysfs entry for \\\"%s\\\"\\n\",\n+               name);\n+\n+    if (formatted != name)\n+        kfree(formatted);\n+\n+    return err;\n+}\n+\n+/*\n+ * Similar to create_sysfs_folder() except that because the node in \"ep\"\n+ * already exist elsewhere in sysfs, a link is created instead.\n+ * Return 0 upon success\n+ */\n+static int create_sysfs_link(struct mdesc_handle *hp,\n+                 struct mdesc_elem *ep,\n+                 const char *name,\n+                 struct kobject *target_kobj,\n+                 struct kobject *parent_kobj,\n+                 unsigned int index,\n+                 unsigned int total)\n+{\n+    char *formatted;\n+    int  err = 0;\n+\n+    formatted = format_name(hp, ep, name, index, total);\n+    if (!formatted)\n+        formatted = (char *) name;\n+\n+    err = sysfs_create_link(parent_kobj, target_kobj, formatted);\n+    if (err)\n+        pr_err(\"mdesc_sysfs: unable to create sysfs link for \\\"%s\\\"\\n\",\n+               name);\n+\n+    if (formatted != name)\n+        kfree(formatted);\n+\n+    return err;\n+}\n+\n+struct mdesc_arcs {\n+    struct list_head list;\n+    const char *name;\n+    unsigned int count;\n+};\n+\n+/*\n+ * Take a node in \"ep\" and build a linked list of mdesc_arcs elements.\n+ * Each element contains a name and how many occurrences exists in \"ep\".\n+ * Return 0 upon success\n+ */\n+static int sysfs_populate_build_arcs_list(struct mdesc_handle *hp,\n+                      struct mdesc_elem *ep,\n+                      struct mdesc_arcs *arcs_list)\n+{\n+    struct mdesc_elem *base = node_block(&hp->mdesc);\n+    const char *names = name_block(&hp->mdesc);\n+\n+    for (ep++; ep->tag != MD_NODE_END; ep++) {\n+        const char *arc_name;\n+        struct mdesc_arcs *p;\n+\n+        /* We only care of the forward arcs of the DAG */\n+        if (ep->tag != MD_PROP_ARC ||\n+                strcmp(names + ep->name_offset, \"fwd\"))\n+            continue;\n+\n+        arc_name = names + (base + ep->d.val)->name_offset;\n+        list_for_each_entry(p, &arcs_list->list, list) {\n+            if (!strcmp(p->name, arc_name))\n+                break;\n+        }\n+        /* Have we found arc_name in the list? */\n+        if (p == arcs_list) {\n+            /* no, we add it to the list */\n+            p = kzalloc(sizeof(struct mdesc_arcs), GFP_KERNEL);\n+            if (p == NULL) {\n+                struct list_head *pos, *q;\n+\n+                /* An error occured: free the whole list */\n+                list_for_each_safe(pos, q, &arcs_list->list) {\n+                    p = list_entry(pos, struct mdesc_arcs,\n+                            list);\n+                    list_del(pos);\n+                    kfree(p);\n+                }\n+                return -ENOMEM;\n+            }\n+            p->name = arc_name;\n+            /* add tail allows to keep mdesc's order */\n+            list_add_tail(&p->list, &arcs_list->list);\n+        }\n+        /* increment the occurrence for this arc */\n+        p->count++;\n+    }\n+    return 0;\n+}\n+\n+struct sysfs_property {\n+    struct kobj_attribute kattr;\n+    char *value;\n+    unsigned int length;\n+};\n+\n+static bool updating;\n+\n+static ssize_t mdesc_sysfs_show(struct kobject *object,\n+                struct kobj_attribute *attr,\n+                char *buf)\n+{\n+    struct sysfs_property *prop;\n+\n+    if (updating)\n+        return -EAGAIN; /* This version is stale */\n+\n+    prop = container_of(attr, struct sysfs_property, kattr);\n+    if (prop->length <= PAGE_SIZE) {\n+        memcpy(buf, prop->value, prop->length);\n+        return prop->length;\n+    }\n+    return sprintf(buf, \"%s\\n\", MDESC_SYSFS_TOO_LARGE);\n+}\n+\n+/*\n+ * Append a property to the list that will be later\n+ * passed to sysfs_create_files()\n+ * \"attributes\" is a pointer to an array of \"struct attribute*\"\n+ */\n+static int add_property(struct sysfs_property *prop,\n+            struct attribute ***attributes,\n+            int *length,\n+            int *size)\n+{\n+    if (*size <= (*length + 1)) {\n+        struct attribute **p;\n+\n+        *size += 16; /* pre-allocation to relieve the allocator */\n+        p = krealloc(*attributes, *size * sizeof(struct attribute *),\n+                GFP_KERNEL);\n+        if (!p)\n+            return -ENOMEM;\n+\n+        *attributes = p;\n+    }\n+    (*attributes)[(*length)++] = &prop->kattr.attr;\n+    /* the array must be NULL terminated */\n+    (*attributes)[*length] = NULL;\n+    return 0;\n+}\n+\n+/*\n+ * Given an arc node in \"ep\", loop through its entries and create\n+ * a list of attributes suitable for sysfs_create_files()\n+ * the entries should be be unique within \"ep\" but there are versions\n+ * where entries are duplicated, so we only add them once to the list\n+ */\n+static int mdesc_sysfs_add_propeties(struct mdesc_handle *hp,\n+                     struct mdesc_elem *ep,\n+                     struct kobject *parent_kobj)\n+{\n+    const char *names = name_block(&hp->mdesc);\n+    void *data = data_block(&hp->mdesc);\n+    struct attribute **sysfs_attributes = NULL;\n+    int sysfs_attributes_length = 0;\n+    int sysfs_attributes_size = 0;\n+    int err;\n+\n+    for (ep++; ep->tag != MD_NODE_END; ep++) {\n+        struct sysfs_property *prop;\n+        char *s = NULL;\n+        size_t len = 0;\n+        bool valid = false;\n+\n+        switch (ep->tag) {\n+        case MD_PROP_VAL:\n+            err = alloc_sprintf(&s, \"%lld\\n\", ep->d.val);\n+            if (err)\n+                return -ENOMEM;\n+            len = strlen(s);\n+            valid = true;\n+            break;\n+        case MD_PROP_STR:\n+            s = kstrndup(data + ep->d.data.data_offset,\n+                     ep->d.data.data_len, GFP_KERNEL);\n+            s = kmalloc(ep->d.data.data_len+1, GFP_KERNEL);\n+            if (s == NULL)\n+                return -ENOMEM;\n+            memcpy(s, data + ep->d.data.data_offset,\n+                   ep->d.data.data_len-1);\n+            s[ep->d.data.data_len-1] = '\\n';\n+            s[ep->d.data.data_len] = '\\0';\n+            len = ep->d.data.data_len;\n+            valid = true;\n+            break;\n+        case MD_PROP_DATA:\n+            s = kmalloc(ep->d.data.data_len, GFP_KERNEL);\n+            if (s == NULL)\n+                return -ENOMEM;\n+            memcpy(s, data + ep->d.data.data_offset,\n+                    ep->d.data.data_len);\n+            len = ep->d.data.data_len;\n+            valid = true;\n+            break;\n+        default:\n+            break;\n+        }\n+\n+        prop = kmalloc(sizeof(*prop), GFP_KERNEL);\n+        if (!prop) {\n+            kfree(s);\n+            kfree(sysfs_attributes);\n+            return -ENOMEM;\n+        }\n+\n+        if (valid) {\n+            const char *c_name = names + ep->name_offset;\n+            char *name;\n+            int i;\n+            bool found = false;\n+\n+            /*\n+             * Check if a property with the same name\n+             * has already been added\n+             */\n+            for (i = 0; i < sysfs_attributes_length; i++) {\n+                struct sysfs_property *prop;\n+\n+                prop = container_of(sysfs_attributes[i],\n+                            struct sysfs_property,\n+                            kattr.attr);\n+                if (!strcmp(prop->kattr.attr.name, c_name)) {\n+                    found = true;\n+                    break;\n+                }\n+            }\n+            if (found) {\n+                pr_info(\"mdesc_sysfs: duplicate property found\\n\");\n+                continue;\n+            }\n+\n+            name = kstrdup(c_name, GFP_KERNEL);\n+            if (!name) {\n+                kfree(s);\n+                kfree(sysfs_attributes);\n+                return -ENOMEM;\n+            }\n+\n+            /* Set the name */\n+            prop->kattr.attr.name = name;\n+            prop->kattr.attr.mode = S_IRUSR;\n+            prop->kattr.show = mdesc_sysfs_show;\n+            prop->kattr.store = NULL;\n+            prop->value = s;\n+            prop->length = len;\n+\n+            /* Add to the (automatically growing) array */\n+            add_property(prop, &sysfs_attributes,\n+                     &sysfs_attributes_length,\n+                     &sysfs_attributes_size);\n+        }\n+    }\n+\n+    if (sysfs_attributes) {\n+        int err;\n+        const struct attribute **pattr = (const struct attribute **)\n+                         sysfs_attributes;\n+\n+        err = sysfs_create_files(parent_kobj, pattr);\n+        if (err)\n+            pr_err(\"mdesc_sysfs: unable to create sysfs properties\\n\");\n+\n+        /*\n+         * Free the array of attributes, but not the individual elements\n+         * because each element points to a \"struct sysfs_property\" that\n+         * mdesc_sysfs_show() uses whenever users read from sysfs\n+         */\n+        kfree(sysfs_attributes);\n+    }\n+\n+    return 0;\n+}\n+\n+/*\n+ * Function called on the root that recursively add nodes to sysfs\n+ * It first looks at the current node in \"ep\", gathering statistics\n+ * on every forward arcs it points to,\n+ * then for every arc type it create a sysfs folder or link\n+ * Finally it adds the properties (non arcs) to sysfs\n+ * Return 0 upon success\n+ */\n+static int sysfs_populate(struct mdesc_handle *hp,\n+              struct mdesc_elem *ep,\n+              struct kobject *parent_kobj,\n+              struct kobject **all_nodes)\n+{\n+    struct mdesc_elem *base = node_block(&hp->mdesc);\n+    const char *names = name_block(&hp->mdesc);\n+    struct mdesc_arcs arcs_list, *p;\n+    struct list_head *pos, *q;\n+    int err = 0;\n+\n+    /*\n+     * Check that it's indeed an arc node\n+     * and lose the 1st entry as it's not useful hereafter\n+     */\n+    if (ep++->tag != MD_NODE) {\n+        pr_err(\"mdesc_sysfs: Not a valid arc node\\n\");\n+        return -EINVAL;\n+    }\n+\n+    /*\n+     * In a high level language we'd create a dictionary\n+     * for example: {\"cpu\": 64\", \"memory\": 1, ...}\n+     * In the kernel we use a linked list of structs.\n+     * We end up with: (\"cpu\", 64) --> (\"memory\", 1) -->...\n+     */\n+    INIT_LIST_HEAD(&arcs_list.list);\n+    err = sysfs_populate_build_arcs_list(hp, ep, &arcs_list);\n+    if (err)\n+        return err;\n+\n+    /*\n+     * For each arc type, we browse the current arc \"ep\" and\n+     * add to sysfs. That allows us to keep an index for these\n+     * arcs that don't contain an \"id\" or \"name\" field\n+     */\n+    list_for_each_entry(p, &arcs_list.list, list) {\n+        struct mdesc_elem *elem;\n+        unsigned int index = 0;\n+\n+        for (elem = ep; elem->tag != MD_NODE_END; elem++) {\n+            struct kobject *kobj;\n+            const char *name;\n+\n+            /* Need to be a forward arc... */\n+            if (elem->tag != MD_PROP_ARC ||\n+                strcmp(names + elem->name_offset, \"fwd\"))\n+                continue;\n+\n+            /*\n+             * ...whose name matches the element of the list\n+             * that we're currently considering\n+             */\n+            name = names + (base + elem->d.val)->name_offset;\n+            if (strcmp(p->name, name))\n+                continue;\n+\n+            /* Have we already added this arc to sysfs? */\n+            if (all_nodes[elem->d.val]) {\n+                /* Yes, we link to it */\n+                err = create_sysfs_link(hp, elem, name,\n+                    all_nodes[elem->d.val], parent_kobj,\n+                    index, p->count);\n+            } else {\n+                /* No, we create a folder for it */\n+                err = create_sysfs_folder(hp, elem, name,\n+                    &kobj, parent_kobj, index, p->count);\n+                if (err)\n+                    return -ENOMEM;\n+\n+                all_nodes[elem->d.val] = kobj;\n+\n+                /* And we add to sysfs whatever it contains */\n+                sysfs_populate(hp, base + elem->d.val,\n+                           kobj, all_nodes);\n+            }\n+\n+            /*\n+             * If it's the last entry of its kind, there's\n+             * no point in looping again\n+             */\n+            if (++index == p->count)\n+                break;\n+        }\n+    }\n+\n+    /* Free the linked list */\n+    list_for_each_safe(pos, q, &arcs_list.list) {\n+        p = list_entry(pos, struct mdesc_arcs, list);\n+        list_del(pos);\n+        kfree(p);\n+    }\n+\n+    /* Add the properties (non-arc) to sysfs (string, int, binary data) */\n+    return mdesc_sysfs_add_propeties(hp, ep, parent_kobj);\n+}\n+\n+static struct kobject *mdesc_kobj;\n+\n+int mdesc_sysfs_populate(void)\n+{\n+    struct mdesc_handle *hp;\n+    unsigned int num_node;\n+    struct kobject **all_nodes, *old_mdesc_kobj;\n+    unsigned int major, minor;\n+    mode_t mode;\n+    int retval = 0;\n+\n+    hp = mdesc_grab();\n+    if (!hp) {\n+        pr_err(\"mdesc_sysfs: hv mdesc not found\\n\");\n+        return -ENODEV;\n+    }\n+\n+    major = hp->mdesc.version >> 16;\n+    minor = hp->mdesc.version & 0xffff;\n+\n+    if (major != 1) {\n+        pr_err(\"mdesc_sysfs: unsupported hv mdesc version: %d.%d\\n\",\n+               major, minor);\n+        mdesc_release(hp);\n+        return -EINVAL;\n+    }\n+\n+    pr_info(\"mdesc_sysfs: machine desc version %d.%d\\n\", major, minor);\n+\n+    num_node = hp->mdesc.node_sz / sizeof(struct mdesc_hdr);\n+    all_nodes = kcalloc(num_node, sizeof(struct kobject *), GFP_KERNEL);\n+    if (!all_nodes) {\n+        mdesc_release(hp);\n+        return -ENOMEM;\n+    }\n+\n+    updating = true;\n+\n+    old_mdesc_kobj = mdesc_kobj;\n+    mdesc_kobj = kobject_create_and_add(\".mdesc.tmp\", firmware_kobj);\n+    if (!mdesc_kobj) {\n+        pr_err(\"mdesc_sysfs: unable to create sysfs entry\\n\");\n+        kfree(all_nodes);\n+        mdesc_release(hp);\n+        return -ENOMEM;\n+    }\n+    mode = mdesc_kobj->sd->mode;\n+     /* Remove access to everyone since we're populating\n+      * the tree. Regardless, root has always access! */\n+    mdesc_kobj->sd->mode &= ~0777;\n+\n+    retval = sysfs_populate(hp, node_block(&hp->mdesc),\n+                mdesc_kobj, all_nodes);\n+\n+    mdesc_release(hp);\n+    kfree(all_nodes);\n+\n+    /* Rename existing mdesc to avoid conflicts */\n+    if (retval)\n+        return retval;\n+\n+    if (old_mdesc_kobj) {\n+        retval = kobject_rename(old_mdesc_kobj, \".mdesc.old\");\n+        if (retval) {\n+            pr_err(\"mdesc_sysfs: unable to rename /sys/firmware/mdesc\\n\");\n+            kobject_del(mdesc_kobj);\n+            kobject_put(mdesc_kobj);\n+            updating = false;\n+            return retval;\n+        }\n+    }\n+\n+    /* Restore original mode and set name */\n+    retval = kobject_rename(mdesc_kobj, \"mdesc\");\n+    if (!retval) {\n+        mdesc_kobj->sd->mode = mode;\n+        if (old_mdesc_kobj) {\n+            kobject_del(old_mdesc_kobj);\n+            kobject_put(old_mdesc_kobj);\n+        }\n+    } else {\n+        int dummy;\n+\n+        /* Attempt to restore the old entry (if any) */\n+        if (old_mdesc_kobj)\n+            dummy = kobject_rename(old_mdesc_kobj, \"mdesc\");\n+        kobject_del(mdesc_kobj);\n+        kobject_put(mdesc_kobj);\n+    }\n+\n+    updating = false;\n+\n+    return retval;\n+}\n+EXPORT_SYMBOL(mdesc_sysfs_populate);\n+\n+MODULE_DESCRIPTION(\"Maps machine description graph to sysfs in \n/sys/firmware/mdesc\");\n+MODULE_AUTHOR(\"Oracle\");\n+MODULE_LICENSE(\"GPL\");\n+\n+static int __init mdesc_sysfs_init(void)\n+{\n+    return mdesc_sysfs_populate();\n","prefixes":[]}