From 744d2a3d39bafcb53e56f45de8aec0a60c5022bd Mon Sep 17 00:00:00 2001 From: Aaron Young Date: Fri, 17 Feb 2017 18:18:53 -0500 Subject: [PATCH] SPARC64: VIO: Support for virtual-device MD node probing 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 Reviewed-By: Alexandre Chartre Orabug: 24841906 --- arch/sparc/include/asm/mdesc.h | 4 ++ arch/sparc/include/asm/vio.h | 2 + arch/sparc/kernel/mdesc.c | 44 +++++++++++++++- arch/sparc/kernel/vio.c | 92 +++++++++++++++++++++++++++++----- 4 files changed, 129 insertions(+), 13 deletions(-) diff --git a/arch/sparc/include/asm/mdesc.h b/arch/sparc/include/asm/mdesc.h index 9018564700c0..7e4837abe9f2 100644 --- a/arch/sparc/include/asm/mdesc.h +++ b/arch/sparc/include/asm/mdesc.h @@ -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); diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h index bfd4cc86aef5..330590b1f7d5 100644 --- a/arch/sparc/include/asm/vio.h +++ b/arch/sparc/include/asm/vio.h @@ -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; diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c index 4d9f4b25f680..665b24fa2080 100644 --- a/arch/sparc/kernel/mdesc.c +++ b/arch/sparc/kernel/mdesc.c @@ -1,6 +1,7 @@ /* mdesc.c: Sun4V machine description handling. * * Copyright (C) 2007, 2008 David S. Miller + * Copyright (C) 2017 Oracle. All rights reserved. */ #include #include @@ -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, diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c index 093efaea75f3..ded17fb4b0ea 100644 --- a/arch/sparc/kernel/vio.c +++ b/arch/sparc/kernel/vio.c @@ -7,6 +7,7 @@ * Stephen Rothwell * * Adapted to sparc64 by David S. Miller davem@davemloft.net + * Copyright (C) 2017 Oracle. All rights reserved. */ #include @@ -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); -- 2.50.1