/* * 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 #include #include #include #include #include #include #include /* * Handle software interrupts through 'softcall' mechanism * * At present softcall mechanism uses a global list headed by softhead. * Entries are added to tail and removed from head so as to preserve FIFO * nature of entries in the softcall list. softcall() takes care of adding * entries to the softtail. * * softint must take care of executing the entries in the FIFO * order. It could be called simultaneously from multiple cpus, however only * one instance of softint should process the softcall list, this is * ensured by * - the state the variable softcall_state will be at time to time. * (IDLE->PEND->DRAIN->IDLE) * * These states are needed for softcall mechanism since Solaris has only * one interface(ie. siron ) as of now for * - raising a soft interrupt architecture independently(ie not through * setsoftint(..) ) * - to process the softcall queue. */ #define NSOFTCALLS 200 /* * Defined states for softcall processing. */ #define SOFT_IDLE 0x01 /* no processing is needed */ #define SOFT_PEND 0x02 /* softcall list needs processing */ #define SOFT_DRAIN 0x04 /* the list is being processed */ typedef struct softcall { void (*sc_func)(void *); /* function to call */ void *sc_arg; /* arg to pass to func */ struct softcall *sc_next; /* next in list */ } softcall_t; static softcall_t softcalls[NSOFTCALLS], *softhead, *softtail, *softfree; static uint_t softcall_state; /* * protects softcall lists and control variable softcall_state. */ static kmutex_t softcall_lock; static void (*kdi_softcall_func)(void); extern void siron(void); void softcall_init(void) { softcall_t *sc; for (sc = softcalls; sc < &softcalls[NSOFTCALLS]; sc++) { sc->sc_next = softfree; softfree = sc; } mutex_init(&softcall_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8)); } /* * Call function func with argument arg * at some later time at software interrupt priority */ void softcall(void (*func)(void *), void *arg) { softcall_t *sc; /* * protect against cross-calls */ mutex_enter(&softcall_lock); /* coalesce identical softcalls */ for (sc = softhead; sc != 0; sc = sc->sc_next) { if (sc->sc_func == func && sc->sc_arg == arg) { mutex_exit(&softcall_lock); return; } } if ((sc = softfree) == 0) panic("too many softcalls"); softfree = sc->sc_next; sc->sc_func = func; sc->sc_arg = arg; sc->sc_next = 0; if (softhead) { softtail->sc_next = sc; softtail = sc; mutex_exit(&softcall_lock); } else { softhead = softtail = sc; if (softcall_state == SOFT_DRAIN) /* * softint is already running; no need to * raise a siron. Due to lock protection of * softhead / softcall state, we know * that softint() will see the new addition to * the softhead queue. */ mutex_exit(&softcall_lock); else { softcall_state = SOFT_PEND; mutex_exit(&softcall_lock); siron(); } } } void kdi_softcall(void (*func)(void)) { kdi_softcall_func = func; if (softhead == NULL) siron(); } /* * Called to process software interrupts take one off queue, call it, * repeat. * * Note queue may change during call; softcall_lock and state variables * softcall_state ensures that * -we don't have multiple cpus pulling from the list (thus causing * a violation of FIFO order). * -we don't miss a new entry having been added to the head. * -we don't miss a wakeup. */ void softint(void) { softcall_t *sc; void (*func)(); caddr_t arg; /* * Check if we are asked to process the softcall list. */ mutex_enter(&softcall_lock); if (softcall_state != SOFT_PEND) { mutex_exit(&softcall_lock); goto out; } softcall_state = SOFT_DRAIN; for (;;) { if ((sc = softhead) != NULL) { func = sc->sc_func; arg = sc->sc_arg; softhead = sc->sc_next; sc->sc_next = softfree; softfree = sc; } if (sc == NULL) { softcall_state = SOFT_IDLE; mutex_exit(&softcall_lock); break; } mutex_exit(&softcall_lock); func(arg); mutex_enter(&softcall_lock); } out: if ((func = kdi_softcall_func) != NULL) { kdi_softcall_func = NULL; func(); } }