sbd_cpu.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 2005 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/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/ddi_impldefs.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/sbdpriv.h>
void
sbd_cpu_set_prop(sbd_cpu_unit_t *cp, dev_info_t *dip)
{
uint32_t clock_freq;
int ecache_size = 0;
char *cache_str = NULL;
/* read in the CPU speed */
clock_freq = 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 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, "cpu implementation type "
"is an unknown %d value", 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);
}
/*
* In the case the size is still 0,
* a zero value will be displayed running non-debug.
*/
ASSERT(ecache_size != 0);
/* convert to the proper units */
cp->sbc_speed = (clock_freq + 500000) / 1000000;
cp->sbc_ecache = ecache_size / (1024 * 1024);
}
static void
sbd_fill_cpu_stat(sbd_cpu_unit_t *cp, dev_info_t *dip, sbd_cpu_stat_t *csp)
{
int namelen;
bzero((caddr_t)csp, sizeof (*csp));
csp->cs_type = cp->sbc_cm.sbdev_type;
csp->cs_unit = cp->sbc_cm.sbdev_unum;
namelen = sizeof (csp->cs_name);
(void) ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
OBP_DEVICETYPE, (caddr_t)csp->cs_name, &namelen);
csp->cs_busy = cp->sbc_cm.sbdev_busy;
csp->cs_time = cp->sbc_cm.sbdev_time;
csp->cs_ostate = cp->sbc_cm.sbdev_ostate;
csp->cs_cpuid = cp->sbc_cpu_id;
csp->cs_suspend = 0;
/*
* If we have marked the cpu's condition previously
* then don't rewrite it
*/
if (csp->cs_cond != SBD_COND_UNUSABLE)
csp->cs_cond = sbd_get_comp_cond(dip);
/*
* 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))
sbd_cpu_set_prop(cp, dip);
/* use the cached speed and ecache values */
csp->cs_speed = cp->sbc_speed;
csp->cs_ecache = cp->sbc_ecache;
}
static void
sbd_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 = SBD_CMP_NUM(csp->cs_unit);
(void) 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 == SBD_CMP_NUM(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
sbd_cpu_flags(sbd_handle_t *hp, sbd_devset_t devset, sbd_dev_stat_t *dsp)
{
int cmp;
int ncpu;
sbd_board_t *sbp;
sbdp_handle_t *hdp;
sbd_cpu_stat_t cstat[MAX_CORES_PER_CMP];
sbp = SBDH2BD(hp->h_sbd);
hdp = sbd_get_sbdp_handle(sbp, hp);
/*
* Grab the status lock before accessing the dip as we allow
* concurrent status and branch unconfigure and disconnect.
*
* The disconnect thread clears the present devset first
* and then destroys dips. It is possible that the status
* thread checks the present devset before they are cleared
* but accesses the dip after they are destroyed causing a
* panic. To prevent this, the status thread should check
* the present devset and access dips with status lock held.
* Similarly disconnect thread should clear the present devset
* and destroy dips with status lock held.
*/
mutex_enter(&sbp->sb_slock);
/*
* Only look for requested devices that are actually present.
*/
devset &= SBD_DEVS_PRESENT(sbp);
/*
* 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 = ncpu = 0; cmp < MAX_CMP_UNITS_PER_BOARD; cmp++) {
int ncores;
int core;
dev_info_t *dip;
sbd_cpu_unit_t *cp;
sbd_cmp_stat_t *psp;
if (DEVSET_IN_SET(devset, SBD_COMP_CMP, cmp) == 0)
continue;
ncores = 0;
for (core = 0; core < MAX_CORES_PER_CMP; core++) {
int unit;
unit = sbdp_portid_to_cpu_unit(cmp, core);
/*
* Check to make sure the cpu is in a state
* where its fully initialized.
*/
if (SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, unit) ==
SBD_STATE_EMPTY)
continue;
dip = sbp->sb_devlist[NIX(SBD_COMP_CMP)][unit];
if (dip == NULL)
continue;
cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
sbd_fill_cpu_stat(cp, dip, &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;
sbd_fill_cmp_stat(cstat, ncores, cp->sbc_cpu_impl, psp);
} else {
ASSERT(ncores == 1);
bcopy(cstat, dsp, sizeof (sbd_cpu_stat_t));
}
dsp++;
ncpu++;
}
mutex_exit(&sbp->sb_slock);
sbd_release_sbdp_handle(hdp);
return (ncpu);
}
int
sbd_pre_release_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
int i, rv = 0, unit;
dev_info_t *dip;
processorid_t cpuid;
struct cpu *cpup;
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
sbderror_t *ep = SBD_HD2ERR(hp);
sbd_cpu_unit_t *cp;
static fn_t f = "sbd_pre_release_cpu";
sbdp_handle_t *hdp;
hdp = sbd_get_sbdp_handle(sbp, hp);
/*
* May have to juggle bootproc in release_component
*/
mutex_enter(&cpu_lock);
for (i = 0; i < devnum; i++, devlist++) {
dip = devlist->dv_dip;
cpuid = sbdp_get_cpuid(hdp, dip);
if (cpuid < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get cpuid for "
"dip (0x%p)", f, (void *)dip);
continue;
} else {
SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
break;
}
}
unit = sbdp_get_unit_num(hdp, dip);
if (unit < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get unit (cpu %d)",
f, cpuid);
continue;
} else {
SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
break;
}
}
cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
cp->sbc_cpu_flags = cpu[cpuid]->cpu_flags;
if (cpu_flagged_active(cp->sbc_cpu_flags)) {
int cpu_offline_flags = 0;
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE)
cpu_offline_flags = CPU_FORCED;
PR_CPU("%s: offlining cpuid %d unit %d", f,
cpuid, unit);
if (cpu_offline(cpu[cpuid], cpu_offline_flags)) {
cmn_err(CE_WARN,
"%s: failed to offline cpu %d",
f, cpuid);
rv = -1;
SBD_SET_ERR(ep, ESBD_OFFLINE);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
cpup = cpu_get(cpuid);
if (cpup && disp_bound_threads(cpup, 0)) {
cmn_err(CE_WARN, "sbd:%s: thread(s) "
"bound to cpu %d",
f, cpup->cpu_id);
}
break;
}
}
if (rv == 0) {
if (sbdp_release_component(hdp, dip)) {
SBD_GET_PERR(hdp->h_err, ep);
break;
}
}
if (rv)
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 >= 0; i--, devlist--) {
dip = devlist->dv_dip;
unit = sbdp_get_unit_num(hdp, dip);
if (unit < 0) {
cmn_err(CE_WARN,
"sbd:%s: failed to get unit for "
"dip (0x%p)", f, (void *)dip);
break;
}
(void) sbd_cancel_cpu(hp, unit);
}
}
SBD_INJECT_ERR(SBD_OFFLINE_CPU_PSEUDO_ERR,
hp->h_err, EIO,
ESBD_OFFLINE,
sbp->sb_cpupath[devnum - 1]);
sbd_release_sbdp_handle(hdp);
return (rv);
}
int
sbd_pre_attach_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
int i;
int unit;
processorid_t cpuid;
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
sbd_istate_t dstate;
dev_info_t *dip;
static fn_t f = "sbd_pre_attach_cpu";
sbdp_handle_t *hdp;
PR_CPU("%s...\n", f);
hdp = sbd_get_sbdp_handle(sbp, hp);
for (i = 0; i < devnum; i++, devlist++) {
dip = devlist->dv_dip;
ASSERT(sbd_is_cmp_child(dip) || e_ddi_branch_held(dip));
cpuid = sbdp_get_cpuid(hdp, dip);
if (cpuid < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get cpuid for "
"dip (0x%p)", f, (void *)dip);
continue;
} else {
SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
break;
}
}
unit = sbdp_get_unit_num(hdp, dip);
if (unit < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get unit (cpu %d)",
f, cpuid);
continue;
} else {
SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
break;
}
}
PR_CPU("%s: attach cpu-unit (%d.%d)\n",
f, sbp->sb_num, unit);
dstate = SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, unit);
if (dstate == SBD_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, cpuid);
/* platform specific release of sigblk */
CPU_SGN_MAPOUT(cpuid);
}
}
mutex_enter(&cpu_lock);
sbd_release_sbdp_handle(hdp);
return (0);
}
int
sbd_post_attach_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
int i;
sbderror_t *ep = SBD_HD2ERR(hp);
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
processorid_t cpuid;
struct cpu *cp;
dev_info_t *dip;
int err = ESBD_NOERROR;
sbdp_handle_t *hdp;
static fn_t f = "sbd_post_attach_cpu";
sbd_cpu_unit_t *cpup;
int unit;
hdp = sbd_get_sbdp_handle(sbp, hp);
/* Startup and online newly-attached CPUs */
for (i = 0; i < devnum; i++, devlist++) {
dip = devlist->dv_dip;
cpuid = sbdp_get_cpuid(hdp, dip);
if (cpuid < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get cpuid for "
"dip (0x%p)", f, (void *)dip);
continue;
} else {
SBD_GET_PERR(hdp->h_err, ep);
break;
}
}
cp = cpu_get(cpuid);
if (cp == NULL) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: cpu_get failed for cpu %d",
f, cpuid);
continue;
} else {
SBD_SET_ERR(ep, ESBD_INTERNAL);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
break;
}
}
if (cpu_is_poweredoff(cp)) {
if (cpu_poweron(cp) != 0) {
SBD_SET_ERR(ep, ESBD_CPUSTART);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
cmn_err(CE_WARN,
"%s: failed to power-on cpu %d",
f, cpuid);
break;
}
SBD_INJECT_ERR(SBD_POWERON_CPU_PSEUDO_ERR,
ep, EIO,
ESBD_CPUSTOP,
sbp->sb_cpupath[i]);
PR_CPU("%s: cpu %d powered ON\n", f, cpuid);
}
if (cpu_is_offline(cp)) {
PR_CPU("%s: onlining cpu %d...\n", f, cpuid);
if (cpu_online(cp) != 0) {
SBD_SET_ERR(ep, ESBD_ONLINE);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
cmn_err(CE_WARN,
"%s: failed to online cpu %d",
f, cp->cpu_id);
}
SBD_INJECT_ERR(SBD_ONLINE_CPU_PSEUDO_ERR,
ep, EIO,
ESBD_ONLINE,
sbp->sb_cpupath[i]);
}
/*
* if there is no error mark the cpu as OK to use
*/
if (SBD_GET_ERR(ep) == 0) {
unit = sbdp_get_unit_num(hdp, dip);
if (unit < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get unit "
"(cpu %d)", f, cpuid);
continue;
} else {
SBD_GET_PERR(hdp->h_err,
SBD_HD2ERR(hp));
break;
}
}
cpup = SBD_GET_BOARD_CPUUNIT(sbp, unit);
cpup->sbc_cm.sbdev_cond = SBD_COND_OK;
}
}
mutex_exit(&cpu_lock);
sbd_release_sbdp_handle(hdp);
if (err != ESBD_NOERROR) {
return (-1);
} else {
return (0);
}
}
int
sbd_pre_detach_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
int i;
int unit;
processorid_t cpuid;
dev_info_t *dip;
struct cpu *cpu;
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
sbderror_t *ep = SBD_HD2ERR(hp);
static fn_t f = "sbd_pre_detach_cpu";
sbdp_handle_t *hdp;
int rv = 0;
PR_CPU("%s...\n", f);
hdp = sbd_get_sbdp_handle(sbp, hp);
mutex_enter(&cpu_lock);
for (i = 0; i < devnum; i++, devlist++) {
dip = devlist->dv_dip;
cpuid = sbdp_get_cpuid(hdp, dip);
if (cpuid < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get cpuid for "
"dip (0x%p)", f, (void *)dip);
continue;
} else {
SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
break;
}
}
cpu = cpu_get(cpuid);
if (cpu == NULL) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get cpu %d",
f, cpuid);
continue;
} else {
SBD_SET_ERR(ep, ESBD_INTERNAL);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
break;
}
}
unit = sbdp_get_unit_num(hdp, dip);
if (unit < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get unit (cpu %d)",
f, cpuid);
continue;
} else {
SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
break;
}
}
PR_CPU("%s: OS detach cpu-unit (%d.%d)\n",
f, sbp->sb_num, unit);
/*
* CPUs were offlined during Release.
*/
if (cpu_is_poweredoff(cpu)) {
PR_CPU("%s: cpu %d already powered OFF\n", f, cpuid);
continue;
}
if (cpu_is_offline(cpu)) {
int e;
if (e = cpu_poweroff(cpu)) {
cmn_err(CE_WARN,
"%s: failed to power-off cpu %d "
"(errno %d)",
f, cpu->cpu_id, e);
SBD_SET_ERR(ep, ESBD_CPUSTOP);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
rv = -1;
break;
} else {
PR_CPU("%s: cpu %d powered OFF\n",
f, cpuid);
}
} else {
cmn_err(CE_WARN, "%s: cpu %d still active",
f, cpu->cpu_id);
SBD_SET_ERR(ep, ESBD_BUSY);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
rv = -1;
break;
}
}
sbd_release_sbdp_handle(hdp);
return (rv);
}
int
sbd_post_detach_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
static fn_t f = "sbd_post_detach_cpu";
int i;
sbderror_t *ep = SBD_HD2ERR(hp);
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
processorid_t cpuid;
dev_info_t *dip;
sbdp_handle_t *hdp;
sbd_cpu_unit_t *cpup;
int unit;
PR_CPU("%s...\n", f);
/*
* We should be holding the cpu_lock at this point,
* and should have blocked device tree changes.
*/
ASSERT(MUTEX_HELD(&cpu_lock));
for (i = 0; i < devnum; i++, devlist++) {
dip = devlist->dv_dip;
hdp = sbd_get_sbdp_handle(sbp, hp);
cpuid = sbdp_get_cpuid(hdp, dip);
if (cpuid < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get cpuid for "
"dip (0x%p)", f, (void *)dip);
continue;
} else {
SBD_GET_PERR(hdp->h_err, ep);
break;
}
}
/*
* if there is no error mark the cpu as unusable
*/
if (SBD_GET_ERR(ep) == 0) {
unit = sbdp_get_unit_num(hdp, dip);
if (unit < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
cmn_err(CE_WARN,
"sbd:%s: failed to get unit "
"(cpu %d)", f, cpuid);
continue;
} else {
SBD_GET_PERR(hdp->h_err,
SBD_HD2ERR(hp));
break;
}
}
cpup = SBD_GET_BOARD_CPUUNIT(sbp, unit);
cpup->sbc_cm.sbdev_cond = SBD_COND_UNUSABLE;
}
sbd_release_sbdp_handle(hdp);
}
mutex_exit(&cpu_lock);
return (0);
}
/*
* 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 they were released. If attempting to power on or online
* a CPU fails, SBD_CPUERR_FATAL is returned to indicate that the CPU appears to
* be unsalvageable. If a CPU reaches an online or nointr state but can't be
* taken to a "lesser" state, SBD_CPUERR_RECOVERABLE is returned to indicate
* that it was not returned to its original state but appears to be functional.
* Note that the latter case can occur due to unexpected but non-erroneous CPU
* manipulation (e.g. by the "psradm" command) during the DR operation.
*/
int
sbd_cancel_cpu(sbd_handle_t *hp, int unit)
{
int rv = SBD_CPUERR_NONE;
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
sbderror_t *ep = SBD_HD2ERR(hp);
sbd_cpu_unit_t *cp;
static fn_t f = "sbd_cancel_cpu";
struct cpu *cpup;
int cpu_offline_flags = 0;
PR_ALL("%s...\n", f);
cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
/*
* If CPU should remain off, nothing needs to be done.
*/
if (cpu_flagged_poweredoff(cp->sbc_cpu_flags))
return (rv);
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE)
cpu_offline_flags = CPU_FORCED;
/*
* CPU had been either offline, online, or set to no-intr. We
* will return a component to its original state that it was
* prior to the failed DR operation. There is a possible race
* condition between the calls to this function and re-obtaining
* the cpu_lock where a cpu state could change. Because of this
* we can't externally document that we are trying to roll cpus
* back to their original state, but we believe a best effort
* should be made.
*/
mutex_enter(&cpu_lock);
cpup = cpu[cp->sbc_cpu_id];
/*
* The following will compare the cpu's current state with a
* snapshot of its state taken before the failed DR operation
* had started.
*/
/* POWEROFF */
if (cpu_is_poweredoff(cpup)) {
if (cpu_poweron(cpup)) {
cmn_err(CE_WARN,
"sbd:%s: failed to power-on cpu %d",
f, cp->sbc_cpu_id);
SBD_SET_ERR(ep, ESBD_CPUSTART);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[unit]);
rv = SBD_CPUERR_FATAL;
goto out;
}
SBD_INJECT_ERR(SBD_POWERON_CPU_PSEUDO_ERR,
hp->h_err, EIO,
ESBD_CPUSTART,
sbp->sb_cpupath[unit]);
}
/* OFFLINE */
if (cpu_is_offline(cpup)) {
if (cpu_flagged_offline(cp->sbc_cpu_flags)) {
PR_CPU("%s: leaving cpu %d OFFLINE\n",
f, cp->sbc_cpu_id);
} else if (cpu_online(cpup)) {
cmn_err(CE_WARN,
"sbd:%s: failed to online cpu %d",
f, cp->sbc_cpu_id);
SBD_SET_ERR(ep, ESBD_ONLINE);
SBD_SET_ERRSTR(ep, sbp->sb_cpupath[unit]);
rv = SBD_CPUERR_FATAL;
goto out;
} else {
SBD_INJECT_ERR(SBD_ONLINE_CPU_PSEUDO_ERR,
hp->h_err, EIO,
ESBD_ONLINE,
sbp->sb_cpupath[unit]);
}
}
/* ONLINE */
if (cpu_is_online(cpup)) {
if (cpu_flagged_online(cp->sbc_cpu_flags)) {
PR_CPU("%s: setting cpu %d ONLINE\n",
f, cp->sbc_cpu_id);
} else if (cpu_flagged_offline(cp->sbc_cpu_flags)) {
if (cpu_offline(cpup, cpu_offline_flags)) {
cmn_err(CE_WARN,
"sbd:%s: failed to offline"
" cpu %d", f, cp->sbc_cpu_id);
rv = SBD_CPUERR_RECOVERABLE;
goto out;
}
} else if (cpu_flagged_nointr(cp->sbc_cpu_flags)) {
if (cpu_intr_disable(cpup)) {
cmn_err(CE_WARN, "%s: failed to "
"disable interrupts on cpu %d",
f, cp->sbc_cpu_id);
rv = SBD_CPUERR_RECOVERABLE;
} else {
PR_CPU("%s: setting cpu %d to NOINTR"
" (was online)\n",
f, cp->sbc_cpu_id);
}
goto out;
}
}
/* NOINTR */
if (cpu_is_nointr(cpup)) {
if (cpu_flagged_online(cp->sbc_cpu_flags)) {
cpu_intr_enable(cpup);
PR_CPU("%s: setting cpu %d ONLINE"
"(was nointr)\n",
f, cp->sbc_cpu_id);
}
if (cpu_flagged_offline(cp->sbc_cpu_flags)) {
if (cpu_offline(cpup, cpu_offline_flags)) {
cmn_err(CE_WARN,
"sbd:%s: failed to offline"
" cpu %d", f, cp->sbc_cpu_id);
rv = SBD_CPUERR_RECOVERABLE;
}
}
}
out:
mutex_exit(&cpu_lock);
return (rv);
}
int
sbd_connect_cpu(sbd_board_t *sbp, int unit)
{
int rv;
processorid_t cpuid;
struct cpu *cpu;
dev_info_t *dip;
sbdp_handle_t *hdp;
extern kmutex_t cpu_lock;
static fn_t f = "sbd_connect_cpu";
sbd_handle_t *hp = MACHBD2HD(sbp);
/*
* get dip for cpu just located in tree walk
*/
if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_CPU, unit)) {
dip = sbp->sb_devlist[NIX(SBD_COMP_CPU)][unit];
if (dip == NULL) {
cmn_err(CE_WARN,
"sbd:%s: bad dip for cpu unit %d board %d",
f, unit, sbp->sb_num);
return (-1);
}
PR_CPU("%s...\n", f);
} else {
return (0);
}
/*
* if sbd has attached this cpu, no need to bring
* it out of reset
*/
if (SBD_DEV_IS_ATTACHED(sbp, SBD_COMP_CPU, unit)) {
return (0);
}
hdp = sbd_get_sbdp_handle(sbp, hp);
cpuid = sbdp_get_cpuid(hdp, dip);
if (cpuid == -1) {
sbd_release_sbdp_handle(hdp);
return (-1);
}
/*
* if the cpu is already under Solaris control,
* do not wake it up
*/
mutex_enter(&cpu_lock);
cpu = cpu_get(cpuid);
mutex_exit(&cpu_lock);
if (cpu != NULL) {
sbd_release_sbdp_handle(hdp);
return (0);
}
rv = sbdp_connect_cpu(hdp, dip, cpuid);
if (rv != 0) {
sbp->sb_memaccess_ok = 0;
cmn_err(CE_WARN,
"sbd:%s: failed to wake up cpu unit %d board %d",
f, unit, sbp->sb_num);
sbd_release_sbdp_handle(hdp);
return (rv);
}
sbd_release_sbdp_handle(hdp);
return (rv);
}
int
sbd_disconnect_cpu(sbd_handle_t *hp, int unit)
{
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
int rv;
dev_info_t *dip;
sbdp_handle_t *hdp;
sbd_cpu_unit_t *cp;
processorid_t cpuid;
static fn_t f = "sbd_disconnect_cpu";
PR_CPU("%s...\n", f);
ASSERT((SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, unit) ==
SBD_STATE_CONNECTED) ||
(SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, unit) ==
SBD_STATE_UNCONFIGURED));
cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
cpuid = cp->sbc_cpu_id;
dip = sbp->sb_devlist[NIX(SBD_COMP_CPU)][unit];
hdp = sbd_get_sbdp_handle(sbp, hp);
rv = sbdp_disconnect_cpu(hdp, dip, cpuid);
if (rv != 0) {
SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
}
sbd_release_sbdp_handle(hdp);
return (rv);
}