diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r-- | arch/powerpc/platforms/pseries/dlpar.c | 197 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-cpu.c | 550 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/lpar.c | 64 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/pseries.h | 9 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/ras.c | 55 |
5 files changed, 626 insertions, 249 deletions
diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index f244dcb4f..2b93ae8d5 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -20,7 +20,6 @@ #include <linux/of.h> #include "of_helpers.h" -#include "offline_states.h" #include "pseries.h" #include <asm/prom.h> @@ -338,185 +337,6 @@ int dlpar_release_drc(u32 drc_index) return 0; } -#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE - -static int dlpar_online_cpu(struct device_node *dn) -{ - int rc = 0; - unsigned int cpu; - int len, nthreads, i; - const __be32 *intserv; - u32 thread; - - intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); - if (!intserv) - return -EINVAL; - - nthreads = len / sizeof(u32); - - 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) != thread) - continue; - BUG_ON(get_cpu_current_state(cpu) - != CPU_STATE_OFFLINE); - cpu_maps_update_done(); - rc = device_online(get_cpu_device(cpu)); - if (rc) - goto out; - cpu_maps_update_begin(); - - break; - } - if (cpu == num_possible_cpus()) - printk(KERN_WARNING "Could not find cpu to online " - "with physical id 0x%x\n", thread); - } - cpu_maps_update_done(); - -out: - return rc; - -} - -static ssize_t dlpar_cpu_probe(const char *buf, size_t count) -{ - struct device_node *dn, *parent; - u32 drc_index; - int rc; - - rc = kstrtou32(buf, 0, &drc_index); - if (rc) - return -EINVAL; - - rc = dlpar_acquire_drc(drc_index); - if (rc) - return -EINVAL; - - parent = of_find_node_by_path("/cpus"); - if (!parent) - return -ENODEV; - - 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); - return rc; - } - - rc = dlpar_online_cpu(dn); - if (rc) - return rc; - - return count; -} - -static int dlpar_offline_cpu(struct device_node *dn) -{ - int rc = 0; - unsigned int cpu; - int len, nthreads, i; - const __be32 *intserv; - u32 thread; - - intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); - if (!intserv) - return -EINVAL; - - nthreads = len / sizeof(u32); - - 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) != thread) - continue; - - if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) - break; - - if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { - set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); - cpu_maps_update_done(); - rc = device_offline(get_cpu_device(cpu)); - if (rc) - goto out; - cpu_maps_update_begin(); - break; - - } - - /* - * The cpu is in CPU_STATE_INACTIVE. - * Upgrade it's state to CPU_STATE_OFFLINE. - */ - set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); - 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", thread); - } - cpu_maps_update_done(); - -out: - return rc; - -} - -static ssize_t dlpar_cpu_release(const char *buf, size_t count) -{ - struct device_node *dn; - u32 drc_index; - int rc; - - dn = of_find_node_by_path(buf); - if (!dn) - return -EINVAL; - - rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index); - if (rc) { - of_node_put(dn); - return -EINVAL; - } - - rc = dlpar_offline_cpu(dn); - if (rc) { - of_node_put(dn); - return -EINVAL; - } - - rc = dlpar_release_drc(drc_index); - if (rc) { - of_node_put(dn); - return rc; - } - - rc = dlpar_detach_node(dn); - if (rc) { - dlpar_acquire_drc(drc_index); - return rc; - } - - of_node_put(dn); - - return count; -} - -#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ - static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) { int rc; @@ -536,6 +356,9 @@ static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) case PSERIES_HP_ELOG_RESOURCE_MEM: rc = dlpar_memory(hp_elog); break; + case PSERIES_HP_ELOG_RESOURCE_CPU: + rc = dlpar_cpu(hp_elog); + break; default: pr_warn_ratelimited("Invalid resource (%d) specified\n", hp_elog->resource); @@ -565,6 +388,9 @@ static ssize_t dlpar_store(struct class *class, struct class_attribute *attr, if (!strncmp(arg, "memory", 6)) { hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; arg += strlen("memory "); + } else if (!strncmp(arg, "cpu", 3)) { + hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU; + arg += strlen("cpu "); } else { pr_err("Invalid resource specified: \"%s\"\n", buf); rc = -EINVAL; @@ -624,16 +450,7 @@ 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 */ - - rc = sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); - - return rc; + return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); } machine_device_initcall(pseries, pseries_dlpar_init); diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 62475440f..32274f72f 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -18,12 +18,15 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) "pseries-hotplug-cpu: " fmt + #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/sched.h> /* for idle_task_exit */ #include <linux/cpu.h> #include <linux/of.h> +#include <linux/slab.h> #include <asm/prom.h> #include <asm/rtas.h> #include <asm/firmware.h> @@ -32,6 +35,7 @@ #include <asm/xics.h> #include <asm/plpar_wrappers.h> +#include "pseries.h" #include "offline_states.h" /* This version can't take the spinlock, because it never returns */ @@ -88,13 +92,7 @@ void set_default_offline_state(int cpu) static void rtas_stop_self(void) { - static struct rtas_args args = { - .nargs = 0, - .nret = cpu_to_be32(1), - .rets = &args.args[0], - }; - - args.token = cpu_to_be32(rtas_stop_self_token); + static struct rtas_args args; local_irq_disable(); @@ -102,7 +100,8 @@ static void rtas_stop_self(void) printk("cpu %u (hwid %u) Ready to die...\n", smp_processor_id(), hard_smp_processor_id()); - enter_rtas(__pa(&args)); + + rtas_call_unlocked(&args, rtas_stop_self_token, 0, 1, NULL); panic("Alas, I survived.\n"); } @@ -339,6 +338,536 @@ static void pseries_remove_processor(struct device_node *np) cpu_maps_update_done(); } +static int dlpar_online_cpu(struct device_node *dn) +{ + int rc = 0; + unsigned int cpu; + int len, nthreads, i; + const __be32 *intserv; + u32 thread; + + intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); + if (!intserv) + return -EINVAL; + + nthreads = len / sizeof(u32); + + 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) != thread) + continue; + BUG_ON(get_cpu_current_state(cpu) + != CPU_STATE_OFFLINE); + cpu_maps_update_done(); + rc = device_online(get_cpu_device(cpu)); + if (rc) + goto out; + cpu_maps_update_begin(); + + break; + } + if (cpu == num_possible_cpus()) + printk(KERN_WARNING "Could not find cpu to online " + "with physical id 0x%x\n", thread); + } + cpu_maps_update_done(); + +out: + return rc; + +} + +static bool dlpar_cpu_exists(struct device_node *parent, u32 drc_index) +{ + struct device_node *child = NULL; + u32 my_drc_index; + bool found; + int rc; + + /* Assume cpu doesn't exist */ + found = false; + + for_each_child_of_node(parent, child) { + rc = of_property_read_u32(child, "ibm,my-drc-index", + &my_drc_index); + if (rc) + continue; + + if (my_drc_index == drc_index) { + of_node_put(child); + found = true; + break; + } + } + + return found; +} + +static bool valid_cpu_drc_index(struct device_node *parent, u32 drc_index) +{ + bool found = false; + int rc, index; + + index = 0; + while (!found) { + u32 drc; + + rc = of_property_read_u32_index(parent, "ibm,drc-indexes", + index++, &drc); + if (rc) + break; + + if (drc == drc_index) + found = true; + } + + return found; +} + +static ssize_t dlpar_cpu_add(u32 drc_index) +{ + struct device_node *dn, *parent; + int rc, saved_rc; + + pr_debug("Attempting to add CPU, drc index: %x\n", drc_index); + + parent = of_find_node_by_path("/cpus"); + if (!parent) { + pr_warn("Failed to find CPU root node \"/cpus\"\n"); + return -ENODEV; + } + + if (dlpar_cpu_exists(parent, drc_index)) { + of_node_put(parent); + pr_warn("CPU with drc index %x already exists\n", drc_index); + return -EINVAL; + } + + if (!valid_cpu_drc_index(parent, drc_index)) { + of_node_put(parent); + pr_warn("Cannot find CPU (drc index %x) to add.\n", drc_index); + return -EINVAL; + } + + rc = dlpar_acquire_drc(drc_index); + if (rc) { + pr_warn("Failed to acquire DRC, rc: %d, drc index: %x\n", + rc, drc_index); + of_node_put(parent); + return -EINVAL; + } + + dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent); + of_node_put(parent); + if (!dn) { + pr_warn("Failed call to configure-connector, drc index: %x\n", + drc_index); + dlpar_release_drc(drc_index); + return -EINVAL; + } + + rc = dlpar_attach_node(dn); + if (rc) { + saved_rc = rc; + pr_warn("Failed to attach node %s, rc: %d, drc index: %x\n", + dn->name, rc, drc_index); + + rc = dlpar_release_drc(drc_index); + if (!rc) + dlpar_free_cc_nodes(dn); + + return saved_rc; + } + + rc = dlpar_online_cpu(dn); + if (rc) { + saved_rc = rc; + pr_warn("Failed to online cpu %s, rc: %d, drc index: %x\n", + dn->name, rc, drc_index); + + rc = dlpar_detach_node(dn); + if (!rc) + dlpar_release_drc(drc_index); + + return saved_rc; + } + + pr_debug("Successfully added CPU %s, drc index: %x\n", dn->name, + drc_index); + return rc; +} + +static int dlpar_offline_cpu(struct device_node *dn) +{ + int rc = 0; + unsigned int cpu; + int len, nthreads, i; + const __be32 *intserv; + u32 thread; + + intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); + if (!intserv) + return -EINVAL; + + nthreads = len / sizeof(u32); + + 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) != thread) + continue; + + if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) + break; + + if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { + set_preferred_offline_state(cpu, + CPU_STATE_OFFLINE); + cpu_maps_update_done(); + rc = device_offline(get_cpu_device(cpu)); + if (rc) + goto out; + cpu_maps_update_begin(); + break; + + } + + /* + * The cpu is in CPU_STATE_INACTIVE. + * Upgrade it's state to CPU_STATE_OFFLINE. + */ + set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); + 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", thread); + } + cpu_maps_update_done(); + +out: + return rc; + +} + +static ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index) +{ + int rc; + + pr_debug("Attemping to remove CPU %s, drc index: %x\n", + dn->name, drc_index); + + rc = dlpar_offline_cpu(dn); + if (rc) { + pr_warn("Failed to offline CPU %s, rc: %d\n", dn->name, rc); + return -EINVAL; + } + + rc = dlpar_release_drc(drc_index); + if (rc) { + pr_warn("Failed to release drc (%x) for CPU %s, rc: %d\n", + drc_index, dn->name, rc); + dlpar_online_cpu(dn); + return rc; + } + + rc = dlpar_detach_node(dn); + if (rc) { + int saved_rc = rc; + + pr_warn("Failed to detach CPU %s, rc: %d", dn->name, rc); + + rc = dlpar_acquire_drc(drc_index); + if (!rc) + dlpar_online_cpu(dn); + + return saved_rc; + } + + pr_debug("Successfully removed CPU, drc index: %x\n", drc_index); + return 0; +} + +static struct device_node *cpu_drc_index_to_dn(u32 drc_index) +{ + struct device_node *dn; + u32 my_index; + int rc; + + for_each_node_by_type(dn, "cpu") { + rc = of_property_read_u32(dn, "ibm,my-drc-index", &my_index); + if (rc) + continue; + + if (my_index == drc_index) + break; + } + + return dn; +} + +static int dlpar_cpu_remove_by_index(u32 drc_index) +{ + struct device_node *dn; + int rc; + + dn = cpu_drc_index_to_dn(drc_index); + if (!dn) { + pr_warn("Cannot find CPU (drc index %x) to remove\n", + drc_index); + return -ENODEV; + } + + rc = dlpar_cpu_remove(dn, drc_index); + of_node_put(dn); + return rc; +} + +static int find_dlpar_cpus_to_remove(u32 *cpu_drcs, int cpus_to_remove) +{ + struct device_node *dn; + int cpus_found = 0; + int rc; + + /* We want to find cpus_to_remove + 1 CPUs to ensure we do not + * remove the last CPU. + */ + for_each_node_by_type(dn, "cpu") { + cpus_found++; + + if (cpus_found > cpus_to_remove) { + of_node_put(dn); + break; + } + + /* Note that cpus_found is always 1 ahead of the index + * into the cpu_drcs array, so we use cpus_found - 1 + */ + rc = of_property_read_u32(dn, "ibm,my-drc-index", + &cpu_drcs[cpus_found - 1]); + if (rc) { + pr_warn("Error occurred getting drc-index for %s\n", + dn->name); + of_node_put(dn); + return -1; + } + } + + if (cpus_found < cpus_to_remove) { + pr_warn("Failed to find enough CPUs (%d of %d) to remove\n", + cpus_found, cpus_to_remove); + } else if (cpus_found == cpus_to_remove) { + pr_warn("Cannot remove all CPUs\n"); + } + + return cpus_found; +} + +static int dlpar_cpu_remove_by_count(u32 cpus_to_remove) +{ + u32 *cpu_drcs; + int cpus_found; + int cpus_removed = 0; + int i, rc; + + pr_debug("Attempting to hot-remove %d CPUs\n", cpus_to_remove); + + cpu_drcs = kcalloc(cpus_to_remove, sizeof(*cpu_drcs), GFP_KERNEL); + if (!cpu_drcs) + return -EINVAL; + + cpus_found = find_dlpar_cpus_to_remove(cpu_drcs, cpus_to_remove); + if (cpus_found <= cpus_to_remove) { + kfree(cpu_drcs); + return -EINVAL; + } + + for (i = 0; i < cpus_to_remove; i++) { + rc = dlpar_cpu_remove_by_index(cpu_drcs[i]); + if (rc) + break; + + cpus_removed++; + } + + if (cpus_removed != cpus_to_remove) { + pr_warn("CPU hot-remove failed, adding back removed CPUs\n"); + + for (i = 0; i < cpus_removed; i++) + dlpar_cpu_add(cpu_drcs[i]); + + rc = -EINVAL; + } else { + rc = 0; + } + + kfree(cpu_drcs); + return rc; +} + +static int find_dlpar_cpus_to_add(u32 *cpu_drcs, u32 cpus_to_add) +{ + struct device_node *parent; + int cpus_found = 0; + int index, rc; + + parent = of_find_node_by_path("/cpus"); + if (!parent) { + pr_warn("Could not find CPU root node in device tree\n"); + kfree(cpu_drcs); + return -1; + } + + /* Search the ibm,drc-indexes array for possible CPU drcs to + * add. Note that the format of the ibm,drc-indexes array is + * the number of entries in the array followed by the array + * of drc values so we start looking at index = 1. + */ + index = 1; + while (cpus_found < cpus_to_add) { + u32 drc; + + rc = of_property_read_u32_index(parent, "ibm,drc-indexes", + index++, &drc); + if (rc) + break; + + if (dlpar_cpu_exists(parent, drc)) + continue; + + cpu_drcs[cpus_found++] = drc; + } + + of_node_put(parent); + return cpus_found; +} + +static int dlpar_cpu_add_by_count(u32 cpus_to_add) +{ + u32 *cpu_drcs; + int cpus_added = 0; + int cpus_found; + int i, rc; + + pr_debug("Attempting to hot-add %d CPUs\n", cpus_to_add); + + cpu_drcs = kcalloc(cpus_to_add, sizeof(*cpu_drcs), GFP_KERNEL); + if (!cpu_drcs) + return -EINVAL; + + cpus_found = find_dlpar_cpus_to_add(cpu_drcs, cpus_to_add); + if (cpus_found < cpus_to_add) { + pr_warn("Failed to find enough CPUs (%d of %d) to add\n", + cpus_found, cpus_to_add); + kfree(cpu_drcs); + return -EINVAL; + } + + for (i = 0; i < cpus_to_add; i++) { + rc = dlpar_cpu_add(cpu_drcs[i]); + if (rc) + break; + + cpus_added++; + } + + if (cpus_added < cpus_to_add) { + pr_warn("CPU hot-add failed, removing any added CPUs\n"); + + for (i = 0; i < cpus_added; i++) + dlpar_cpu_remove_by_index(cpu_drcs[i]); + + rc = -EINVAL; + } else { + rc = 0; + } + + kfree(cpu_drcs); + return rc; +} + +int dlpar_cpu(struct pseries_hp_errorlog *hp_elog) +{ + u32 count, drc_index; + int rc; + + count = hp_elog->_drc_u.drc_count; + drc_index = hp_elog->_drc_u.drc_index; + + lock_device_hotplug(); + + switch (hp_elog->action) { + case PSERIES_HP_ELOG_ACTION_REMOVE: + if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) + rc = dlpar_cpu_remove_by_count(count); + else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) + rc = dlpar_cpu_remove_by_index(drc_index); + else + rc = -EINVAL; + break; + case PSERIES_HP_ELOG_ACTION_ADD: + if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) + rc = dlpar_cpu_add_by_count(count); + else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) + rc = dlpar_cpu_add(drc_index); + else + rc = -EINVAL; + break; + default: + pr_err("Invalid action (%d) specified\n", hp_elog->action); + rc = -EINVAL; + break; + } + + unlock_device_hotplug(); + return rc; +} + +#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE + +static ssize_t dlpar_cpu_probe(const char *buf, size_t count) +{ + u32 drc_index; + int rc; + + rc = kstrtou32(buf, 0, &drc_index); + if (rc) + return -EINVAL; + + rc = dlpar_cpu_add(drc_index); + + return rc ? rc : count; +} + +static ssize_t dlpar_cpu_release(const char *buf, size_t count) +{ + struct device_node *dn; + u32 drc_index; + int rc; + + dn = of_find_node_by_path(buf); + if (!dn) + return -EINVAL; + + rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index); + if (rc) { + of_node_put(dn); + return -EINVAL; + } + + rc = dlpar_cpu_remove(dn, drc_index); + of_node_put(dn); + + return rc ? rc : count; +} + +#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ + static int pseries_smp_notifier(struct notifier_block *nb, unsigned long action, void *data) { @@ -385,6 +914,11 @@ static int __init pseries_cpu_hotplug_init(void) int cpu; int qcss_tok; +#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 */ + for_each_node_by_name(np, "interrupt-controller") { typep = of_get_property(np, "compatible", NULL); if (strstr(typep, "open-pic")) { diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index b7a67e3d2..477290ad8 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -315,48 +315,48 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot, return 0; } -static unsigned long pSeries_lpar_hpte_getword0(unsigned long slot) +static long __pSeries_lpar_hpte_find(unsigned long want_v, unsigned long hpte_group) { - unsigned long dword0; - unsigned long lpar_rc; - unsigned long dummy_word1; - unsigned long flags; + long lpar_rc; + unsigned long i, j; + struct { + unsigned long pteh; + unsigned long ptel; + } ptes[4]; - /* Read 1 pte at a time */ - /* Do not need RPN to logical page translation */ - /* No cross CEC PFT access */ - flags = 0; + for (i = 0; i < HPTES_PER_GROUP; i += 4, hpte_group += 4) { - lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1); + lpar_rc = plpar_pte_read_4(0, hpte_group, (void *)ptes); + if (lpar_rc != H_SUCCESS) + continue; - BUG_ON(lpar_rc != H_SUCCESS); + for (j = 0; j < 4; j++) { + if (HPTE_V_COMPARE(ptes[j].pteh, want_v) && + (ptes[j].pteh & HPTE_V_VALID)) + return i + j; + } + } - return dword0; + return -1; } static long pSeries_lpar_hpte_find(unsigned long vpn, int psize, int ssize) { - unsigned long hash; - unsigned long i; long slot; - unsigned long want_v, hpte_v; + unsigned long hash; + unsigned long want_v; + unsigned long hpte_group; hash = hpt_hash(vpn, mmu_psize_defs[psize].shift, ssize); want_v = hpte_encode_avpn(vpn, psize, ssize); /* Bolted entries are always in the primary group */ - slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; - for (i = 0; i < HPTES_PER_GROUP; i++) { - hpte_v = pSeries_lpar_hpte_getword0(slot); - - if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) - /* HPTE matches */ - return slot; - ++slot; - } - - return -1; -} + hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP; + slot = __pSeries_lpar_hpte_find(want_v, hpte_group); + if (slot < 0) + return -1; + return hpte_group + slot; +} static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, @@ -396,6 +396,7 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long vpn, BUG_ON(lpar_rc != H_SUCCESS); } +#ifdef CONFIG_TRANSPARENT_HUGEPAGE /* * Limit iterations holding pSeries_lpar_tlbie_lock to 3. We also need * to make sure that we avoid bouncing the hypervisor tlbie lock. @@ -494,6 +495,15 @@ static void pSeries_lpar_hugepage_invalidate(unsigned long vsid, __pSeries_lpar_hugepage_invalidate(slot_array, vpn_array, index, psize, ssize); } +#else +static void pSeries_lpar_hugepage_invalidate(unsigned long vsid, + unsigned long addr, + unsigned char *hpte_slot_array, + int psize, int ssize, int local) +{ + WARN(1, "%s called without THP support\n", __func__); +} +#endif static void pSeries_lpar_hpte_removebolted(unsigned long ea, int psize, int ssize) diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h index 8411c2729..7aa83f00a 100644 --- a/arch/powerpc/platforms/pseries/pseries.h +++ b/arch/powerpc/platforms/pseries/pseries.h @@ -73,6 +73,15 @@ static inline int dlpar_memory(struct pseries_hp_errorlog *hp_elog) } #endif +#ifdef CONFIG_HOTPLUG_CPU +int dlpar_cpu(struct pseries_hp_errorlog *hp_elog); +#else +static inline int dlpar_cpu(struct pseries_hp_errorlog *hp_elog) +{ + return -EOPNOTSUPP; +} +#endif + /* PCI root bridge prepare function override for pseries */ struct pci_host_bridge; int pseries_root_bridge_prepare(struct pci_host_bridge *bridge); diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index 3b6647e57..9a3e27b86 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -40,6 +40,9 @@ static int ras_check_exception_token; #define EPOW_SENSOR_TOKEN 9 #define EPOW_SENSOR_INDEX 0 +/* EPOW events counter variable */ +static int num_epow_events; + static irqreturn_t ras_epow_interrupt(int irq, void *dev_id); static irqreturn_t ras_error_interrupt(int irq, void *dev_id); @@ -82,32 +85,30 @@ static void handle_system_shutdown(char event_modifier) { switch (event_modifier) { case EPOW_SHUTDOWN_NORMAL: - pr_emerg("Firmware initiated power off"); + pr_emerg("Power off requested\n"); orderly_poweroff(true); break; case EPOW_SHUTDOWN_ON_UPS: - pr_emerg("Loss of power reported by firmware, system is " - "running on UPS/battery"); - pr_emerg("Check RTAS error log for details"); + pr_emerg("Loss of system power detected. System is running on" + " UPS/battery. Check RTAS error log for details\n"); orderly_poweroff(true); break; case EPOW_SHUTDOWN_LOSS_OF_CRITICAL_FUNCTIONS: - pr_emerg("Loss of system critical functions reported by " - "firmware"); - pr_emerg("Check RTAS error log for details"); + pr_emerg("Loss of system critical functions detected. Check" + " RTAS error log for details\n"); orderly_poweroff(true); break; case EPOW_SHUTDOWN_AMBIENT_TEMPERATURE_TOO_HIGH: - pr_emerg("Ambient temperature too high reported by firmware"); - pr_emerg("Check RTAS error log for details"); + pr_emerg("High ambient temperature detected. Check RTAS" + " error log for details\n"); orderly_poweroff(true); break; default: - pr_err("Unknown power/cooling shutdown event (modifier %d)", + pr_err("Unknown power/cooling shutdown event (modifier = %d)\n", event_modifier); } } @@ -145,17 +146,20 @@ static void rtas_parse_epow_errlog(struct rtas_error_log *log) switch (action_code) { case EPOW_RESET: - pr_err("Non critical power or cooling issue cleared"); + if (num_epow_events) { + pr_info("Non critical power/cooling issue cleared\n"); + num_epow_events--; + } break; case EPOW_WARN_COOLING: - pr_err("Non critical cooling issue reported by firmware"); - pr_err("Check RTAS error log for details"); + pr_info("Non-critical cooling issue detected. Check RTAS error" + " log for details\n"); break; case EPOW_WARN_POWER: - pr_err("Non critical power issue reported by firmware"); - pr_err("Check RTAS error log for details"); + pr_info("Non-critical power issue detected. Check RTAS error" + " log for details\n"); break; case EPOW_SYSTEM_SHUTDOWN: @@ -163,23 +167,27 @@ static void rtas_parse_epow_errlog(struct rtas_error_log *log) break; case EPOW_SYSTEM_HALT: - pr_emerg("Firmware initiated power off"); + pr_emerg("Critical power/cooling issue detected. Check RTAS" + " error log for details. Powering off.\n"); orderly_poweroff(true); break; case EPOW_MAIN_ENCLOSURE: case EPOW_POWER_OFF: - pr_emerg("Critical power/cooling issue reported by firmware"); - pr_emerg("Check RTAS error log for details"); - pr_emerg("Immediate power off"); + pr_emerg("System about to lose power. Check RTAS error log " + " for details. Powering off immediately.\n"); emergency_sync(); kernel_power_off(); break; default: - pr_err("Unknown power/cooling event (action code %d)", + pr_err("Unknown power/cooling event (action code = %d)\n", action_code); } + + /* Increment epow events counter variable */ + if (action_code != EPOW_RESET) + num_epow_events++; } /* Handle environmental and power warning (EPOW) interrupts. */ @@ -249,13 +257,12 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id) log_error(ras_log_buf, ERR_TYPE_RTAS_LOG, fatal); if (fatal) { - pr_emerg("Fatal hardware error reported by firmware"); - pr_emerg("Check RTAS error log for details"); - pr_emerg("Immediate power off"); + pr_emerg("Fatal hardware error detected. Check RTAS error" + " log for details. Powering off immediately\n"); emergency_sync(); kernel_power_off(); } else { - pr_err("Recoverable hardware error reported by firmware"); + pr_err("Recoverable hardware error detected\n"); } spin_unlock(&ras_log_buf_lock); |