--- zzzz-none-000/linux-3.10.107/arch/powerpc/platforms/pseries/dlpar.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/arch/powerpc/platforms/pseries/dlpar.c 2021-02-04 17:41:59.000000000 +0000 @@ -10,14 +10,18 @@ * 2 as published by the Free Software Foundation. */ +#define pr_fmt(fmt) "dlpar: " fmt + #include -#include #include #include #include #include #include + +#include "of_helpers.h" #include "offline_states.h" +#include "pseries.h" #include #include @@ -25,11 +29,11 @@ #include struct cc_workarea { - u32 drc_index; - u32 zero; - u32 name_offset; - u32 prop_length; - u32 prop_offset; + __be32 drc_index; + __be32 zero; + __be32 name_offset; + __be32 prop_length; + __be32 prop_offset; }; void dlpar_free_cc_property(struct property *prop) @@ -49,11 +53,11 @@ if (!prop) return NULL; - name = (char *)ccwa + ccwa->name_offset; + name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); prop->name = kstrdup(name, GFP_KERNEL); - prop->length = ccwa->prop_length; - value = (char *)ccwa + ccwa->prop_offset; + prop->length = be32_to_cpu(ccwa->prop_length); + value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset); prop->value = kmemdup(value, prop->length, GFP_KERNEL); if (!prop->value) { dlpar_free_cc_property(prop); @@ -63,26 +67,32 @@ return prop; } -static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) +static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa, + const char *path) { struct device_node *dn; char *name; + /* If parent node path is "/" advance path to NULL terminator to + * prevent double leading slashs in full_name. + */ + if (!path[1]) + path++; + dn = kzalloc(sizeof(*dn), GFP_KERNEL); if (!dn) return NULL; - /* The configure connector reported name does not contain a - * preceding '/', so we allocate a buffer large enough to - * prepend this to the full_name. - */ - name = (char *)ccwa + ccwa->name_offset; - dn->full_name = kasprintf(GFP_KERNEL, "/%s", name); + name = (char *)ccwa + be32_to_cpu(ccwa->name_offset); + dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name); if (!dn->full_name) { kfree(dn); return NULL; } + of_node_set_flag(dn, OF_DYNAMIC); + of_node_init(dn); + return dn; } @@ -120,7 +130,8 @@ #define CALL_AGAIN -2 #define ERR_CFG_USE -9003 -struct device_node *dlpar_configure_connector(u32 drc_index) +struct device_node *dlpar_configure_connector(__be32 drc_index, + struct device_node *parent) { struct device_node *dn; struct device_node *first_dn = NULL; @@ -129,6 +140,7 @@ struct property *last_property = NULL; struct cc_workarea *ccwa; char *data_buf; + const char *parent_path = parent->full_name; int cc_token; int rc = -1; @@ -162,7 +174,7 @@ break; case NEXT_SIBLING: - dn = dlpar_parse_cc_node(ccwa); + dn = dlpar_parse_cc_node(ccwa, parent_path); if (!dn) goto cc_error; @@ -172,13 +184,17 @@ break; case NEXT_CHILD: - dn = dlpar_parse_cc_node(ccwa); + if (first_dn) + parent_path = last_dn->full_name; + + dn = dlpar_parse_cc_node(ccwa, parent_path); if (!dn) goto cc_error; - if (!first_dn) + if (!first_dn) { + dn->parent = parent; first_dn = dn; - else { + } else { dn->parent = last_dn; if (last_dn) last_dn->child = dn; @@ -202,6 +218,7 @@ case PREV_PARENT: last_dn = last_dn->parent; + parent_path = last_dn->parent->full_name; break; case CALL_AGAIN: @@ -229,38 +246,13 @@ return first_dn; } -static struct device_node *derive_parent(const char *path) -{ - struct device_node *parent; - char *last_slash; - - last_slash = strrchr(path, '/'); - if (last_slash == path) { - parent = of_find_node_by_path("/"); - } else { - char *parent_path; - int parent_path_len = last_slash - path + 1; - parent_path = kmalloc(parent_path_len, GFP_KERNEL); - if (!parent_path) - return NULL; - - strlcpy(parent_path, path, parent_path_len); - parent = of_find_node_by_path(parent_path); - kfree(parent_path); - } - - return parent; -} - int dlpar_attach_node(struct device_node *dn) { int rc; - of_node_set_flag(dn, OF_DYNAMIC); - kref_init(&dn->kref); - dn->parent = derive_parent(dn->full_name); - if (!dn->parent) - return -ENOMEM; + dn->parent = pseries_of_derive_parent(dn->full_name); + if (IS_ERR(dn->parent)) + return PTR_ERR(dn->parent); rc = of_attach_node(dn); if (rc) { @@ -275,8 +267,15 @@ int dlpar_detach_node(struct device_node *dn) { + struct device_node *child; int rc; + child = of_get_next_child(dn, NULL); + while (child) { + dlpar_detach_node(child); + child = of_get_next_child(dn, child); + } + rc = of_detach_node(dn); if (rc) return rc; @@ -346,7 +345,8 @@ int rc = 0; unsigned int cpu; int len, nthreads, i; - const u32 *intserv; + const __be32 *intserv; + u32 thread; intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); if (!intserv) @@ -356,13 +356,14 @@ cpu_maps_update_begin(); for (i = 0; i < nthreads; i++) { + thread = be32_to_cpu(intserv[i]); for_each_present_cpu(cpu) { - if (get_hard_smp_processor_id(cpu) != intserv[i]) + if (get_hard_smp_processor_id(cpu) != thread) continue; BUG_ON(get_cpu_current_state(cpu) != CPU_STATE_OFFLINE); cpu_maps_update_done(); - rc = cpu_up(cpu); + rc = device_online(get_cpu_device(cpu)); if (rc) goto out; cpu_maps_update_begin(); @@ -371,7 +372,7 @@ } if (cpu == num_possible_cpus()) printk(KERN_WARNING "Could not find cpu to online " - "with physical id 0x%x\n", intserv[i]); + "with physical id 0x%x\n", thread); } cpu_maps_update_done(); @@ -382,57 +383,41 @@ static ssize_t dlpar_cpu_probe(const char *buf, size_t count) { - struct device_node *dn; - unsigned long drc_index; - char *cpu_name; + struct device_node *dn, *parent; + u32 drc_index; int rc; - cpu_hotplug_driver_lock(); - rc = strict_strtoul(buf, 0, &drc_index); - if (rc) { - rc = -EINVAL; - goto out; - } - - dn = dlpar_configure_connector(drc_index); - if (!dn) { - rc = -EINVAL; - goto out; - } + rc = kstrtou32(buf, 0, &drc_index); + if (rc) + return -EINVAL; - /* configure-connector reports cpus as living in the base - * directory of the device tree. CPUs actually live in the - * cpus directory so we need to fixup the full_name. - */ - cpu_name = kasprintf(GFP_KERNEL, "/cpus%s", dn->full_name); - if (!cpu_name) { - dlpar_free_cc_nodes(dn); - rc = -ENOMEM; - goto out; - } + rc = dlpar_acquire_drc(drc_index); + if (rc) + return -EINVAL; - kfree(dn->full_name); - dn->full_name = cpu_name; + parent = of_find_node_by_path("/cpus"); + if (!parent) + return -ENODEV; - rc = dlpar_acquire_drc(drc_index); - if (rc) { - dlpar_free_cc_nodes(dn); - rc = -EINVAL; - goto out; + dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent); + of_node_put(parent); + if (!dn) { + dlpar_release_drc(drc_index); + return -EINVAL; } rc = dlpar_attach_node(dn); if (rc) { dlpar_release_drc(drc_index); dlpar_free_cc_nodes(dn); - goto out; + return rc; } rc = dlpar_online_cpu(dn); -out: - cpu_hotplug_driver_unlock(); + if (rc) + return rc; - return rc ? rc : count; + return count; } static int dlpar_offline_cpu(struct device_node *dn) @@ -440,7 +425,8 @@ int rc = 0; unsigned int cpu; int len, nthreads, i; - const u32 *intserv; + const __be32 *intserv; + u32 thread; intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); if (!intserv) @@ -450,8 +436,9 @@ cpu_maps_update_begin(); for (i = 0; i < nthreads; i++) { + thread = be32_to_cpu(intserv[i]); for_each_present_cpu(cpu) { - if (get_hard_smp_processor_id(cpu) != intserv[i]) + if (get_hard_smp_processor_id(cpu) != thread) continue; if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) @@ -460,7 +447,7 @@ if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); cpu_maps_update_done(); - rc = cpu_down(cpu); + rc = device_offline(get_cpu_device(cpu)); if (rc) goto out; cpu_maps_update_begin(); @@ -473,14 +460,14 @@ * Upgrade it's state to CPU_STATE_OFFLINE. */ set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); - BUG_ON(plpar_hcall_norets(H_PROD, intserv[i]) + BUG_ON(plpar_hcall_norets(H_PROD, thread) != H_SUCCESS); __cpu_die(cpu); break; } if (cpu == num_possible_cpus()) printk(KERN_WARNING "Could not find cpu to offline " - "with physical id 0x%x\n", intserv[i]); + "with physical id 0x%x\n", thread); } cpu_maps_update_done(); @@ -492,52 +479,161 @@ static ssize_t dlpar_cpu_release(const char *buf, size_t count) { struct device_node *dn; - const u32 *drc_index; + u32 drc_index; int rc; dn = of_find_node_by_path(buf); if (!dn) return -EINVAL; - drc_index = of_get_property(dn, "ibm,my-drc-index", NULL); - if (!drc_index) { + rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index); + if (rc) { of_node_put(dn); return -EINVAL; } - cpu_hotplug_driver_lock(); rc = dlpar_offline_cpu(dn); if (rc) { of_node_put(dn); - rc = -EINVAL; - goto out; + return -EINVAL; } - rc = dlpar_release_drc(*drc_index); + rc = dlpar_release_drc(drc_index); if (rc) { of_node_put(dn); - goto out; + return rc; } rc = dlpar_detach_node(dn); if (rc) { - dlpar_acquire_drc(*drc_index); - goto out; + dlpar_acquire_drc(drc_index); + return rc; } of_node_put(dn); -out: - cpu_hotplug_driver_unlock(); + + return count; +} + +#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ + +static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) +{ + int rc; + + /* pseries error logs are in BE format, convert to cpu type */ + switch (hp_elog->id_type) { + case PSERIES_HP_ELOG_ID_DRC_COUNT: + hp_elog->_drc_u.drc_count = + be32_to_cpu(hp_elog->_drc_u.drc_count); + break; + case PSERIES_HP_ELOG_ID_DRC_INDEX: + hp_elog->_drc_u.drc_index = + be32_to_cpu(hp_elog->_drc_u.drc_index); + } + + switch (hp_elog->resource) { + case PSERIES_HP_ELOG_RESOURCE_MEM: + rc = dlpar_memory(hp_elog); + break; + default: + pr_warn_ratelimited("Invalid resource (%d) specified\n", + hp_elog->resource); + rc = -EINVAL; + } + + return rc; +} + +static ssize_t dlpar_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct pseries_hp_errorlog *hp_elog; + const char *arg; + int rc; + + hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL); + if (!hp_elog) { + rc = -ENOMEM; + goto dlpar_store_out; + } + + /* Parse out the request from the user, this will be in the form + * + */ + arg = buf; + if (!strncmp(arg, "memory", 6)) { + hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; + arg += strlen("memory "); + } else { + pr_err("Invalid resource specified: \"%s\"\n", buf); + rc = -EINVAL; + goto dlpar_store_out; + } + + if (!strncmp(arg, "add", 3)) { + hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD; + arg += strlen("add "); + } else if (!strncmp(arg, "remove", 6)) { + hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE; + arg += strlen("remove "); + } else { + pr_err("Invalid action specified: \"%s\"\n", buf); + rc = -EINVAL; + goto dlpar_store_out; + } + + if (!strncmp(arg, "index", 5)) { + u32 index; + + hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; + arg += strlen("index "); + if (kstrtou32(arg, 0, &index)) { + rc = -EINVAL; + pr_err("Invalid drc_index specified: \"%s\"\n", buf); + goto dlpar_store_out; + } + + hp_elog->_drc_u.drc_index = cpu_to_be32(index); + } else if (!strncmp(arg, "count", 5)) { + u32 count; + + hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT; + arg += strlen("count "); + if (kstrtou32(arg, 0, &count)) { + rc = -EINVAL; + pr_err("Invalid count specified: \"%s\"\n", buf); + goto dlpar_store_out; + } + + hp_elog->_drc_u.drc_count = cpu_to_be32(count); + } else { + pr_err("Invalid id_type specified: \"%s\"\n", buf); + rc = -EINVAL; + goto dlpar_store_out; + } + + rc = handle_dlpar_errorlog(hp_elog); + +dlpar_store_out: + kfree(hp_elog); return rc ? rc : count; } +static CLASS_ATTR(dlpar, S_IWUSR, NULL, dlpar_store); + static int __init pseries_dlpar_init(void) { + int rc; + +#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE ppc_md.cpu_probe = dlpar_cpu_probe; ppc_md.cpu_release = dlpar_cpu_release; +#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ - return 0; + rc = sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); + + return rc; } machine_device_initcall(pseries, pseries_dlpar_init); -#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */