diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/sun4u/starfire/os/bbus_intr.c | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/uts/sun4u/starfire/os/bbus_intr.c')
| -rw-r--r-- | usr/src/uts/sun4u/starfire/os/bbus_intr.c | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/starfire/os/bbus_intr.c b/usr/src/uts/sun4u/starfire/os/bbus_intr.c new file mode 100644 index 0000000000..6e4110a6d4 --- /dev/null +++ b/usr/src/uts/sun4u/starfire/os/bbus_intr.c @@ -0,0 +1,643 @@ +/* + * 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" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/kmem.h> +#include <sys/cpu_sgnblk_defs.h> +#include <vm/seg.h> +#include <sys/iommu.h> +#include <sys/vtrace.h> +#include <sys/intreg.h> +#include <sys/ivintr.h> +#include <sys/cpuvar.h> +#include <sys/systm.h> +#include <sys/machsystm.h> +#include <sys/cyclic.h> +#include <sys/cpu_sgn.h> + +extern cpu_sgnblk_t *cpu_sgnblkp[NCPU]; +extern struct cpu *SIGBCPU; +extern void power_down(const char *); + +uint_t bbus_intr_inum; +uint_t bbus_poll_inum; + +/* + * Support for sgnblk polling. + */ + +/* Internal function prototypes */ +static void sgnblk_poll_init(); +static uint_t bbus_poll(caddr_t arg1, caddr_t arg2); +static void sgnblk_poll_handler(void *unused); +#ifdef THROTTLE +static void sgnblk_poll_throttle(uint64_t interval); +#endif /* THROTTLE */ + +/* Default sgnblk polling interval is every 5 seconds. */ +#define ONE_SECOND (1000000) /* in usecs */ +#ifdef THROTTLE +#define SGNBLK_POLL_INTERVAL (5 * ONE_SECOND) +#define SGNBLK_POLL_FAST (ONE_SECOND >> 1) +#define SGNBLK_POLL_FAST_WIN ((60 * ONE_SECOND) / \ + SGNBLK_POLL_FAST) +#else /* THROTTLE */ +/* + * Until we can find a way to throttle back to 0.5 second intervals + * we're stuck fixed on 2.5 second intervals. + */ +#define SGNBLK_POLL_INTERVAL ((2 * ONE_SECOND) + (ONE_SECOND >> 1)) +#endif /* THROTTLE */ + +#define MAX_SGNBLK_POLL_CLNT 5 + +void (*pollclntfunc[MAX_SGNBLK_POLL_CLNT])(); +/* + * sgnblk_mutex Protects juggling & sgnblk_poll_refs[]. + * sgnblk_poll_mutex Protects pollclntfunc[]. + */ +kmutex_t sgnblk_mutex; +kmutex_t sgnblk_poll_mutex; +static uint64_t sgnblk_poll_interval = SGNBLK_POLL_INTERVAL; +#ifdef THROTTLE +static uint64_t sgnblk_poll_fast = SGNBLK_POLL_FAST; +static int64_t sgnblk_poll_fast_win = SGNBLK_POLL_FAST_WIN; +#endif /* THROTTLE */ +static processorid_t sgnblk_pollcpu = -1; +/* + * Note that the sigblock polling depends on CY_HIGH_LEVEL + * being higher than PIL_13 since we ultimately need to + * dispatch a PIL_13 soft handler. + * Also, we assume one sgnblk handler for the entire system. + * Once upon a time we had them per-cpu. With the Cyclic stuff + * we would have to bind our cyclic handler to a cpu and doing + * this prevents that cpu from being offlined. Since the Cyclic + * subsystem could indirectly juggle us without us knowing we + * have to assume we're running from any possible cpu and not + * always SIGBCPU. + */ +#ifdef THROTTLE +static cyclic_id_t sgnblk_poll_cycid = CYCLIC_NONE; +#endif /* THROTTLE */ +static cyc_handler_t sgnblk_poll_cychandler = { + sgnblk_poll_handler, + NULL, + CY_HIGH_LEVEL +}; +static cyc_time_t sgnblk_poll_time; + +/* + * Anybody that references the polling (SIGBCPU) can + * register a callback function that will be called if + * the polling cpu is juggled, e.g. during a DR operation. + */ +#define MAX_SGNBLK_POLL_REFS 10 + +struct sgnblk_poll_refs { + void (*callback)(cpu_sgnblk_t *sigbp, void *arg); + void *arg; +} sgnblk_poll_refs[MAX_SGNBLK_POLL_REFS]; + +/* + * Bootbus intr handler: Generic handler for all SSP/CBS + * interrupt requests initiated via the hw bootbus intr + * mechanism. This is similar to the level15 + * interrupt handling for sigb commands in the CS6400. + * Most of these code were stolen from the sigb stuff in + * in CS6400. + */ + +extern struct cpu cpu0; + +/*ARGSUSED*/ +static uint_t +bbus_intr(caddr_t arg) +{ + int cmd = 0; + processorid_t cpu_id = CPU->cpu_id; + int retflag; + int resp = 0; + proc_t *initpp; + + ASSERT(cpu_sgnblkp[cpu_id] != NULL); + + /* + * Check for unsolicited messages in the host's mailbox. + */ + retflag = cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag; + + switch (retflag) { + case CBS_TO_HOST: + retflag = HOST_TO_CBS; + break; + default: + retflag = SIGB_MBOX_EMPTY; + break; + } + if (retflag == SIGB_MBOX_EMPTY) + return (0); /* interrupt not claimed */ + + /* + * We only look for UNSOLICITED messages, i.e. commands. + * Responses to these commands are returned into the same + * mailbox from which the command was received, i.e. host's. + * + * If the host should solicit a message from the SSP, that + * message/command goes into the SSP's mailbox (sigb_ssp_mbox). + * The responses (from the SSP) to these messages will be + * read from the ssp mailbox by whomever solicited it, but + * will NOT be handled through this level 15 interrupt + * mechanism. + * + * Note that use of the flag field of the signature block mailbox + * structure and the mailbox protocol itself, serializes access + * to these mailboxes. + */ + + resp = 0; + + /* + * The first sizeof (uint_t) bytes of the data field + * is the command. + */ + cmd = cpu_sgnblkp[cpu_id]->sigb_host_mbox.cmd; + + switch (cmd) { + case SSP_GOTO_OBP: + /* + * Let's set the mailbox flag to BUSY while we are in OBP + */ + cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_BUSY; + + debug_enter("SSP requested (SSP_GOTO_OBP)"); + /* + * This command does NOT require a response. + */ + resp = 0; + break; + + case SSP_GOTO_PANIC: + /* + * Let's reset the mailbox flag before we bail. + */ + cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_EMPTY; + + cmn_err(CE_PANIC, "SSP requested (SSP_GOTO_PANIC)\n"); + /* should never reach this point */ + + resp = 0; + break; + case SSP_ENVIRON: + /* + * Environmental Interrupt. + */ + + /* + * Send SIGPWR to init(1) it will run rc0, which will uadmin to + * powerdown. + */ + + mutex_enter(&pidlock); + initpp = prfind(P_INITPID); + mutex_exit(&pidlock); + + /* + * If we're still booting and init(1) isn't set up yet, + * simply halt. + */ + if (initpp == NULL) { + extern void halt(char *); + cmn_err(CE_WARN, "?Environmental Interrupt"); + power_down((char *)NULL); + halt("Power off the System!\n"); /* just in case */ + } + + /* + * else, graceful shutdown with inittab and all getting involved + * + * XXX: Do we Need to modify the init process for the Cray 6400! + */ + psignal(initpp, SIGPWR); + + /* + * XXX: kick off a sanity timeout panic in case the /etc/inittab + * or /etc/rc0 files are hosed. The 6400 needs to hang here + * when we return from psignal. + * + * cmn_err(CE_PANIC, "SSP requested (SSP_ENVIRON)\n"); + * should never reach this point + */ + + resp = 0; + break; + /* + * Could handle more mailbox commands right here. + */ + + default: + resp = SIGB_BAD_MBOX_CMD; + break; + } + + /* + * If resp is non-zero then we'll automatically reset + * the handler_sigb lock once we've sent the response, + * however if no response is needed, then resetlck must + * be set so that the handler_sigb lock is reset. + */ + if (resp != 0) { + /* + * Had some kind of trouble handling the mailbox + * command. Need to send back an error response + * and back out of the cpu_sgnblk handling. + */ + cpu_sgnblkp[cpu_id]->sigb_host_mbox.cmd = resp; + bcopy((caddr_t)&cmd, + (caddr_t)&cpu_sgnblkp[cpu_id]->sigb_host_mbox.data[0], + sizeof (cmd)); + cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = retflag; + } else { + /* + * No response expected, but we still have to + * reset the flag to empty for the next person. + */ + cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_EMPTY; + } + return (1); /* interrupt claimed */ +} + +void +register_bbus_intr() +{ + /* + * Starfire's ASIC have the capability to generate a mondo + * vector. The SSP uses this capability via the Boot Bus to + * send an interrupt to a domain. + * + * The SSP generates a mondo with: + * ign = UPAID_TO_IGN(bootcpu_upaid) + * ino = 0 + * + * An interrupt handler is added for this inum. + */ + bbus_intr_inum = UPAID_TO_IGN(cpu0.cpu_id) * MAX_INO; + VERIFY(add_ivintr(bbus_intr_inum, PIL_13, bbus_intr, 0, 0) == 0); + + + /* + * Due to a HW flaw in starfire, liberal use + * of bootbus intrs under heavy system load + * may cause the machine to arbstop. The workaround + * is to provide a polling mechanism thru the signature + * block interface to allow another way for the SSP to + * interrupt the host. Applications like IDN which generate + * a high degree of SSP to host interruptions for + * synchronization will need to use the polling facility + * instead of the hw bootbus interrupt mechanism. + * The HW bootbus intr support is left intact as it + * will still be used by existing SSP applications for system + * recovery in the event of system hangs etc.. In such situations, + * HW bootbus intr is a better mechanism as it is HW generated + * level 15 interrupt that has a better chance of kicking + * a otherwise hung OS into recovery. + * + * Polling is done by scheduling a constant tick timer + * interrupt at a certain predefined interval. + * The handler will do a poll and if there is a + * "intr" request, scheduled a soft level 13 intr + * to handle it. Allocate the inum for the level + * 13 intr here. + */ + bbus_poll_inum = add_softintr(PIL_13, bbus_poll, 0); +} + +static void +sgnblk_poll_init() +{ + ASSERT(MUTEX_HELD(&sgnblk_mutex)); + + mutex_init(&sgnblk_poll_mutex, NULL, + MUTEX_SPIN, (void *)ipltospl(PIL_14)); + sgnblk_pollcpu = SIGBCPU->cpu_id; + mutex_enter(&cpu_lock); + sgnblk_poll_time.cyt_when = 0ull; + sgnblk_poll_time.cyt_interval = sgnblk_poll_interval * 1000ull; +#ifdef THROTTLE + sgnblk_poll_cycid = cyclic_add(&sgnblk_poll_cychandler, + &sgnblk_poll_time); +#else /* THROTTLE */ + (void) cyclic_add(&sgnblk_poll_cychandler, &sgnblk_poll_time); +#endif /* THROTTLE */ + mutex_exit(&cpu_lock); + ASSERT(sgnblk_pollcpu == SIGBCPU->cpu_id); +} + +int +sgnblk_poll_register(void(*func)(processorid_t cpu_id, + cpu_sgnblk_t *cpu_sgnblkp)) +{ + int i; + + /* + * See if we need to initialize + * sgnblk polling + */ + mutex_enter(&sgnblk_mutex); + if (sgnblk_pollcpu == -1) + sgnblk_poll_init(); + mutex_exit(&sgnblk_mutex); + + mutex_enter(&sgnblk_poll_mutex); + + /* + * Look for a empty slot + */ + for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) { + if (pollclntfunc[i] == NULL) { + pollclntfunc[i] = func; + mutex_exit(&sgnblk_poll_mutex); + return (1); + } + } + mutex_exit(&sgnblk_poll_mutex); + return (0); /* failed */ +} + +int +sgnblk_poll_unregister(void(*func)(processorid_t cpu_id, + cpu_sgnblk_t *cpu_sgnblkp)) +{ + int i; + + mutex_enter(&sgnblk_poll_mutex); + + /* + * Look for the slot matching the function passed in. + */ + for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) { + if (pollclntfunc[i] == func) { + pollclntfunc[i] = NULL; + mutex_exit(&sgnblk_poll_mutex); + return (1); + } + } + mutex_exit(&sgnblk_poll_mutex); + return (0); /* failed */ +} + + +/* + * For DR support. + * Juggle poll tick client to another cpu + * Assumed to be called single threaded. + */ +void +juggle_sgnblk_poll(struct cpu *cp) +{ + int i; + + mutex_enter(&sgnblk_mutex); + + if (sgnblk_pollcpu == -1 || + (cp != NULL && sgnblk_pollcpu == cp->cpu_id)) { + mutex_exit(&sgnblk_mutex); + return; + } + + /* + * Disable by simply returning here + * Passing a null cp is assumed to be + * sgnpoll disable request. + */ + if (cp == NULL) { + for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) { + void (*func)(), *arg; + + if ((func = sgnblk_poll_refs[i].callback) != NULL) { + arg = sgnblk_poll_refs[i].arg; + (*func)(NULL, arg); + } + } + mutex_exit(&sgnblk_mutex); + return; + } + + sgnblk_pollcpu = cp->cpu_id; + + for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) { + void (*func)(), *arg; + + if ((func = sgnblk_poll_refs[i].callback) != NULL) { + arg = sgnblk_poll_refs[i].arg; + (*func)(cpu_sgnblkp[sgnblk_pollcpu], arg); + } + } + + mutex_exit(&sgnblk_mutex); +} + +#ifdef THROTTLE +/*ARGSUSED0*/ +static void +_sgnblk_poll_throttle(void *unused) +{ + mutex_enter(&cpu_lock); + if (sgnblk_poll_cycid != CYCLIC_NONE) { + cyclic_remove(sgnblk_poll_cycid); + sgnblk_poll_cycid = CYCLIC_NONE; + } + + if (sgnblk_poll_time.cyt_interval > 0ull) + sgnblk_poll_cycid = cyclic_add(&sgnblk_poll_cychandler, + &sgnblk_poll_time); + mutex_exit(&cpu_lock); +} + +/* + * We don't want to remove the cyclic within the context of + * the handler so we kick off the throttle in background + * via a timeout call. + */ +static void +sgnblk_poll_throttle(uint64_t new_interval) +{ + mutex_enter(&cpu_lock); + sgnblk_poll_time.cyt_when = 0ull; + sgnblk_poll_time.cyt_interval = new_interval * 1000ull; + mutex_exit(&cpu_lock); + + (void) timeout(_sgnblk_poll_throttle, NULL, (clock_t)0); +} +#endif /* THROTTLE */ + +/* + * High priority interrupt handler (PIL_14) + * for signature block mbox polling. + */ +/*ARGSUSED0*/ +static void +sgnblk_poll_handler(void *unused) +{ + processorid_t cpuid = SIGBCPU->cpu_id; +#ifdef THROTTLE + static int64_t sb_window = -1; + static uint64_t sb_interval = 0; +#endif /* THROTTLE */ + + if (cpu_sgnblkp[cpuid] == NULL) + return; + + /* + * Poll for SSP requests + */ + if (cpu_sgnblkp[cpuid]->sigb_host_mbox.intr == SIGB_INTR_SEND) { + /* reset the flag - sure hope this is atomic */ + cpu_sgnblkp[cpuid]->sigb_host_mbox.intr = SIGB_INTR_OFF; + +#ifdef THROTTLE + /* + * Go into fast poll mode for a short duration + * (SGNBLK_POLL_FAST_WIN) in SGNBLK_POLL_FAST interval. + * The assumption here is that we just got activity + * on the mbox poll, the probability of more coming down + * the pipe is high - so let's look more often. + */ + if ((sb_window < 0) && (sb_interval > sgnblk_poll_fast)) { + sb_interval = sgnblk_poll_fast; + sgnblk_poll_throttle(sb_interval); + } + sb_window = sgnblk_poll_fast_win; +#endif /* THROTTLE */ + + /* schedule poll processing */ + setsoftint(bbus_poll_inum); + +#ifdef THROTTLE + } else if (sb_window >= 0) { + /* Revert to slow polling once fast window ends */ + if ((--sb_window < 0) && + (sb_interval < sgnblk_poll_interval)) { + sb_interval = sgnblk_poll_interval; + sgnblk_poll_throttle(sb_interval); + } +#endif /* THROTTLE */ + } +} + +/*ARGSUSED*/ +static uint_t +bbus_poll(caddr_t arg1, caddr_t arg2) +{ + int i; + processorid_t cpu_id = SIGBCPU->cpu_id; + cpu_sgnblk_t *sgnblkp = cpu_sgnblkp[cpu_id]; + + /* + * Go thru the poll client array and call the + * poll client functions one by one + */ + mutex_enter(&sgnblk_poll_mutex); + + for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) { + void (*func)(processorid_t cpuid, cpu_sgnblk_t *sgnblkp); + + if ((func = pollclntfunc[i]) != NULL) { + mutex_exit(&sgnblk_poll_mutex); + (*func)(cpu_id, sgnblkp); + mutex_enter(&sgnblk_poll_mutex); + } + } + mutex_exit(&sgnblk_poll_mutex); + + return (1); +} + +int +sgnblk_poll_reference(void (*callback)(cpu_sgnblk_t *sigb, void *arg), + void *arg) +{ + int i, slot; + cpu_sgnblk_t *sigbp; + + if (callback == NULL) + return (-1); + + mutex_enter(&sgnblk_mutex); + /* + * First verify caller is not already registered. + */ + slot = -1; + for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) { + if ((slot == -1) && (sgnblk_poll_refs[i].callback == NULL)) { + slot = i; + continue; + } + if (sgnblk_poll_refs[i].callback == callback) { + mutex_exit(&sgnblk_mutex); + return (-1); + } + } + /* + * Now find an empty entry. + */ + if (slot == -1) { + mutex_exit(&sgnblk_mutex); + return (-1); + } + sgnblk_poll_refs[slot].callback = callback; + sgnblk_poll_refs[slot].arg = arg; + + sigbp = (sgnblk_pollcpu != -1) ? cpu_sgnblkp[sgnblk_pollcpu] : NULL; + + (*callback)(sigbp, arg); + + mutex_exit(&sgnblk_mutex); + + return (0); +} + +void +sgnblk_poll_unreference(void (*callback)(cpu_sgnblk_t *sigb, void *arg)) +{ + int i; + + mutex_enter(&sgnblk_mutex); + for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) { + if (sgnblk_poll_refs[i].callback == callback) { + void *arg; + + arg = sgnblk_poll_refs[i].arg; + (*callback)(NULL, arg); + sgnblk_poll_refs[i].callback = NULL; + sgnblk_poll_refs[i].arg = NULL; + break; + } + } + mutex_exit(&sgnblk_mutex); +} |
