diff options
Diffstat (limited to 'usr/src/uts/sun4u/ngdr/io/dr_cpu.c')
| -rw-r--r-- | usr/src/uts/sun4u/ngdr/io/dr_cpu.c | 934 |
1 files changed, 934 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/ngdr/io/dr_cpu.c b/usr/src/uts/sun4u/ngdr/io/dr_cpu.c new file mode 100644 index 0000000000..d9f1f17247 --- /dev/null +++ b/usr/src/uts/sun4u/ngdr/io/dr_cpu.c @@ -0,0 +1,934 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * CPU support routines for DR + */ + +#include <sys/note.h> +#include <sys/debug.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/cred.h> +#include <sys/dditypes.h> +#include <sys/devops.h> +#include <sys/modctl.h> +#include <sys/poll.h> +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/ndi_impldefs.h> +#include <sys/stat.h> +#include <sys/kmem.h> +#include <sys/processor.h> +#include <sys/cpuvar.h> +#include <sys/mem_config.h> +#include <sys/promif.h> +#include <sys/x_call.h> +#include <sys/cpu_sgnblk_defs.h> +#include <sys/membar.h> +#include <sys/stack.h> +#include <sys/sysmacros.h> +#include <sys/machsystm.h> +#include <sys/spitregs.h> + +#include <sys/archsystm.h> +#include <vm/hat_sfmmu.h> +#include <sys/pte.h> +#include <sys/mmu.h> +#include <sys/x_call.h> +#include <sys/cpu_module.h> +#include <sys/cheetahregs.h> + +#include <sys/autoconf.h> +#include <sys/cmn_err.h> + +#include <sys/dr.h> +#include <sys/dr_util.h> + +#ifdef _STARFIRE +#include <sys/starfire.h> +extern struct cpu *SIGBCPU; +#else +/* for the DR*INTERNAL_ERROR macros. see sys/dr.h. */ +static char *dr_ie_fmt = "dr_cpu.c %d"; +#endif /* _STARFIRE */ + +int +dr_cpu_unit_is_sane(dr_board_t *bp, dr_cpu_unit_t *cp) +{ +#ifdef DEBUG + processorid_t cpuid; + + /* + * cpuid and unit number should never be different + * than they were at discovery/connect time + */ + ASSERT(drmach_cpu_get_id(cp->sbc_cm.sbdev_id, &cpuid) == 0); + + ASSERT(cp->sbc_cm.sbdev_bp == bp); + ASSERT(cp->sbc_cm.sbdev_type == SBD_COMP_CPU); + ASSERT(cp->sbc_cpu_id == cpuid); +#else + _NOTE(ARGUNUSED(bp)) + _NOTE(ARGUNUSED(cp)) +#endif + + return (1); +} + +static int +dr_errno2ecode(int error) +{ + int rv; + + switch (error) { + case EBUSY: + rv = ESBD_BUSY; + break; + case EINVAL: + rv = ESBD_INVAL; + break; + case EALREADY: + rv = ESBD_ALREADY; + break; + case ENODEV: + rv = ESBD_NODEV; + break; + case ENOMEM: + rv = ESBD_NOMEM; + break; + default: + rv = ESBD_INVAL; + } + + return (rv); +} + +static void +dr_cpu_set_prop(dr_cpu_unit_t *cp) +{ + sbd_error_t *err; + dev_info_t *dip; + uint64_t clock_freq; + int ecache_size = 0; + char *cache_str = NULL; + + err = drmach_get_dip(cp->sbc_cm.sbdev_id, &dip); + if (err) { + DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err); + return; + } + + if (dip == NULL) { +#ifndef _STARFIRE + /* + * Do not report an error on Starfire since + * the dip will not be created until after + * the CPU has been configured. + */ + DR_DEV_INTERNAL_ERROR(&cp->sbc_cm); +#endif /* !_STARFIRE */ + return; + } + + /* read in the CPU speed */ + clock_freq = (unsigned int)ddi_prop_get_int(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "clock-frequency", 0); + + ASSERT(clock_freq != 0); + + /* + * The ecache property string is not the same + * for all CPU implementations. + */ + switch (cp->sbc_cpu_impl) { + case BLACKBIRD_IMPL: + case CHEETAH_IMPL: + case CHEETAH_PLUS_IMPL: + cache_str = "ecache-size"; + break; + case JAGUAR_IMPL: + cache_str = "l2-cache-size"; + break; + case PANTHER_IMPL: + cache_str = "l3-cache-size"; + break; + default: + cmn_err(CE_WARN, "Unknown cpu implementation=0x%x", + cp->sbc_cpu_impl); + ASSERT(0); + break; + } + + if (cache_str != NULL) { + /* read in the ecache size */ + ecache_size = ddi_prop_get_int(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, cache_str, 0); + } + + ASSERT(ecache_size != 0); + + /* convert to the proper units */ + cp->sbc_speed = (clock_freq + 500000) / 1000000; + cp->sbc_ecache = ecache_size / (1024 * 1024); +} + +void +dr_init_cpu_unit(dr_cpu_unit_t *cp) +{ + sbd_error_t *err; + dr_state_t new_state; + int cpuid; + int impl; + + if (DR_DEV_IS_ATTACHED(&cp->sbc_cm)) { + new_state = DR_STATE_CONFIGURED; + cp->sbc_cm.sbdev_cond = SBD_COND_OK; + } else if (DR_DEV_IS_PRESENT(&cp->sbc_cm)) { + new_state = DR_STATE_CONNECTED; + cp->sbc_cm.sbdev_cond = SBD_COND_OK; + } else { + new_state = DR_STATE_EMPTY; + cp->sbc_cm.sbdev_cond = SBD_COND_UNKNOWN; + } + + if (DR_DEV_IS_PRESENT(&cp->sbc_cm)) { + err = drmach_cpu_get_id(cp->sbc_cm.sbdev_id, &cpuid); + if (err) { + DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err); + new_state = DR_STATE_FATAL; + goto done; + } + + err = drmach_cpu_get_impl(cp->sbc_cm.sbdev_id, &impl); + if (err) { + DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err); + new_state = DR_STATE_FATAL; + goto done; + } + } else { + cp->sbc_cpu_id = -1; + cp->sbc_cpu_impl = -1; + goto done; + } + + cp->sbc_cpu_id = cpuid; + cp->sbc_cpu_impl = impl; + + /* if true at init time, it must always be true */ + ASSERT(dr_cpu_unit_is_sane(cp->sbc_cm.sbdev_bp, cp)); + + mutex_enter(&cpu_lock); + if ((cpuid >= 0) && cpu[cpuid]) + cp->sbc_cpu_flags = cpu[cpuid]->cpu_flags; + else + cp->sbc_cpu_flags = P_OFFLINE | P_POWEROFF; + mutex_exit(&cpu_lock); + + dr_cpu_set_prop(cp); + +done: + /* delay transition until fully initialized */ + dr_device_transition(&cp->sbc_cm, new_state); +} + +int +dr_pre_attach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) +{ + int i; + int curr_cpu; + int next_cpu; + static fn_t f = "dr_pre_attach_cpu"; + + PR_CPU("%s...\n", f); + + for (next_cpu = 0, i = 0; i < devnum; i++) { + dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i]; + + ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up)); + + /* + * Print a console message for each attachment + * point. For CMP devices, this means that only + * one message should be printed, no matter how + * many cores are actually present. + */ + curr_cpu = DR_UNUM2SBD_UNUM(up->sbc_cm.sbdev_unum); + if (curr_cpu >= next_cpu) { + cmn_err(CE_CONT, "OS configure %s", + up->sbc_cm.sbdev_path); + next_cpu = curr_cpu + 1; + } + + if (up->sbc_cm.sbdev_state == DR_STATE_UNCONFIGURED) { + /* + * If we're coming from the UNCONFIGURED + * state then the cpu's sigblock will + * still be mapped in. Need to unmap it + * before continuing with attachment. + */ + PR_CPU("%s: unmapping sigblk for cpu %d\n", + f, up->sbc_cpu_id); + + CPU_SGN_MAPOUT(up->sbc_cpu_id); + } + } + + /* + * Block out status threads while creating + * devinfo tree branches + */ + dr_lock_status(hp->h_bd); + mutex_enter(&cpu_lock); + + return (0); +} + +/*ARGSUSED*/ +void +dr_attach_cpu(dr_handle_t *hp, dr_common_unit_t *cp) +{ + sbd_error_t *err; + processorid_t cpuid; + int rv; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + err = drmach_configure(cp->sbdev_id, 0); + if (err) { + DRERR_SET_C(&cp->sbdev_error, &err); + return; + } + + err = drmach_cpu_get_id(cp->sbdev_id, &cpuid); + if (err) { + DRERR_SET_C(&cp->sbdev_error, &err); + + err = drmach_unconfigure(cp->sbdev_id, DRMACH_DEVI_REMOVE); + if (err) + sbd_err_clear(&err); + } else if ((rv = cpu_configure(cpuid)) != 0) { + dr_dev_err(CE_WARN, cp, dr_errno2ecode(rv)); + err = drmach_unconfigure(cp->sbdev_id, + DRMACH_DEVI_REMOVE); + if (err) + sbd_err_clear(&err); + } +} + +/* + * dr_post_attach_cpu + * + * sbd error policy: Does not stop on error. Processes all units in list. + */ +int +dr_post_attach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) +{ + int i; + int errflag = 0; + static fn_t f = "dr_post_attach_cpu"; + + PR_CPU("%s...\n", f); + hp->h_ndi = 0; + + /* Startup and online newly-attached CPUs */ + for (i = 0; i < devnum; i++) { + dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i]; + struct cpu *cp; + + ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up)); + + cp = cpu_get(up->sbc_cpu_id); + if (cp == NULL) { + cmn_err(CE_WARN, "%s: cpu_get failed for cpu %d", + f, up->sbc_cpu_id); + continue; + } + + if (cpu_is_poweredoff(cp)) { + if (cpu_poweron(cp) != 0) { + dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_CPUSTART); + errflag = 1; + } + PR_CPU("%s: cpu %d powered ON\n", f, up->sbc_cpu_id); + } + + if (cpu_is_offline(cp)) { + PR_CPU("%s: onlining cpu %d...\n", f, up->sbc_cpu_id); + + if (cpu_online(cp) != 0) { + dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_ONLINE); + errflag = 1; + } + } + + } + + mutex_exit(&cpu_lock); + dr_unlock_status(hp->h_bd); + + if (errflag) + return (-1); + else + return (0); +} + +/* + * dr_pre_release_cpu + * + * sbd error policy: Stops on first error. + */ +int +dr_pre_release_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) +{ + int c, cix, i, lastoffline = -1, rv = 0; + processorid_t cpuid; + struct cpu *cp; + dr_cpu_unit_t *up; + dr_devset_t devset; + sbd_dev_stat_t *ds; + static fn_t f = "dr_pre_release_cpu"; + int cpu_flags = 0; + + devset = DR_DEVS_PRESENT(hp->h_bd); + + /* allocate status struct storage. */ + ds = (sbd_dev_stat_t *) kmem_zalloc(sizeof (sbd_dev_stat_t) * + MAX_CPU_UNITS_PER_BOARD, KM_SLEEP); + + cix = dr_cpu_status(hp, devset, ds); + + mutex_enter(&cpu_lock); + + for (i = 0; i < devnum; i++) { + up = (dr_cpu_unit_t *)devlist[i]; + ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up)); + + /* + * The STARCAT platform borrows cpus for use by POST in + * iocage testing. These cpus cannot be unconfigured + * while they are in use for the iocage. + * This check determines if a CPU is currently in use + * for iocage testing, and if so, returns a "Device busy" + * error. + */ + for (c = 0; c < cix; c++) { + if (ds[c].d_cpu.cs_unit == up->sbc_cm.sbdev_unum) { + if (ds[c].d_cpu.cs_busy) { + dr_dev_err(CE_WARN, + &up->sbc_cm, ESBD_BUSY); + rv = -1; + break; + } + } + } + if (c < cix) + break; + cpuid = up->sbc_cpu_id; + if ((cp = cpu_get(cpuid)) == NULL) { + dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE); + rv = -1; + break; + } + + /* used by dr_cancel_cpu during error flow */ + up->sbc_cpu_flags = cp->cpu_flags; + + if (CPU_ACTIVE(cp)) { + if (dr_cmd_flags(hp) & SBD_FLAG_FORCE) + cpu_flags = CPU_FORCED; + + PR_CPU("%s: offlining cpu %d\n", f, cpuid); + if (cpu_offline(cp, cpu_flags)) { + PR_CPU("%s: failed to offline cpu %d\n", + f, cpuid); + dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE); + if (disp_bound_threads(cp, 0)) { + cmn_err(CE_WARN, "%s: thread(s) " + "bound to cpu %d", + f, cp->cpu_id); + } + rv = -1; + break; + } else + lastoffline = i; + } + + if (!rv) { + sbd_error_t *err; + + err = drmach_release(up->sbc_cm.sbdev_id); + if (err) { + DRERR_SET_C(&up->sbc_cm.sbdev_error, &err); + rv = -1; + break; + } + } + } + + mutex_exit(&cpu_lock); + + if (rv) { + /* + * Need to unwind others since at this level (pre-release) + * the device state has not yet transitioned and failures + * will prevent us from reaching the "post" release + * function where states are normally transitioned. + */ + for (i = lastoffline; i >= 0; i--) { + up = (dr_cpu_unit_t *)devlist[i]; + (void) dr_cancel_cpu(up); + } + } + + kmem_free(ds, sizeof (sbd_dev_stat_t) * MAX_CPU_UNITS_PER_BOARD); + return (rv); +} + +/* + * dr_pre_detach_cpu + * + * sbd error policy: Stops on first error. + */ +int +dr_pre_detach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) +{ + _NOTE(ARGUNUSED(hp)) + + int i; + int curr_cpu; + int next_cpu; + int cpu_flags = 0; + static fn_t f = "dr_pre_detach_cpu"; + + PR_CPU("%s...\n", f); + + /* + * Block out status threads while destroying devinfo tree + * branches + */ + dr_lock_status(hp->h_bd); + mutex_enter(&cpu_lock); + + for (next_cpu = 0, i = 0; i < devnum; i++) { + dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i]; + struct cpu *cp; + + ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up)); + + cp = cpu_get(up->sbc_cpu_id); + if (cp == NULL) + continue; + + /* + * Print a console message for each attachment + * point. For CMP devices, this means that only + * one message should be printed, no matter how + * many cores are actually present. + */ + curr_cpu = DR_UNUM2SBD_UNUM(up->sbc_cm.sbdev_unum); + if (curr_cpu >= next_cpu) { + cmn_err(CE_CONT, "OS unconfigure %s\n", + up->sbc_cm.sbdev_path); + next_cpu = curr_cpu + 1; + } + + /* + * CPUs were offlined during Release. + */ + if (cpu_is_poweredoff(cp)) { + PR_CPU("%s: cpu %d already powered OFF\n", + f, up->sbc_cpu_id); + continue; + } + + if (!cpu_is_offline(cp)) { + if (dr_cmd_flags(hp) & SBD_FLAG_FORCE) + cpu_flags = CPU_FORCED; + /* cpu was onlined after release. Offline it again */ + PR_CPU("%s: offlining cpu %d\n", f, up->sbc_cpu_id); + if (cpu_offline(cp, cpu_flags)) { + PR_CPU("%s: failed to offline cpu %d\n", + f, up->sbc_cpu_id); + dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE); + if (disp_bound_threads(cp, 0)) { + cmn_err(CE_WARN, "%s: thread(s) " + "bound to cpu %d", + f, cp->cpu_id); + } + goto err; + } + } + if (cpu_poweroff(cp) != 0) { + dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_CPUSTOP); + goto err; + } else { + PR_CPU("%s: cpu %d powered OFF\n", f, up->sbc_cpu_id); + } + } + + return (0); + +err: + mutex_exit(&cpu_lock); + dr_unlock_status(hp->h_bd); + return (-1); +} + +/*ARGSUSED*/ +void +dr_detach_cpu(dr_handle_t *hp, dr_common_unit_t *cp) +{ + sbd_error_t *err; + processorid_t cpuid; + int rv; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + err = drmach_cpu_get_id(cp->sbdev_id, &cpuid); + if (err) { + DRERR_SET_C(&cp->sbdev_error, &err); + } else if ((rv = cpu_unconfigure(cpuid)) != 0) { + dr_dev_err(CE_IGNORE, cp, dr_errno2ecode(rv)); + } else { + err = drmach_unconfigure(cp->sbdev_id, DRMACH_DEVI_REMOVE); + if (err) { + DRERR_SET_C(&cp->sbdev_error, &err); + } + } +} + +/*ARGSUSED1*/ +int +dr_post_detach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) +{ + static fn_t f = "dr_post_detach_cpu"; + + PR_CPU("%s...\n", f); + hp->h_ndi = 0; + + mutex_exit(&cpu_lock); + dr_unlock_status(hp->h_bd); + + return (0); +} + +static void +dr_fill_cpu_stat(dr_cpu_unit_t *cp, drmach_status_t *pstat, sbd_cpu_stat_t *csp) +{ + ASSERT(cp && pstat && csp); + + /* Fill in the common status information */ + bzero((caddr_t)csp, sizeof (*csp)); + csp->cs_type = cp->sbc_cm.sbdev_type; + csp->cs_unit = cp->sbc_cm.sbdev_unum; + strncpy(csp->cs_name, pstat->type, sizeof (csp->cs_name)); + csp->cs_cond = cp->sbc_cm.sbdev_cond; + csp->cs_busy = cp->sbc_cm.sbdev_busy | pstat->busy; + csp->cs_time = cp->sbc_cm.sbdev_time; + csp->cs_ostate = cp->sbc_cm.sbdev_ostate; + csp->cs_suspend = 0; + + /* CPU specific status data */ + csp->cs_cpuid = cp->sbc_cpu_id; + +#ifdef _STARFIRE + csp->cs_isbootproc = (SIGBCPU->cpu_id == cp->sbc_cpu_id) ? 1 : 0; +#endif /* _STARFIRE */ + + /* + * If the speed and ecache properties have not been + * cached yet, read them in from the device tree. + */ + if ((cp->sbc_speed == 0) || (cp->sbc_ecache == 0)) + dr_cpu_set_prop(cp); + + /* use the cached speed and ecache values */ + csp->cs_speed = cp->sbc_speed; + csp->cs_ecache = cp->sbc_ecache; + + mutex_enter(&cpu_lock); + if (!cpu_get(csp->cs_cpuid)) { + /* ostate must be UNCONFIGURED */ + csp->cs_cm.c_ostate = SBD_STAT_UNCONFIGURED; + } + mutex_exit(&cpu_lock); +} + +static void +dr_fill_cmp_stat(sbd_cpu_stat_t *csp, int ncores, int impl, sbd_cmp_stat_t *psp) +{ + int core; + + ASSERT(csp && psp && (ncores >= 1)); + + bzero((caddr_t)psp, sizeof (*psp)); + + /* + * Fill in the common status information based + * on the data for the first core. + */ + psp->ps_type = SBD_COMP_CMP; + psp->ps_unit = DR_UNUM2SBD_UNUM(csp->cs_unit); + strncpy(psp->ps_name, csp->cs_name, sizeof (psp->ps_name)); + psp->ps_cond = csp->cs_cond; + psp->ps_busy = csp->cs_busy; + psp->ps_time = csp->cs_time; + psp->ps_ostate = csp->cs_ostate; + psp->ps_suspend = csp->cs_suspend; + + /* CMP specific status data */ + *psp->ps_cpuid = csp->cs_cpuid; + psp->ps_ncores = 1; + psp->ps_speed = csp->cs_speed; + psp->ps_ecache = csp->cs_ecache; + + /* + * Walk through the data for the remaining cores. + * Make any adjustments to the common status data, + * or the shared CMP specific data if necessary. + */ + for (core = 1; core < ncores; core++) { + + /* + * The following properties should be the same + * for all the cores of the CMP. + */ + ASSERT(psp->ps_unit == DR_UNUM2SBD_UNUM(csp[core].cs_unit)); + ASSERT(psp->ps_speed == csp[core].cs_speed); + + psp->ps_cpuid[core] = csp[core].cs_cpuid; + psp->ps_ncores++; + + /* + * Jaguar has a split ecache, so the ecache + * for each core must be added together to + * get the total ecache for the whole chip. + */ + if (IS_JAGUAR(impl)) { + psp->ps_ecache += csp[core].cs_ecache; + } + + /* adjust time if necessary */ + if (csp[core].cs_time > psp->ps_time) { + psp->ps_time = csp[core].cs_time; + } + + psp->ps_busy |= csp[core].cs_busy; + + /* + * If any of the cores are configured, the + * entire CMP is marked as configured. + */ + if (csp[core].cs_ostate == SBD_STAT_CONFIGURED) { + psp->ps_ostate = csp[core].cs_ostate; + } + } +} + +int +dr_cpu_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp) +{ + int cmp; + int core; + int ncpu; + dr_board_t *bp; + sbd_cpu_stat_t cstat[MAX_CORES_PER_CMP]; + + bp = hp->h_bd; + ncpu = 0; + + devset &= DR_DEVS_PRESENT(bp); + + /* + * Treat every CPU as a CMP. In the case where the + * device is not a CMP, treat it as a CMP with only + * one core. + */ + for (cmp = 0; cmp < MAX_CMP_UNITS_PER_BOARD; cmp++) { + + int ncores; + dr_cpu_unit_t *cp; + drmach_status_t pstat; + sbd_error_t *err; + sbd_cmp_stat_t *psp; + + if ((devset & DEVSET(SBD_COMP_CMP, cmp)) == 0) { + continue; + } + + ncores = 0; + + for (core = 0; core < MAX_CORES_PER_CMP; core++) { + + cp = dr_get_cpu_unit(bp, DR_CMP_CORE_UNUM(cmp, core)); + + if (cp->sbc_cm.sbdev_state == DR_STATE_EMPTY) { + /* present, but not fully initialized */ + continue; + } + + ASSERT(dr_cpu_unit_is_sane(hp->h_bd, cp)); + + /* skip if not present */ + if (cp->sbc_cm.sbdev_id == (drmachid_t)0) { + continue; + } + + /* fetch platform status */ + err = drmach_status(cp->sbc_cm.sbdev_id, &pstat); + if (err) { + DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err); + continue; + } + + dr_fill_cpu_stat(cp, &pstat, &cstat[ncores++]); + } + + if (ncores == 0) { + continue; + } + + /* + * Store the data to the outgoing array. If the + * device is a CMP, combine all the data for the + * cores into a single stat structure. + * + * The check for a CMP device uses the last core + * found, assuming that all cores will have the + * same implementation. + */ + if (CPU_IMPL_IS_CMP(cp->sbc_cpu_impl)) { + psp = (sbd_cmp_stat_t *)dsp; + dr_fill_cmp_stat(cstat, ncores, cp->sbc_cpu_impl, psp); + } else { + ASSERT(ncores == 1); + bcopy(cstat, dsp, sizeof (sbd_cpu_stat_t)); + } + + dsp++; + ncpu++; + } + + return (ncpu); +} + +/* + * Cancel previous release operation for cpu. + * For cpus this means simply bringing cpus that + * were offline back online. Note that they had + * to have been online at the time there were + * released. + */ +int +dr_cancel_cpu(dr_cpu_unit_t *up) +{ + int rv = 0; + static fn_t f = "dr_cancel_cpu"; + + ASSERT(dr_cpu_unit_is_sane(up->sbc_cm.sbdev_bp, up)); + + if (cpu_flagged_active(up->sbc_cpu_flags)) { + struct cpu *cp; + + /* + * CPU had been online, go ahead + * bring it back online. + */ + PR_CPU("%s: bringing cpu %d back ONLINE\n", + f, up->sbc_cpu_id); + + mutex_enter(&cpu_lock); + cp = cpu[up->sbc_cpu_id]; + + if (cpu_is_poweredoff(cp)) { + if (cpu_poweron(cp)) { + cmn_err(CE_WARN, "%s: failed to power-on " + "cpu %d", f, up->sbc_cpu_id); + rv = -1; + } + } + + if (cpu_is_offline(cp)) { + if (cpu_online(cp)) { + cmn_err(CE_WARN, "%s: failed to online cpu %d", + f, up->sbc_cpu_id); + rv = -1; + } + } + + if (cpu_is_online(cp)) { + if (cpu_flagged_nointr(up->sbc_cpu_flags)) { + if (cpu_intr_disable(cp) != 0) { + cmn_err(CE_WARN, "%s: failed to " + "disable interrupts on cpu %d", + f, up->sbc_cpu_id); + } + } + } + + mutex_exit(&cpu_lock); + } + + return (rv); +} + +int +dr_disconnect_cpu(dr_cpu_unit_t *up) +{ + sbd_error_t *err; + static fn_t f = "dr_disconnect_cpu"; + + PR_CPU("%s...\n", f); + + ASSERT((up->sbc_cm.sbdev_state == DR_STATE_CONNECTED) || + (up->sbc_cm.sbdev_state == DR_STATE_UNCONFIGURED)); + + ASSERT(dr_cpu_unit_is_sane(up->sbc_cm.sbdev_bp, up)); + + if (up->sbc_cm.sbdev_state == DR_STATE_CONNECTED) { + /* + * Cpus were never brought in and so are still + * effectively disconnected, so nothing to do here. + */ + PR_CPU("%s: cpu %d never brought in\n", + f, up->sbc_cpu_id); + return (0); + } + + err = drmach_cpu_disconnect(up->sbc_cm.sbdev_id); + if (err == NULL) + return (0); + else { + DRERR_SET_C(&up->sbc_cm.sbdev_error, &err); + return (-1); + } + /*NOTREACHED*/ +} |
