162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pseries CPU Hotplug infrastructure. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Split out from arch/powerpc/platforms/pseries/setup.c 662306a36Sopenharmony_ci * arch/powerpc/kernel/rtas.c, and arch/powerpc/platforms/pseries/smp.c 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Peter Bergner, IBM March 2001. 962306a36Sopenharmony_ci * Copyright (C) 2001 IBM. 1062306a36Sopenharmony_ci * Dave Engebretsen, Peter Bergner, and 1162306a36Sopenharmony_ci * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com 1262306a36Sopenharmony_ci * Plus various changes from other IBM teams... 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Copyright (C) 2006 Michael Ellerman, IBM Corporation 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) "pseries-hotplug-cpu: " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/sched.h> /* for idle_task_exit */ 2362306a36Sopenharmony_ci#include <linux/sched/hotplug.h> 2462306a36Sopenharmony_ci#include <linux/cpu.h> 2562306a36Sopenharmony_ci#include <linux/of.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <asm/prom.h> 2862306a36Sopenharmony_ci#include <asm/rtas.h> 2962306a36Sopenharmony_ci#include <asm/firmware.h> 3062306a36Sopenharmony_ci#include <asm/machdep.h> 3162306a36Sopenharmony_ci#include <asm/vdso_datapage.h> 3262306a36Sopenharmony_ci#include <asm/xics.h> 3362306a36Sopenharmony_ci#include <asm/xive.h> 3462306a36Sopenharmony_ci#include <asm/plpar_wrappers.h> 3562306a36Sopenharmony_ci#include <asm/topology.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "pseries.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* This version can't take the spinlock, because it never returns */ 4062306a36Sopenharmony_cistatic int rtas_stop_self_token = RTAS_UNKNOWN_SERVICE; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Record the CPU ids used on each nodes. 4462306a36Sopenharmony_ci * Protected by cpu_add_remove_lock. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic cpumask_var_t node_recorded_ids_map[MAX_NUMNODES]; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void rtas_stop_self(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci static struct rtas_args args; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci local_irq_disable(); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci BUG_ON(rtas_stop_self_token == RTAS_UNKNOWN_SERVICE); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci rtas_call_unlocked(&args, rtas_stop_self_token, 0, 1, NULL); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci panic("Alas, I survived.\n"); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void pseries_cpu_offline_self(void) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci unsigned int hwcpu = hard_smp_processor_id(); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci local_irq_disable(); 6662306a36Sopenharmony_ci idle_task_exit(); 6762306a36Sopenharmony_ci if (xive_enabled()) 6862306a36Sopenharmony_ci xive_teardown_cpu(); 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci xics_teardown_cpu(); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci unregister_slb_shadow(hwcpu); 7362306a36Sopenharmony_ci unregister_vpa(hwcpu); 7462306a36Sopenharmony_ci rtas_stop_self(); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Should never get here... */ 7762306a36Sopenharmony_ci BUG(); 7862306a36Sopenharmony_ci for(;;); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int pseries_cpu_disable(void) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int cpu = smp_processor_id(); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci set_cpu_online(cpu, false); 8662306a36Sopenharmony_ci vdso_data->processorCount--; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /*fix boot_cpuid here*/ 8962306a36Sopenharmony_ci if (cpu == boot_cpuid) 9062306a36Sopenharmony_ci boot_cpuid = cpumask_any(cpu_online_mask); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* FIXME: abstract this to not be platform specific later on */ 9362306a36Sopenharmony_ci if (xive_enabled()) 9462306a36Sopenharmony_ci xive_smp_disable_cpu(); 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci xics_migrate_irqs_away(); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci cleanup_cpu_mmu_context(); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* 10462306a36Sopenharmony_ci * pseries_cpu_die: Wait for the cpu to die. 10562306a36Sopenharmony_ci * @cpu: logical processor id of the CPU whose death we're awaiting. 10662306a36Sopenharmony_ci * 10762306a36Sopenharmony_ci * This function is called from the context of the thread which is performing 10862306a36Sopenharmony_ci * the cpu-offline. Here we wait for long enough to allow the cpu in question 10962306a36Sopenharmony_ci * to self-destroy so that the cpu-offline thread can send the CPU_DEAD 11062306a36Sopenharmony_ci * notifications. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * OTOH, pseries_cpu_offline_self() is called by the @cpu when it wants to 11362306a36Sopenharmony_ci * self-destruct. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic void pseries_cpu_die(unsigned int cpu) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int cpu_status = 1; 11862306a36Sopenharmony_ci unsigned int pcpu = get_hard_smp_processor_id(cpu); 11962306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(120000); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci while (true) { 12262306a36Sopenharmony_ci cpu_status = smp_query_cpu_stopped(pcpu); 12362306a36Sopenharmony_ci if (cpu_status == QCSS_STOPPED || 12462306a36Sopenharmony_ci cpu_status == QCSS_HARDWARE_ERROR) 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 12862306a36Sopenharmony_ci pr_warn("CPU %i (hwid %i) didn't die after 120 seconds\n", 12962306a36Sopenharmony_ci cpu, pcpu); 13062306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(120000); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci cond_resched(); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (cpu_status == QCSS_HARDWARE_ERROR) { 13762306a36Sopenharmony_ci pr_warn("CPU %i (hwid %i) reported error while dying\n", 13862306a36Sopenharmony_ci cpu, pcpu); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci paca_ptrs[cpu]->cpu_start = 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/** 14562306a36Sopenharmony_ci * find_cpu_id_range - found a linear ranger of @nthreads free CPU ids. 14662306a36Sopenharmony_ci * @nthreads : the number of threads (cpu ids) 14762306a36Sopenharmony_ci * @assigned_node : the node it belongs to or NUMA_NO_NODE if free ids from any 14862306a36Sopenharmony_ci * node can be peek. 14962306a36Sopenharmony_ci * @cpu_mask: the returned CPU mask. 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * Returns 0 on success. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic int find_cpu_id_range(unsigned int nthreads, int assigned_node, 15462306a36Sopenharmony_ci cpumask_var_t *cpu_mask) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci cpumask_var_t candidate_mask; 15762306a36Sopenharmony_ci unsigned int cpu, node; 15862306a36Sopenharmony_ci int rc = -ENOSPC; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!zalloc_cpumask_var(&candidate_mask, GFP_KERNEL)) 16162306a36Sopenharmony_ci return -ENOMEM; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci cpumask_clear(*cpu_mask); 16462306a36Sopenharmony_ci for (cpu = 0; cpu < nthreads; cpu++) 16562306a36Sopenharmony_ci cpumask_set_cpu(cpu, *cpu_mask); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci BUG_ON(!cpumask_subset(cpu_present_mask, cpu_possible_mask)); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Get a bitmap of unoccupied slots. */ 17062306a36Sopenharmony_ci cpumask_xor(candidate_mask, cpu_possible_mask, cpu_present_mask); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (assigned_node != NUMA_NO_NODE) { 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * Remove free ids previously assigned on the other nodes. We 17562306a36Sopenharmony_ci * can walk only online nodes because once a node became online 17662306a36Sopenharmony_ci * it is not turned offlined back. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci for_each_online_node(node) { 17962306a36Sopenharmony_ci if (node == assigned_node) 18062306a36Sopenharmony_ci continue; 18162306a36Sopenharmony_ci cpumask_andnot(candidate_mask, candidate_mask, 18262306a36Sopenharmony_ci node_recorded_ids_map[node]); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (cpumask_empty(candidate_mask)) 18762306a36Sopenharmony_ci goto out; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci while (!cpumask_empty(*cpu_mask)) { 19062306a36Sopenharmony_ci if (cpumask_subset(*cpu_mask, candidate_mask)) 19162306a36Sopenharmony_ci /* Found a range where we can insert the new cpu(s) */ 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci cpumask_shift_left(*cpu_mask, *cpu_mask, nthreads); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!cpumask_empty(*cpu_mask)) 19762306a36Sopenharmony_ci rc = 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciout: 20062306a36Sopenharmony_ci free_cpumask_var(candidate_mask); 20162306a36Sopenharmony_ci return rc; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * Update cpu_present_mask and paca(s) for a new cpu node. The wrinkle 20662306a36Sopenharmony_ci * here is that a cpu device node may represent multiple logical cpus 20762306a36Sopenharmony_ci * in the SMT case. We must honor the assumption in other code that 20862306a36Sopenharmony_ci * the logical ids for sibling SMT threads x and y are adjacent, such 20962306a36Sopenharmony_ci * that x^1 == y and y^1 == x. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic int pseries_add_processor(struct device_node *np) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int len, nthreads, node, cpu, assigned_node; 21462306a36Sopenharmony_ci int rc = 0; 21562306a36Sopenharmony_ci cpumask_var_t cpu_mask; 21662306a36Sopenharmony_ci const __be32 *intserv; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", &len); 21962306a36Sopenharmony_ci if (!intserv) 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci nthreads = len / sizeof(u32); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (!alloc_cpumask_var(&cpu_mask, GFP_KERNEL)) 22562306a36Sopenharmony_ci return -ENOMEM; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * Fetch from the DT nodes read by dlpar_configure_connector() the NUMA 22962306a36Sopenharmony_ci * node id the added CPU belongs to. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci node = of_node_to_nid(np); 23262306a36Sopenharmony_ci if (node < 0 || !node_possible(node)) 23362306a36Sopenharmony_ci node = first_online_node; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci BUG_ON(node == NUMA_NO_NODE); 23662306a36Sopenharmony_ci assigned_node = node; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci cpu_maps_update_begin(); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci rc = find_cpu_id_range(nthreads, node, &cpu_mask); 24162306a36Sopenharmony_ci if (rc && nr_node_ids > 1) { 24262306a36Sopenharmony_ci /* 24362306a36Sopenharmony_ci * Try again, considering the free CPU ids from the other node. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci node = NUMA_NO_NODE; 24662306a36Sopenharmony_ci rc = find_cpu_id_range(nthreads, NUMA_NO_NODE, &cpu_mask); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (rc) { 25062306a36Sopenharmony_ci pr_err("Cannot add cpu %pOF; this system configuration" 25162306a36Sopenharmony_ci " supports %d logical cpus.\n", np, num_possible_cpus()); 25262306a36Sopenharmony_ci goto out; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for_each_cpu(cpu, cpu_mask) { 25662306a36Sopenharmony_ci BUG_ON(cpu_present(cpu)); 25762306a36Sopenharmony_ci set_cpu_present(cpu, true); 25862306a36Sopenharmony_ci set_hard_smp_processor_id(cpu, be32_to_cpu(*intserv++)); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Record the newly used CPU ids for the associate node. */ 26262306a36Sopenharmony_ci cpumask_or(node_recorded_ids_map[assigned_node], 26362306a36Sopenharmony_ci node_recorded_ids_map[assigned_node], cpu_mask); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * If node is set to NUMA_NO_NODE, CPU ids have be reused from 26762306a36Sopenharmony_ci * another node, remove them from its mask. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci if (node == NUMA_NO_NODE) { 27062306a36Sopenharmony_ci cpu = cpumask_first(cpu_mask); 27162306a36Sopenharmony_ci pr_warn("Reusing free CPU ids %d-%d from another node\n", 27262306a36Sopenharmony_ci cpu, cpu + nthreads - 1); 27362306a36Sopenharmony_ci for_each_online_node(node) { 27462306a36Sopenharmony_ci if (node == assigned_node) 27562306a36Sopenharmony_ci continue; 27662306a36Sopenharmony_ci cpumask_andnot(node_recorded_ids_map[node], 27762306a36Sopenharmony_ci node_recorded_ids_map[node], 27862306a36Sopenharmony_ci cpu_mask); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciout: 28362306a36Sopenharmony_ci cpu_maps_update_done(); 28462306a36Sopenharmony_ci free_cpumask_var(cpu_mask); 28562306a36Sopenharmony_ci return rc; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * Update the present map for a cpu node which is going away, and set 29062306a36Sopenharmony_ci * the hard id in the paca(s) to -1 to be consistent with boot time 29162306a36Sopenharmony_ci * convention for non-present cpus. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_cistatic void pseries_remove_processor(struct device_node *np) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci unsigned int cpu; 29662306a36Sopenharmony_ci int len, nthreads, i; 29762306a36Sopenharmony_ci const __be32 *intserv; 29862306a36Sopenharmony_ci u32 thread; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", &len); 30162306a36Sopenharmony_ci if (!intserv) 30262306a36Sopenharmony_ci return; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci nthreads = len / sizeof(u32); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci cpu_maps_update_begin(); 30762306a36Sopenharmony_ci for (i = 0; i < nthreads; i++) { 30862306a36Sopenharmony_ci thread = be32_to_cpu(intserv[i]); 30962306a36Sopenharmony_ci for_each_present_cpu(cpu) { 31062306a36Sopenharmony_ci if (get_hard_smp_processor_id(cpu) != thread) 31162306a36Sopenharmony_ci continue; 31262306a36Sopenharmony_ci BUG_ON(cpu_online(cpu)); 31362306a36Sopenharmony_ci set_cpu_present(cpu, false); 31462306a36Sopenharmony_ci set_hard_smp_processor_id(cpu, -1); 31562306a36Sopenharmony_ci update_numa_cpu_lookup_table(cpu, -1); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci if (cpu >= nr_cpu_ids) 31962306a36Sopenharmony_ci printk(KERN_WARNING "Could not find cpu to remove " 32062306a36Sopenharmony_ci "with physical id 0x%x\n", thread); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci cpu_maps_update_done(); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int dlpar_offline_cpu(struct device_node *dn) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci int rc = 0; 32862306a36Sopenharmony_ci unsigned int cpu; 32962306a36Sopenharmony_ci int len, nthreads, i; 33062306a36Sopenharmony_ci const __be32 *intserv; 33162306a36Sopenharmony_ci u32 thread; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 33462306a36Sopenharmony_ci if (!intserv) 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci nthreads = len / sizeof(u32); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci cpu_maps_update_begin(); 34062306a36Sopenharmony_ci for (i = 0; i < nthreads; i++) { 34162306a36Sopenharmony_ci thread = be32_to_cpu(intserv[i]); 34262306a36Sopenharmony_ci for_each_present_cpu(cpu) { 34362306a36Sopenharmony_ci if (get_hard_smp_processor_id(cpu) != thread) 34462306a36Sopenharmony_ci continue; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!cpu_online(cpu)) 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * device_offline() will return -EBUSY (via cpu_down()) if there 35162306a36Sopenharmony_ci * is only one CPU left. Check it here to fail earlier and with a 35262306a36Sopenharmony_ci * more informative error message, while also retaining the 35362306a36Sopenharmony_ci * cpu_add_remove_lock to be sure that no CPUs are being 35462306a36Sopenharmony_ci * online/offlined during this check. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci if (num_online_cpus() == 1) { 35762306a36Sopenharmony_ci pr_warn("Unable to remove last online CPU %pOFn\n", dn); 35862306a36Sopenharmony_ci rc = -EBUSY; 35962306a36Sopenharmony_ci goto out_unlock; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci cpu_maps_update_done(); 36362306a36Sopenharmony_ci rc = device_offline(get_cpu_device(cpu)); 36462306a36Sopenharmony_ci if (rc) 36562306a36Sopenharmony_ci goto out; 36662306a36Sopenharmony_ci cpu_maps_update_begin(); 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci if (cpu == num_possible_cpus()) { 37062306a36Sopenharmony_ci pr_warn("Could not find cpu to offline with physical id 0x%x\n", 37162306a36Sopenharmony_ci thread); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ciout_unlock: 37562306a36Sopenharmony_ci cpu_maps_update_done(); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ciout: 37862306a36Sopenharmony_ci return rc; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int dlpar_online_cpu(struct device_node *dn) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci int rc = 0; 38462306a36Sopenharmony_ci unsigned int cpu; 38562306a36Sopenharmony_ci int len, nthreads, i; 38662306a36Sopenharmony_ci const __be32 *intserv; 38762306a36Sopenharmony_ci u32 thread; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 39062306a36Sopenharmony_ci if (!intserv) 39162306a36Sopenharmony_ci return -EINVAL; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci nthreads = len / sizeof(u32); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci cpu_maps_update_begin(); 39662306a36Sopenharmony_ci for (i = 0; i < nthreads; i++) { 39762306a36Sopenharmony_ci thread = be32_to_cpu(intserv[i]); 39862306a36Sopenharmony_ci for_each_present_cpu(cpu) { 39962306a36Sopenharmony_ci if (get_hard_smp_processor_id(cpu) != thread) 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!topology_is_primary_thread(cpu)) { 40362306a36Sopenharmony_ci if (cpu_smt_control != CPU_SMT_ENABLED) 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci if (!topology_smt_thread_allowed(cpu)) 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci cpu_maps_update_done(); 41062306a36Sopenharmony_ci find_and_update_cpu_nid(cpu); 41162306a36Sopenharmony_ci rc = device_online(get_cpu_device(cpu)); 41262306a36Sopenharmony_ci if (rc) { 41362306a36Sopenharmony_ci dlpar_offline_cpu(dn); 41462306a36Sopenharmony_ci goto out; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci cpu_maps_update_begin(); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci if (cpu == num_possible_cpus()) 42162306a36Sopenharmony_ci printk(KERN_WARNING "Could not find cpu to online " 42262306a36Sopenharmony_ci "with physical id 0x%x\n", thread); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci cpu_maps_update_done(); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ciout: 42762306a36Sopenharmony_ci return rc; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic bool dlpar_cpu_exists(struct device_node *parent, u32 drc_index) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct device_node *child = NULL; 43462306a36Sopenharmony_ci u32 my_drc_index; 43562306a36Sopenharmony_ci bool found; 43662306a36Sopenharmony_ci int rc; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Assume cpu doesn't exist */ 43962306a36Sopenharmony_ci found = false; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci for_each_child_of_node(parent, child) { 44262306a36Sopenharmony_ci rc = of_property_read_u32(child, "ibm,my-drc-index", 44362306a36Sopenharmony_ci &my_drc_index); 44462306a36Sopenharmony_ci if (rc) 44562306a36Sopenharmony_ci continue; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (my_drc_index == drc_index) { 44862306a36Sopenharmony_ci of_node_put(child); 44962306a36Sopenharmony_ci found = true; 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return found; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic bool drc_info_valid_index(struct device_node *parent, u32 drc_index) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct property *info; 46062306a36Sopenharmony_ci struct of_drc_info drc; 46162306a36Sopenharmony_ci const __be32 *value; 46262306a36Sopenharmony_ci u32 index; 46362306a36Sopenharmony_ci int count, i, j; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci info = of_find_property(parent, "ibm,drc-info", NULL); 46662306a36Sopenharmony_ci if (!info) 46762306a36Sopenharmony_ci return false; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci value = of_prop_next_u32(info, NULL, &count); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* First value of ibm,drc-info is number of drc-info records */ 47262306a36Sopenharmony_ci if (value) 47362306a36Sopenharmony_ci value++; 47462306a36Sopenharmony_ci else 47562306a36Sopenharmony_ci return false; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci for (i = 0; i < count; i++) { 47862306a36Sopenharmony_ci if (of_read_drc_info_cell(&info, &value, &drc)) 47962306a36Sopenharmony_ci return false; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (strncmp(drc.drc_type, "CPU", 3)) 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (drc_index > drc.last_drc_index) 48562306a36Sopenharmony_ci continue; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci index = drc.drc_index_start; 48862306a36Sopenharmony_ci for (j = 0; j < drc.num_sequential_elems; j++) { 48962306a36Sopenharmony_ci if (drc_index == index) 49062306a36Sopenharmony_ci return true; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci index += drc.sequential_inc; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return false; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic bool valid_cpu_drc_index(struct device_node *parent, u32 drc_index) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci bool found = false; 50262306a36Sopenharmony_ci int rc, index; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (of_property_present(parent, "ibm,drc-info")) 50562306a36Sopenharmony_ci return drc_info_valid_index(parent, drc_index); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Note that the format of the ibm,drc-indexes array is 50862306a36Sopenharmony_ci * the number of entries in the array followed by the array 50962306a36Sopenharmony_ci * of drc values so we start looking at index = 1. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci index = 1; 51262306a36Sopenharmony_ci while (!found) { 51362306a36Sopenharmony_ci u32 drc; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci rc = of_property_read_u32_index(parent, "ibm,drc-indexes", 51662306a36Sopenharmony_ci index++, &drc); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (rc) 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (drc == drc_index) 52262306a36Sopenharmony_ci found = true; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return found; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int pseries_cpuhp_attach_nodes(struct device_node *dn) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct of_changeset cs; 53162306a36Sopenharmony_ci int ret; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * This device node is unattached but may have siblings; open-code the 53562306a36Sopenharmony_ci * traversal. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci for (of_changeset_init(&cs); dn != NULL; dn = dn->sibling) { 53862306a36Sopenharmony_ci ret = of_changeset_attach_node(&cs, dn); 53962306a36Sopenharmony_ci if (ret) 54062306a36Sopenharmony_ci goto out; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci ret = of_changeset_apply(&cs); 54462306a36Sopenharmony_ciout: 54562306a36Sopenharmony_ci of_changeset_destroy(&cs); 54662306a36Sopenharmony_ci return ret; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic ssize_t dlpar_cpu_add(u32 drc_index) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct device_node *dn, *parent; 55262306a36Sopenharmony_ci int rc, saved_rc; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci pr_debug("Attempting to add CPU, drc index: %x\n", drc_index); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci parent = of_find_node_by_path("/cpus"); 55762306a36Sopenharmony_ci if (!parent) { 55862306a36Sopenharmony_ci pr_warn("Failed to find CPU root node \"/cpus\"\n"); 55962306a36Sopenharmony_ci return -ENODEV; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (dlpar_cpu_exists(parent, drc_index)) { 56362306a36Sopenharmony_ci of_node_put(parent); 56462306a36Sopenharmony_ci pr_warn("CPU with drc index %x already exists\n", drc_index); 56562306a36Sopenharmony_ci return -EINVAL; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (!valid_cpu_drc_index(parent, drc_index)) { 56962306a36Sopenharmony_ci of_node_put(parent); 57062306a36Sopenharmony_ci pr_warn("Cannot find CPU (drc index %x) to add.\n", drc_index); 57162306a36Sopenharmony_ci return -EINVAL; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci rc = dlpar_acquire_drc(drc_index); 57562306a36Sopenharmony_ci if (rc) { 57662306a36Sopenharmony_ci pr_warn("Failed to acquire DRC, rc: %d, drc index: %x\n", 57762306a36Sopenharmony_ci rc, drc_index); 57862306a36Sopenharmony_ci of_node_put(parent); 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent); 58362306a36Sopenharmony_ci if (!dn) { 58462306a36Sopenharmony_ci pr_warn("Failed call to configure-connector, drc index: %x\n", 58562306a36Sopenharmony_ci drc_index); 58662306a36Sopenharmony_ci dlpar_release_drc(drc_index); 58762306a36Sopenharmony_ci of_node_put(parent); 58862306a36Sopenharmony_ci return -EINVAL; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci rc = pseries_cpuhp_attach_nodes(dn); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Regardless we are done with parent now */ 59462306a36Sopenharmony_ci of_node_put(parent); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (rc) { 59762306a36Sopenharmony_ci saved_rc = rc; 59862306a36Sopenharmony_ci pr_warn("Failed to attach node %pOFn, rc: %d, drc index: %x\n", 59962306a36Sopenharmony_ci dn, rc, drc_index); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci rc = dlpar_release_drc(drc_index); 60262306a36Sopenharmony_ci if (!rc) 60362306a36Sopenharmony_ci dlpar_free_cc_nodes(dn); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return saved_rc; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci update_numa_distance(dn); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci rc = dlpar_online_cpu(dn); 61162306a36Sopenharmony_ci if (rc) { 61262306a36Sopenharmony_ci saved_rc = rc; 61362306a36Sopenharmony_ci pr_warn("Failed to online cpu %pOFn, rc: %d, drc index: %x\n", 61462306a36Sopenharmony_ci dn, rc, drc_index); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci rc = dlpar_detach_node(dn); 61762306a36Sopenharmony_ci if (!rc) 61862306a36Sopenharmony_ci dlpar_release_drc(drc_index); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return saved_rc; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci pr_debug("Successfully added CPU %pOFn, drc index: %x\n", dn, 62462306a36Sopenharmony_ci drc_index); 62562306a36Sopenharmony_ci return rc; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic unsigned int pseries_cpuhp_cache_use_count(const struct device_node *cachedn) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci unsigned int use_count = 0; 63162306a36Sopenharmony_ci struct device_node *dn, *tn; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci WARN_ON(!of_node_is_type(cachedn, "cache")); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci for_each_of_cpu_node(dn) { 63662306a36Sopenharmony_ci tn = of_find_next_cache_node(dn); 63762306a36Sopenharmony_ci of_node_put(tn); 63862306a36Sopenharmony_ci if (tn == cachedn) 63962306a36Sopenharmony_ci use_count++; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci for_each_node_by_type(dn, "cache") { 64362306a36Sopenharmony_ci tn = of_find_next_cache_node(dn); 64462306a36Sopenharmony_ci of_node_put(tn); 64562306a36Sopenharmony_ci if (tn == cachedn) 64662306a36Sopenharmony_ci use_count++; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return use_count; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int pseries_cpuhp_detach_nodes(struct device_node *cpudn) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct device_node *dn; 65562306a36Sopenharmony_ci struct of_changeset cs; 65662306a36Sopenharmony_ci int ret = 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci of_changeset_init(&cs); 65962306a36Sopenharmony_ci ret = of_changeset_detach_node(&cs, cpudn); 66062306a36Sopenharmony_ci if (ret) 66162306a36Sopenharmony_ci goto out; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci dn = cpudn; 66462306a36Sopenharmony_ci while ((dn = of_find_next_cache_node(dn))) { 66562306a36Sopenharmony_ci if (pseries_cpuhp_cache_use_count(dn) > 1) { 66662306a36Sopenharmony_ci of_node_put(dn); 66762306a36Sopenharmony_ci break; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = of_changeset_detach_node(&cs, dn); 67162306a36Sopenharmony_ci of_node_put(dn); 67262306a36Sopenharmony_ci if (ret) 67362306a36Sopenharmony_ci goto out; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = of_changeset_apply(&cs); 67762306a36Sopenharmony_ciout: 67862306a36Sopenharmony_ci of_changeset_destroy(&cs); 67962306a36Sopenharmony_ci return ret; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic ssize_t dlpar_cpu_remove(struct device_node *dn, u32 drc_index) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci int rc; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci pr_debug("Attempting to remove CPU %pOFn, drc index: %x\n", 68762306a36Sopenharmony_ci dn, drc_index); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci rc = dlpar_offline_cpu(dn); 69062306a36Sopenharmony_ci if (rc) { 69162306a36Sopenharmony_ci pr_warn("Failed to offline CPU %pOFn, rc: %d\n", dn, rc); 69262306a36Sopenharmony_ci return -EINVAL; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci rc = dlpar_release_drc(drc_index); 69662306a36Sopenharmony_ci if (rc) { 69762306a36Sopenharmony_ci pr_warn("Failed to release drc (%x) for CPU %pOFn, rc: %d\n", 69862306a36Sopenharmony_ci drc_index, dn, rc); 69962306a36Sopenharmony_ci dlpar_online_cpu(dn); 70062306a36Sopenharmony_ci return rc; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci rc = pseries_cpuhp_detach_nodes(dn); 70462306a36Sopenharmony_ci if (rc) { 70562306a36Sopenharmony_ci int saved_rc = rc; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci pr_warn("Failed to detach CPU %pOFn, rc: %d", dn, rc); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci rc = dlpar_acquire_drc(drc_index); 71062306a36Sopenharmony_ci if (!rc) 71162306a36Sopenharmony_ci dlpar_online_cpu(dn); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return saved_rc; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci pr_debug("Successfully removed CPU, drc index: %x\n", drc_index); 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic struct device_node *cpu_drc_index_to_dn(u32 drc_index) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct device_node *dn; 72362306a36Sopenharmony_ci u32 my_index; 72462306a36Sopenharmony_ci int rc; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci for_each_node_by_type(dn, "cpu") { 72762306a36Sopenharmony_ci rc = of_property_read_u32(dn, "ibm,my-drc-index", &my_index); 72862306a36Sopenharmony_ci if (rc) 72962306a36Sopenharmony_ci continue; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (my_index == drc_index) 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return dn; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int dlpar_cpu_remove_by_index(u32 drc_index) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct device_node *dn; 74162306a36Sopenharmony_ci int rc; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci dn = cpu_drc_index_to_dn(drc_index); 74462306a36Sopenharmony_ci if (!dn) { 74562306a36Sopenharmony_ci pr_warn("Cannot find CPU (drc index %x) to remove\n", 74662306a36Sopenharmony_ci drc_index); 74762306a36Sopenharmony_ci return -ENODEV; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci rc = dlpar_cpu_remove(dn, drc_index); 75162306a36Sopenharmony_ci of_node_put(dn); 75262306a36Sopenharmony_ci return rc; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ciint dlpar_cpu(struct pseries_hp_errorlog *hp_elog) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci u32 drc_index; 75862306a36Sopenharmony_ci int rc; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci drc_index = hp_elog->_drc_u.drc_index; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci lock_device_hotplug(); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci switch (hp_elog->action) { 76562306a36Sopenharmony_ci case PSERIES_HP_ELOG_ACTION_REMOVE: 76662306a36Sopenharmony_ci if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { 76762306a36Sopenharmony_ci rc = dlpar_cpu_remove_by_index(drc_index); 76862306a36Sopenharmony_ci /* 76962306a36Sopenharmony_ci * Setting the isolation state of an UNISOLATED/CONFIGURED 77062306a36Sopenharmony_ci * device to UNISOLATE is a no-op, but the hypervisor can 77162306a36Sopenharmony_ci * use it as a hint that the CPU removal failed. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ci if (rc) 77462306a36Sopenharmony_ci dlpar_unisolate_drc(drc_index); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci else 77762306a36Sopenharmony_ci rc = -EINVAL; 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci case PSERIES_HP_ELOG_ACTION_ADD: 78062306a36Sopenharmony_ci if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) 78162306a36Sopenharmony_ci rc = dlpar_cpu_add(drc_index); 78262306a36Sopenharmony_ci else 78362306a36Sopenharmony_ci rc = -EINVAL; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci default: 78662306a36Sopenharmony_ci pr_err("Invalid action (%d) specified\n", hp_elog->action); 78762306a36Sopenharmony_ci rc = -EINVAL; 78862306a36Sopenharmony_ci break; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci unlock_device_hotplug(); 79262306a36Sopenharmony_ci return rc; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic ssize_t dlpar_cpu_probe(const char *buf, size_t count) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci u32 drc_index; 80062306a36Sopenharmony_ci int rc; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci rc = kstrtou32(buf, 0, &drc_index); 80362306a36Sopenharmony_ci if (rc) 80462306a36Sopenharmony_ci return -EINVAL; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci rc = dlpar_cpu_add(drc_index); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return rc ? rc : count; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic ssize_t dlpar_cpu_release(const char *buf, size_t count) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct device_node *dn; 81462306a36Sopenharmony_ci u32 drc_index; 81562306a36Sopenharmony_ci int rc; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci dn = of_find_node_by_path(buf); 81862306a36Sopenharmony_ci if (!dn) 81962306a36Sopenharmony_ci return -EINVAL; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index); 82262306a36Sopenharmony_ci if (rc) { 82362306a36Sopenharmony_ci of_node_put(dn); 82462306a36Sopenharmony_ci return -EINVAL; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci rc = dlpar_cpu_remove(dn, drc_index); 82862306a36Sopenharmony_ci of_node_put(dn); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return rc ? rc : count; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic int pseries_smp_notifier(struct notifier_block *nb, 83662306a36Sopenharmony_ci unsigned long action, void *data) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci struct of_reconfig_data *rd = data; 83962306a36Sopenharmony_ci int err = 0; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci switch (action) { 84262306a36Sopenharmony_ci case OF_RECONFIG_ATTACH_NODE: 84362306a36Sopenharmony_ci err = pseries_add_processor(rd->dn); 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci case OF_RECONFIG_DETACH_NODE: 84662306a36Sopenharmony_ci pseries_remove_processor(rd->dn); 84762306a36Sopenharmony_ci break; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci return notifier_from_errno(err); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic struct notifier_block pseries_smp_nb = { 85362306a36Sopenharmony_ci .notifier_call = pseries_smp_notifier, 85462306a36Sopenharmony_ci}; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_civoid __init pseries_cpu_hotplug_init(void) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci int qcss_tok; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci rtas_stop_self_token = rtas_function_token(RTAS_FN_STOP_SELF); 86162306a36Sopenharmony_ci qcss_tok = rtas_function_token(RTAS_FN_QUERY_CPU_STOPPED_STATE); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (rtas_stop_self_token == RTAS_UNKNOWN_SERVICE || 86462306a36Sopenharmony_ci qcss_tok == RTAS_UNKNOWN_SERVICE) { 86562306a36Sopenharmony_ci printk(KERN_INFO "CPU Hotplug not supported by firmware " 86662306a36Sopenharmony_ci "- disabling.\n"); 86762306a36Sopenharmony_ci return; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci smp_ops->cpu_offline_self = pseries_cpu_offline_self; 87162306a36Sopenharmony_ci smp_ops->cpu_disable = pseries_cpu_disable; 87262306a36Sopenharmony_ci smp_ops->cpu_die = pseries_cpu_die; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int __init pseries_dlpar_init(void) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci unsigned int node; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 88062306a36Sopenharmony_ci ppc_md.cpu_probe = dlpar_cpu_probe; 88162306a36Sopenharmony_ci ppc_md.cpu_release = dlpar_cpu_release; 88262306a36Sopenharmony_ci#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* Processors can be added/removed only on LPAR */ 88562306a36Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_LPAR)) { 88662306a36Sopenharmony_ci for_each_node(node) { 88762306a36Sopenharmony_ci if (!alloc_cpumask_var_node(&node_recorded_ids_map[node], 88862306a36Sopenharmony_ci GFP_KERNEL, node)) 88962306a36Sopenharmony_ci return -ENOMEM; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* Record ids of CPU added at boot time */ 89262306a36Sopenharmony_ci cpumask_copy(node_recorded_ids_map[node], 89362306a36Sopenharmony_ci cpumask_of_node(node)); 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci of_reconfig_notifier_register(&pseries_smp_nb); 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return 0; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_cimachine_arch_initcall(pseries, pseries_dlpar_init); 902