]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
SPARC64: VIO: Support for virtual-device MD node probing
authorAaron Young <Aaron.Young@oracle.com>
Fri, 17 Feb 2017 23:18:53 +0000 (18:18 -0500)
committerChuck Anderson <chuck.anderson@oracle.com>
Thu, 6 Apr 2017 07:13:43 +0000 (00:13 -0700)
This update adds support to the mdesc/vio infrastructure to
probe for "virtual-device" nodes in the MD. The vio
module will create sysfs device files for these nodes which
can be accessed by user space code (such as udev). In addition,
VIO drivers can now probe for these MD nodes if the need arises.

This functionality will serve as part of the fix for
BUG 24841906.

Signed-off-by: Aaron Young <Aaron.Young@oracle.com>
Reviewed-By: Alexandre Chartre <Alexandre.Chartre@oracle.com>
Orabug: 24841906

arch/sparc/include/asm/mdesc.h
arch/sparc/include/asm/vio.h
arch/sparc/kernel/mdesc.c
arch/sparc/kernel/vio.c

index 9018564700c0782273ba4b1df3db91965adce746..7e4837abe9f258bcd49dd05a4cbc9f4464e1f922 100644 (file)
@@ -84,6 +84,10 @@ union md_node_info {
        struct ds_port {
                u64 id;                         /* id */
        } ds_port;
+       struct vdev {
+               char name[MDESC_MAX_STR_LEN];   /* name (property) */
+               u64 cfg_hdl;                    /* config handle */
+       } vdev;
 };
 u64 mdesc_get_node(struct mdesc_handle *hp, char *node_name,
        union md_node_info *node_info);
index bfd4cc86aef52401d15dc5f4df46d6dec81af16c..330590b1f7d5d951f95184c825557685be0af1c4 100644 (file)
@@ -365,12 +365,14 @@ static inline u32 vio_dring_prev(struct vio_dring_state *dr, u32 index)
 #define VIO_MAX_TYPE_LEN       32
 #define VIO_MAX_NAME_LEN       32
 #define VIO_MAX_COMPAT_LEN     64
+#define VIO_MAX_STR_LEN                MDESC_MAX_STR_LEN
 
 struct vio_dev {
        struct device_node      *dp;
 
        char                    node_name[VIO_MAX_NAME_LEN];
        char                    type[VIO_MAX_TYPE_LEN];
+       char                    devalias[VIO_MAX_STR_LEN];
        char                    compat[VIO_MAX_COMPAT_LEN];
        int                     compat_len;
 
index 4d9f4b25f680dc6218ba984ee330863a14be8009..665b24fa208006265fc804832d5d5e99b129b16f 100644 (file)
@@ -1,6 +1,7 @@
 /* mdesc.c: Sun4V machine description handling.
  *
  * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2017 Oracle. All rights reserved.
  */
 #include <linux/kernel.h>
 #include <linux/types.h>
@@ -95,11 +96,16 @@ static int get_ds_port_node_info(struct mdesc_handle *md, u64 node,
        union md_node_info *node_info);
 static bool ds_port_node_match(union md_node_info *a_node_info,
        union md_node_info *b_node_info);
+static int get_vdev_node_info(struct mdesc_handle *md, u64 node,
+       union md_node_info *node_info);
+static bool vdev_node_match(union md_node_info *a_node_info,
+       union md_node_info *b_node_info);
 
 /* supported node types which can be registered */
 static struct md_node_ops md_node_ops_table[] = {
        {"virtual-device-port", get_vdev_port_node_info, vdev_port_node_match},
        {"domain-services-port", get_ds_port_node_info, ds_port_node_match},
+       {"virtual-device", get_vdev_node_info, vdev_node_match},
        {NULL, NULL, NULL}
 };
 
@@ -333,7 +339,7 @@ static int get_vdev_port_node_info(struct mdesc_handle *md, u64 node,
        const char *name;
 
        /*
-        * Virtual device nodes are distinguished by:
+        * Virtual device port nodes are distinguished by:
         * 1. "id" property
         * 2. "name" property
         * 3. parent node "cfg-handle" property
@@ -395,6 +401,42 @@ static bool ds_port_node_match(union md_node_info *a_node_info,
        return true;
 }
 
+static int get_vdev_node_info(struct mdesc_handle *md, u64 node,
+       union md_node_info *node_info)
+{
+       const u64 *cfg_hdlp;
+       const char *name;
+
+       /*
+        * Virtual device nodes are distinguished by:
+        * 1. "cfg-handle" property
+        * 2. "name" property
+        */
+       cfg_hdlp = mdesc_get_property(md, node, "cfg-handle", NULL);
+       name = mdesc_get_property(md, node, "name", NULL);
+
+       if (!cfg_hdlp || !name)
+               return -1;
+
+       strncpy(node_info->vdev.name, name, MDESC_MAX_STR_LEN);
+       node_info->vdev.cfg_hdl = *cfg_hdlp;
+
+       return 0;
+}
+
+static bool vdev_node_match(union md_node_info *a_node_info,
+       union md_node_info *b_node_info)
+{
+       if (a_node_info->vdev.cfg_hdl != b_node_info->vdev.cfg_hdl)
+               return false;
+
+       if (strncmp(a_node_info->vdev.name,
+           b_node_info->vdev.name, MDESC_MAX_STR_LEN) != 0)
+               return false;
+
+       return true;
+}
+
 /* Run 'func' on nodes which are in A but not in B.  */
 static void invoke_on_missing(const char *name,
                struct mdesc_handle *a,
index 093efaea75f3d3ddfbf55e01c1d5c5ac41e037ed..ded17fb4b0ea788b4aecdb722e4406e0d082d97f 100644 (file)
@@ -7,6 +7,7 @@
  *     Stephen Rothwell
  *
  * Adapted to sparc64 by David S. Miller davem@davemloft.net
+ * Copyright (C) 2017 Oracle. All rights reserved.
  */
 
 #include <linux/kernel.h>
@@ -133,6 +134,8 @@ static ssize_t devspec_show(struct device *dev,
                str = "vnet";
        else if (!strcmp(vdev->type, "vdc-port"))
                str = "vdisk";
+       else if (vdev->devalias)
+               str = vdev->devalias;
 
        return sprintf(buf, "%s\n", str);
 }
@@ -207,7 +210,14 @@ static const u64 *vio_cfg_handle(struct mdesc_handle *hp, u64 node)
        const u64 *cfg_handle;
        u64 a;
 
-       cfg_handle = NULL;
+       /* Find the first cfg-handle property for the node starting with
+        * the current node and then, if not found, try the parent nodes.
+        */
+
+       cfg_handle = mdesc_get_property(hp, node, "cfg-handle", NULL);
+       if (cfg_handle)
+               return cfg_handle;
+
        mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
                u64 target;
 
@@ -292,9 +302,18 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
        struct vio_dev *vdev;
        int err, tlen, clen;
        const u64 *id, *cfg_handle;
-
+       int devalias_len;
+       const char *devalias;
+
+       /* We use the device-type property to name the device if
+        * present. If not present, fallback to use the name property.
+        * NOTE - since there seem to be several different
+        * virtual-device nodes with a generic device-type of "serial",
+        * we use name property instead for these devices as they
+        * are more descriptive and unique.
+        */
        type = mdesc_get_property(hp, mp, "device-type", &tlen);
-       if (!type) {
+       if (!type || !strcmp(type, "serial")) {
                type = mdesc_get_property(hp, mp, "name", &tlen);
                if (!type) {
                        type = mdesc_node_name(hp, mp);
@@ -312,14 +331,19 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
        cfg_handle = vio_cfg_handle(hp, mp);
 
        compat = mdesc_get_property(hp, mp, "device-type", &clen);
-       if (!compat) {
-               clen = 0;
-       } else if (clen > VIO_MAX_COMPAT_LEN) {
+       if (compat && clen > VIO_MAX_COMPAT_LEN) {
                printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n",
                       clen, type);
                return NULL;
        }
 
+       devalias = mdesc_get_property(hp, mp, "devalias", &devalias_len);
+       if (devalias && devalias_len > VIO_MAX_STR_LEN) {
+               printk(KERN_ERR "VIO: devalias len %d for [%s] is too long.\n",
+                      devalias_len, type);
+               return NULL;
+       }
+
        vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
        if (!vdev) {
                printk(KERN_ERR "VIO: Could not allocate vio_dev\n");
@@ -327,11 +351,13 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
        }
 
        memcpy(vdev->type, type, tlen);
-       if (compat)
+       if (compat) {
                memcpy(vdev->compat, compat, clen);
-       else
-               memset(vdev->compat, 0, sizeof(vdev->compat));
-       vdev->compat_len = clen;
+               vdev->compat_len = clen;
+       }
+
+       if (devalias)
+               memcpy(vdev->devalias, devalias, devalias_len);
 
        vdev->port_id = ~0UL;
        vdev->tx_irq = 0;
@@ -339,12 +365,22 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
 
        vio_fill_channel_info(hp, mp, vdev);
 
-       if (!id) {
+       /* Create the final name for the device.
+        * If there was no parent given or no id
+        * and no cfg-handle was found, just use the type
+        * for the device name. If we found an id and/or
+        * a cfg-handle, append them to the name to help
+        * make the name as unique as possible.
+        */
+       if (parent == NULL || (!id && !cfg_handle)) {
                dev_set_name(&vdev->dev, "%s", type);
                vdev->dev_no = ~(u64)0;
        } else if (!cfg_handle) {
                dev_set_name(&vdev->dev, "%s-%llu", type, *id);
                vdev->dev_no = *id;
+       } else if (!id) {
+               dev_set_name(&vdev->dev, "%s-%llu", type, *cfg_handle);
+               vdev->dev_no = *cfg_handle;
        } else {
                dev_set_name(&vdev->dev, "%s-%llu-%llu", type,
                             *cfg_handle, *id);
@@ -452,12 +488,43 @@ static void vio_remove(struct mdesc_handle *hp, u64 node, const char *node_name)
        }
 }
 
-static struct mdesc_notifier_client vio_device_notifier = {
+static struct mdesc_notifier_client vio_device_port_notifier = {
        .add            = vio_add,
        .remove         = vio_remove,
        .node_name      = "virtual-device-port",
 };
 
+/* We are only interested in virtual device ports under the
+ * "channel-devices" node.
+ */
+static void vio_add_vdev(struct mdesc_handle *hp, u64 node,
+       const char *node_name)
+{
+       int found;
+       u64 a;
+
+       found = 0;
+       mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
+               u64 target = mdesc_arc_target(hp, a);
+               const char *name = mdesc_node_name(hp, target);
+
+               if (!strcmp(name, "channel-devices")) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (found)
+               (void) vio_create_one(hp, node, (char *)node_name,
+                                     &root_vdev->dev);
+}
+
+static struct mdesc_notifier_client vio_device_notifier = {
+       .add            = vio_add_vdev,
+       .remove         = vio_remove,
+       .node_name      = "virtual-device",
+};
+
 /* We are only interested in domain service ports under the
  * "domain-services" node.  On control nodes there is another port
  * under "openboot" that we should not mess with as aparently that is
@@ -557,6 +624,7 @@ static int __init vio_init(void)
        }
 
        mdesc_register_notifier(&vio_device_notifier);
+       mdesc_register_notifier(&vio_device_port_notifier);
        mdesc_register_notifier(&vio_ds_notifier);
 
        mdesc_release(hp);