Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/814465/?format=api
{ "id": 814465, "url": "http://patchwork.ozlabs.org/api/patches/814465/?format=api", "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=api", "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=api", "name": "Eric Saint Etienne", "email": "eric.saint.etienne@oracle.com" }, "delegate": { "id": 34, "url": "http://patchwork.ozlabs.org/api/users/34/?format=api", "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=api", "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": [] }