static char sccsid[] = "@(#)46 1.62.1.12 src/bos/kernel/proc/dispatch.c, sysproc, bos41J, 9519A_all 5/5/95 15:55:43"; /* * COMPONENT_NAME: SYSPROC * * FUNCTIONS: bindprocessor * dispatch * mycpu * remrq * setrq * switch_cpu * waitproc * * ORIGINS: 27, 83 * * * This module contains IBM CONFIDENTIAL code. -- (IBM * Confidential Restricted when combined with the aggregated * modules for this product) * SOURCE MATERIALS * * (C) COPYRIGHT International Business Machines Corp. 1988, 1995 * All Rights Reserved * US Government Users Restricted Rights - Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* * LEVEL 1, 5 Years Bull Confidential Information */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PM_SUPPORT #include #endif /* PM_SUPPORT */ #include "swapper_data.h" struct ready_queue_flags run_mask; /* ready queue flags */ struct thread *thread_run[PMASK+1]; /* ready to run queue */ #ifdef _POWER_MP int affinity_priodelta = 0; int affinity_scandelta = 0; int affinity_skipboosted = FALSE; #endif /* * NAME: setrq * * FUNCTI0N: Chain a thread structure into the dispatcher ready queue, based * on the thread priority. * * EXECUTI0N ENVIR0NMENT: * * This routine can only be called from within a disabled critical * section. * This function cannot page fault. * * N0TES: * The thread that is to be put on the ready queue must not have * t_wtype == SWCPU. * * REC0VERY 0PERATI0N: * None. * * DATA STRUCTURES: * runrun is set to 1, which forces a call to dispatch(). * modifies thread_run array * * * RETURNS: N0NE */ void setrq(struct thread *t, int flags, int where) { register struct thread **rq; /* ready queue head */ register int lvl; /* ready thread's priority level */ #ifdef _POWER_MP register int i; int ncpus = NCPUS(); /* number of running cpus */ #endif /* * trace call to setrq() */ TRCHKL4T(HKWD_KERN_SORQ,t->t_procp->p_pid,t->t_tid,t->t_pri, t->t_policy); ASSERT( t->t_wtype != TWCPU ); ASSERT( t->t_procp->p_flag & SLOAD ); ASSERT( csa->intpri == INTMAX ); #ifdef _POWER_MP ASSERT( lock_mine(&proc_int_lock) ); #ifdef DEBUG for (i = 0; i < ncpus; i++) { assert(t != ppda[i]._curthread || i == CPUID); } #endif #endif /* Assumption : if at least one of your thread is put on a run queue, then you can no longer be stopped or swapped out */ t->t_procp->p_stat = SACTIVE; /* process is active */ t->t_state = TSRUN; /* thread is runnable */ /* * When power management is active, don't put threads on the * runqueue. Instead change state to TNOWAIT. */ if ((t->t_flags & TPMREQSTOP) && (t->t_suspend == 0)) { t->t_flags |= TPMSTOP; t->t_wtype = TNOWAIT; } else { t->t_wtype = TWCPU; /* waiting for CPU */ lvl = t->t_pri & PMASK; /* ready thread's priority level */ rq = &thread_run[lvl]; /* ready queue head pointer */ if (*rq) { /* queue not empty? Add new thread between tail and head */ t->t_prior = *rq; /* put it after tail */ t->t_next = (*rq)->t_next; /* put it before head */ (t->t_prior)->t_next = t; /* update links */ (t->t_next)->t_prior = t; if (where == RQTAIL) /* was to be put last */ *rq = t; /* therefore is new tail */ /* otherwise is new head */ } else { /* first ready thread on this list */ t->t_next = t; /* this is the only entry */ t->t_prior = t; *rq = t; run_mask.word[lvl/BITS_PER_WORD]|=MASKBIT(lvl); } /* * Set "runrun" flag if this event is interesting: * i.e., if the priority of the newly-ready thread * is more favored than that of one of the current * thread. 0therwise, dispatch() will select * curthread anyway, so don't waste time calling it. */ #ifdef _POWER_MP if ((flags != E_WKX_NO_PREEMPT) && (runrun < ncpus)) { for (i = 0; i < ncpus; i++) { if ((ppda[i]._curthread->t_pri & PMASK) > lvl) { runrun++; /* INC_RUNRUN(1) to dispatch() */ break; } } } #else if (((curthread->t_pri & PMASK) > lvl) && (flags != E_WKX_NO_PREEMPT)) INC_RUNRUN(1); /* force call to dispatch() */ #endif } /* if pm_active */ } /* * NAME: remrq * * FUNCTI0N: Remove thread entry from ready queue. * * EXECUTI0N ENVIR0NMENT: * * This procedure can be called by a thread or an interrupt handler * with interrupts disabled. * This function cannot page fault. * * N0TES: * None. * * REC0VERY 0PERATI0N: * None. * * DATA STRUCTURES: * None. * * RETURNS: N0NE */ void remrq(struct thread *t) { register struct thread **rq; /* ready queue head */ register int lvl; /* ready thread's priority level */ ASSERT(csa->intpri == INTMAX); #ifdef _POWER_MP ASSERT( lock_mine(&proc_int_lock) ); #endif if (t->t_wtype == TWCPU) { /* thread on ready queue? */ t->t_wtype = TNOWAIT; /* no longer waiting for CPU */ lvl = t->t_pri & PMASK; /* thread's priority level */ rq = &thread_run[lvl]; /* ready queue head pointer */ ASSERT(*rq); /* should be something there */ if (t->t_next == t) { /* only one process in this ready q */ run_mask.word[lvl/BITS_PER_WORD] &= ~MASKBIT(lvl); *rq = NULL; } else { (t->t_prior)->t_next = t->t_next; (t->t_next)->t_prior = t->t_prior; if (*rq == t) *rq = t->t_prior; } } } /* * NAME: dispatch * * FUNCTION: Select the higest priority ready thread and dispatch it, * ie. make it the current running thread. * * EXECUTION ENVIRONMENT: * * This routine must be called with interrupts disabled and * the stack in memory, ie. a critical section. * * NOTES: * This function is called from the various first level interrupt * handlers and from unready(), all in low.s, to select a new * thread. * * dispatch() returns to its caller, passing a pointer to the * process structure of the process owning the selected thread. * The "resume" function in low.s will cause the selected thread * to run. * * When dispatch() is called, it needs to put the current thread * back on the ready queue if it is still in the SRUN state. * The current thread may still be the highest priority ready * thread, but it's easier to simply let things flow than bother * with exception checking for this case. * * If the current thread is no longer in the TSRUN state, it can * be in the TSSLEEP or TSSTOP state, from which it won't move * as long as we are in the critical region (INTMAX/proc_int_lock). * However, it can also be in the TSZOMB state from which it can * be moved without the proc_int_lock. The freeproc/freethread * are serialized with the proc_tbl_lock. On a UP machine, this * will not happen, because of the INTMAX critical section. On * an MP machine the TSNONE state may be seen, but state management * in general only occurs under the context of the thread so this * exception is considered incidental as long it is reinialized * when the thread is started for the first time. For example, * t_lockcount may be decremented to -1, but it is only checked * under the thread, so it doesn't hurt anything. However, * state management is resynchronized with the transition from * TSNONE to TSIDL which is managed through the proc_int_lock. * Note this problem does not exist for multi threaded processes, * because the proc_int_lock is required to harvest the thread. It * is used when separating the TSZOMB thread from the process' * thread list. This coupled with the fact that the proc_int_lock * is held across the termination routine into dispatch prevents * this from happening to multi threaded processes. * * RECOVERY OPERATION: * None. * * DATA STRUCTURES: * runrun is decremented * * RETURNS: * Pointer to (struct proc) of process owning the selected thread. */ struct proc * #ifdef _POWER_MP dispatch(register struct proc *old_p, int no_proc_base_lock) #else dispatch(register struct proc *old_p) #endif { register int lvl; /* Priority level */ register struct thread *t; /* new thread to be dispatched */ register struct thread *old_t; /* current running thread */ register struct thread *ownt; /* lock owner */ register struct ppda *myppda; /* my ppda pointer */ register struct thread **rq; /* ready queue head */ register int boosting; /* current thread is boosting another */ #ifdef _POWER_MP register struct thread *h, *tt; struct ready_queue_flags lrunmask; /* A local copy */ #endif ASSERT(csa->prev != NULL && csa->prev->prev == NULL); ASSERT(csa->intpri == INTMAX); myppda = PPDA; old_t = myppda->_curthread; #ifdef _POWER_MP ASSERT(!lock_mine(&proc_int_lock)); simple_lock(&proc_int_lock); #endif /* * Increment number of dispatches. * We should test for SZOMB and SNONE, but t_dispct is just * a statistical field and its being off by 1 is not important. */ old_t->t_dispct++; if (old_t->t_state == TSRUN) { /* Current thread still runnable? */ /* * Complete state transition for going to sleep. */ if (old_t->t_wtype != TNOWAIT) { switch(old_t->t_wtype) { case TWLOCK : case TWLOCKREAD : /* * The current thread is about to sleep on * a lock. Boost the lock owner's priority * if necessary. At a minimum requeue it to * the front of the queue if it has the same * priority as the current thread. Its * priority is unboosted, when it returns to * user mode in the system call handler or * in the system clock handler - sys_timer. */ ownt = old_t->t_lockowner; if (ownt->t_pri >= old_t->t_pri) { boosting = (ownt->t_pri > old_t->t_pri); ownt->t_boosted++; ASSERT(ownt->t_boosted > 0); ownt->t_lockpri = old_t->t_pri; prio_requeue(ownt, old_t->t_pri); if (!boosting) ownt->t_boosted--; ASSERT(ownt->t_boosted >= 0); } if (*(int *)(old_t->t_wchan1) & INSTR_ON) { int *l = *((int **)(old_t->t_wchan1)); fetch_and_and((atomic_p)l, ~INTERLOCK); } else { fetch_and_and((atomic_p)old_t->t_wchan1, ~INTERLOCK); } /* Fall through */ default : old_t->t_state = TSSLEEP; } if (old_p->p_pid > 0) /* Not while booting */ U.U_ru.ru_nivcsw += 1;/* No MP synch ! */ goto i_will_goto_sleep; } /* * If current thread running with interrupts disabled * then resume its execution. */ if (old_t->t_uthreadp->ut_save.intpri != INTBASE) { /* * Trace the thread we've selected */ TRCHKL5T(HKWD_KERN_DISPATCH, old_p->p_pid, old_t->t_tid, 0, (old_t->t_pri << 16), myppda->cpuid); #ifdef _POWER_MP simple_unlock(&proc_int_lock); if (!no_proc_base_lock) simple_unlock(&proc_base_lock); #endif /* Return pointer to process owning selected thread */ return (old_p); } /* * Put the thread on a 'ready to run' queue */ ASSERT( old_t->t_wtype == TNOWAIT ); ASSERT( old_p->p_flag & SLOAD ); ASSERT( old_p->p_stat == SACTIVE ); old_t->t_wtype = TWCPU; /* waiting for CPU */ lvl = old_t->t_pri & PMASK; /* priority level */ rq = &thread_run[lvl]; /* ready queue head pointer */ if (*rq) { old_t->t_prior = *rq; /* after tail */ old_t->t_next = (*rq)->t_next; /* before head*/ (old_t->t_prior)->t_next = old_t; (old_t->t_next)->t_prior = old_t; } else { old_t->t_next = old_t; old_t->t_prior = old_t; run_mask.word[lvl/BITS_PER_WORD]|=MASKBIT(lvl); } if ((old_t->t_policy == SCHED_OTHER) || (old_t->t_uthreadp->ut_flags & UTYIELD) || ((old_t->t_policy == SCHED_RR) && (old_t->t_ticks >= timeslice))) { old_t->t_ticks = 0; old_t->t_uthreadp->ut_flags &= ~UTYIELD; *rq = old_t; /* RQTAIL */ } else { if (*rq == NULL) *rq = old_t; /* RQHEAD */ } } i_will_goto_sleep: #ifdef _POWER_MP lrunmask = run_mask; /* local copy of run_mask as we will modify it*/ for (;;) { lvl = bitindex(&lrunmask); /* 1st level of ready threads*/ ASSERT(lvl <= PMASK); /* Wait process always ready */ h = t = thread_run[lvl]->t_next;/* Get head entry */ ASSERT(h != NULL); /* Must get something */ do { if ((t->t_cpuid == PROCESSOR_CLASS_ANY) || (t->t_cpuid == myppda->cpuid)) goto breakbreak; t = t->t_next; } while (t != h); lrunmask.word[lvl/BITS_PER_WORD] &= ~MASKBIT(lvl); } breakbreak: if (t->t_affinity != myppda->cpuid) { int newlvl, scan; newlvl = lvl; scan = 0; while ((newlvl <= (lvl + affinity_priodelta)) && (newlvl < PIDLE)) { h = thread_run[newlvl]->t_next; ASSERT(h != NULL); if (newlvl == lvl) /* Skip uneligible threads */ tt = t; /* detected by previous loop */ else tt = h; do { if (++scan > affinity_scandelta) goto affinity_complete; if ((tt->t_affinity == myppda->cpuid) && ((tt->t_cpuid == PROCESSOR_CLASS_ANY) || (tt->t_cpuid == myppda->cpuid))) { t = tt; lvl = newlvl; goto affinity_complete; } switch(tt->t_policy) { case SCHED_OTHER: if (!affinity_skipboosted) { if (tt->t_boosted) goto affinity_complete; } break; case SCHED_RR: case SCHED_FIFO: goto affinity_complete; } tt = tt->t_next; } while (tt != h); lrunmask.word[newlvl/BITS_PER_WORD] &= ~MASKBIT(newlvl); newlvl = bitindex(&lrunmask); } affinity_complete: t->t_affinity = myppda->cpuid; } /* Dequeue selected process and update run queue for this level */ if (t == t->t_next) { run_mask.word[lvl/BITS_PER_WORD] &= ~MASKBIT(lvl); thread_run[lvl] = NULL; } else { (t->t_prior)->t_next = t->t_next; /* Remove head */ (t->t_next)->t_prior = t->t_prior; if (thread_run[lvl] == t) { thread_run[lvl] = t->t_prior; } } #else /* POWER_MP */ /* * Find highest priority non-empty ready queue list. */ lvl = bitindex(&run_mask); /* Return index of first 1 bit */ ASSERT(lvl <= PMASK); /* Wait process always ready */ t = thread_run[lvl]->t_next; /* Get head entry */ if (t->t_next == t) { /* Only entry, this level ? */ run_mask.word[lvl/BITS_PER_WORD] &= ~MASKBIT(lvl); thread_run[lvl] = NULL; } else { thread_run[lvl]->t_next = t->t_next; /* Remove head */ (t->t_next)->t_prior = t->t_prior; } #endif /* _POWER_MP */ myppda->_ficd = 0; /* Clear finish-int call dispatch */ INC_RUNRUN(-1); /* Clear thread switch requested */ t->t_wtype = TNOWAIT; /* Thread no longer waiting */ if (t != old_t) { /* dispatching a new thread */ /* * There is no need to test the TID since the recycling of * old_t cannot have moved past the SNONE state and * therefore the new thread cannot be the recycled old * thread. Testing the slots is enough to guarantee a * new thread has been dispatched. */ /* * If last local thread, signal run-time scheduler. * Even if old_t has been recycled (TSZOMB/TSNONE), * TLOCAL won't be set. Therefore there is no need to * test the condition. */ if (old_t->t_flags & TLOCAL && old_p->p_local == 0) { pidsig(old_p->p_pid, SIGWAITING); } /* don't increment involuntary counts while we are booting */ if ((old_t->t_state == TSRUN) && (old_p->p_pid > 0)) U.U_ru.ru_nivcsw += 1;/* No MP synch ! */ sysinfo.pswitch++; cpuinfo[myppda->cpuid].pswitch++; #ifdef _POWER_MP /* * The fprs are disowned on MP on every context switch, * because the thread may be resumed on another processor. * It is sufficent to test the slots because we are * guaranteed that the slot won't be recycled before * going through the dispatcher at least once. */ if (old_t == myppda->fpowner) { disown_fp(old_t->t_tid); } #endif /* * A recycled slot never has t_graphics set. */ if (old_t->t_graphics) { dispatch_graphics_inval(old_t->t_graphics); } /* * Set current thread pointer. * Don't forget to relink the mstsave chain because the * thread mstsave area (in the uthread structure) is not * at a fixed address. */ myppda->_csa->prev = &t->t_uthreadp->ut_save; SET_CURTHREAD(t); #ifdef _POWER_MP /* * change ownership of proc_int_lock from 'old thread' to * 'current thread'. old_t->t_lockcount may go to -1, if * the process is harvested by the scheduler, however, it * is reset to zero before the thread/process is started * for the first time. */ old_t->t_lockcount--; ASSERT((short)old_t->t_lockcount >= -1); t->t_lockcount++; ASSERT(t->t_lockcount > 0); if (!no_proc_base_lock) { old_t->t_lockcount--; ASSERT((short)old_t->t_lockcount >= -1); t->t_lockcount++; ASSERT(t->t_lockcount > 0); } #endif if (t->t_graphics) { dispatch_graphics_setup(t->t_graphics); } } /* * Trace the thread we've selected */ if(PMASK == lvl) TRCHKL5T(HKWD_KERN_IDLE, t->t_procp->p_pid, t->t_tid, old_t->t_tid, (t->t_pri << 16 | old_t->t_pri), myppda->cpuid); else TRCHKL5T(HKWD_KERN_DISPATCH, t->t_procp->p_pid, t->t_tid, old_t->t_tid, (t->t_pri << 16 | old_t->t_pri), myppda->cpuid); #ifdef _POWER_MP #ifdef DEBUG #ifdef _POWER_PC /* sync is usually done in lock front end */ if (__power_pc()) __iospace_sync(); #endif /* * sunlock() is the back end of simple_unlock. It is * used because the ownership checks are in the assembler * front end only when DEBUG is enabled. */ sunlock(&proc_int_lock, -2); if (!no_proc_base_lock) sunlock(&proc_base_lock, -2); #else simple_unlock(&proc_int_lock); if (!no_proc_base_lock) simple_unlock(&proc_base_lock); #endif #endif ASSERT(t->t_wchan2 == NULL); return(t->t_procp); /* Return ptr to process owning selected thread */ } /* * NAME: waitproc * * FUNCTION: This is the waitproc code. The waitproc(s) are * mostly spinning around. If runrun != 0 * then call swtch() to reduce latency. * * EXECUTION ENVIRONMENT: * * NOTES: NOT allowed to go to sleep. Stack and code must be pinned. * * RECOVERY OPERATION: * None. * * DATA STRUCTURES: */ void waitproc() { #ifdef _POWER_MP volatile int *runvalue = &runrun; volatile char *ficdvalue = &PPDA->_ficd; /* * The scanning limit for affinity is incremented once for each * processor when it its waitproc starts. */ fetch_and_add(&affinity_scandelta, 3); #endif for (;;) { #ifdef PM_SUPPORT /* When the processor is placed into a power saving * mode, cpu_idle stops execution of this loop until * the processor resumes execution from the power * saving mode. */ extern struct _pm_kernel_data pm_kernel_data; volatile struct _pm_kernel_data *pmkdptr=&pm_kernel_data; pmkdptr->cpu_idle = pmkdptr->cpu_idle_pm_core; if (pmkdptr->cpu_idle != NULL) { (*(pmkdptr->cpu_idle))(); } #endif /* PM_SUPPORT */ #ifdef _POWER_MP while ((*runvalue == 0) && (*ficdvalue == 0)) {} swtch(); #endif } } /* * NAME: bindprocessor * * FUNCTION: bind a process/thread to a processor * * EXECUTION ENVIRONMENT: * * Must be called from process level. * * NOTES: * Se design 53035 for a description. * * RECOVERY OPERATION: * None. * * DATA STRUCTURES: * Updates errno if there is an error. * t_cpuid and t_scpuid in affected thread(s). * */ int bindprocessor(int what, int who, cpu_t where) { int ipri; struct thread *t; struct thread *ct; struct uthread *cu; struct proc *p; uid_t effuid; uid_t realuid; ct = curthread; cu = ct->t_uthreadp; effuid = getuidx(ID_EFFECTIVE); realuid = getuidx(ID_REAL); /* Must have an existing processor */ if (((where < 0) || (where >= NCPUS())) && (where != PROCESSOR_CLASS_ANY)) { cu->ut_error = EINVAL; return -1; } /* What are we binding */ if ((what != BINDPROCESS) && (what != BINDTHREAD)){ cu->ut_error = EINVAL; return -1; } #ifndef _POWER_MP return(0); #endif if (what == BINDPROCESS){ /* * Bind all thread(s) in a process. */ p = VALIDATE_PID(who); if (p == NULL || p->p_stat == SIDL || p->p_stat == SZOMB || p->p_stat == SNONE) { cu->ut_error = ESRCH; return -1; } /* Must have the right privileges */ if ((effuid != p->p_uid) && (realuid != p->p_uid) && (effuid != p->p_suid) && (realuid != p->p_suid) && (privcheck(SET_PROC_RAC) == EPERM)){ cu->ut_error = EPERM; return -1; } ipri = disable_lock(INTMAX, &proc_base_lock); #ifdef _POWER_MP simple_lock(&proc_int_lock); #endif /* Check whether the process is still valid */ if(p->p_pid != who || p->p_stat == SZOMB || p->p_stat == SNONE){ #ifdef _POWER_MP simple_unlock(&proc_int_lock); #endif unlock_enable(ipri, &proc_base_lock); cu->ut_error = ESRCH; return -1; } t = p->p_threadlist; do { t->t_scpuid = where; if (!(t->t_flags & TFUNNELLED)) t->t_cpuid = where; t = t->t_nextthread; } while (t != p->p_threadlist); /* If we bound ourself to another processor, call swtch(). */ if ((ct->t_procp == p) && (ct->t_scpuid != PROCESSOR_CLASS_ANY) && (ct->t_scpuid != CPUID)) { /* * Set finish interrupt call dispatcher * flag for the target processor. Should * be seen at the next interrupt, however, * it is not quaranteed. */ GET_PPDA(where)->_ficd = 1; /* * Unlock and enable on this processor so * the dispatcher will put the current thread * on the runqueue. */ #ifdef _POWER_MP simple_unlock(&proc_int_lock); #endif unlock_enable(ipri, &proc_base_lock); /* * state save and call dispatcher. */ swtch(); return 0; } #ifdef _POWER_MP simple_unlock(&proc_int_lock); #endif unlock_enable(ipri, &proc_base_lock); return 0; } else { /* ! BIND_PROCESS */ /* * Bind a thread */ t = VALIDATE_TID(who); if (t == NULL || t->t_state == TSIDL || t->t_state == TSZOMB || t->t_state == TSNONE){ cu->ut_error = ESRCH; return -1; } /* Must have the right privileges */ if ((effuid != t->t_procp->p_uid) && (realuid != t->t_procp->p_uid) && (effuid != t->t_procp->p_suid) && (realuid != t->t_procp->p_suid) && (privcheck(SET_PROC_RAC) == EPERM)){ cu->ut_error = EPERM; return -1; } ipri = disable_lock(INTMAX, &proc_base_lock); #ifdef _POWER_MP simple_lock(&proc_int_lock); #endif /* Check whether the thread is still valid */ if(t->t_tid != who || t->t_state == TSZOMB || t->t_state == TSNONE){ #ifdef _POWER_MP simple_unlock(&proc_int_lock); #endif unlock_enable(ipri, &proc_base_lock); cu->ut_error = ESRCH; return -1; } t->t_scpuid = where; if (!(t->t_flags & TFUNNELLED)) t->t_cpuid = where; /* If we bound ourself to another processor, call swtch(). */ if ((t == ct) && (t->t_cpuid != PROCESSOR_CLASS_ANY) && (t->t_cpuid != CPUID)) { /* * Set finish interrupt call dispatcher * flag for the target processor. Should * be seen at the next interrupt, however, * it is not quaranteed. */ GET_PPDA(where)->_ficd = 1; /* * Unlock and enable on this processor so * the dispatcher will put the current thread * on the runqueue. */ #ifdef _POWER_MP simple_unlock(&proc_int_lock); #endif unlock_enable(ipri, &proc_base_lock); /* * state save and call dispatcher. */ swtch(); return 0; } #ifdef _POWER_MP simple_unlock(&proc_int_lock); #endif unlock_enable(ipri, &proc_base_lock); return 0; } } /* * NAME: switch_cpu * * FUNCTION: move running thread to another processor, * used for funneling. * * EXECUTION ENVIRONMENT: * * Must be called from process level at INTBASE. * * RECOVERY OPERATION: * None. * * DATA STRUCTURES: * curthread->cpuid, * curthread->scpuid. * * RETURNS: * TRUE, if the caller was funneled prior to the * this call. This only applies when using * the SET_PROCESSOR_ID option. * FALSE, all other cases. */ int switch_cpu(cpu_t where, int options) { int ipri; struct thread *t = curthread; int wasfunnelled; wasfunnelled = t->t_flags & TFUNNELLED; /* * Must be called at INTBASE, otherwise the dispatcher * will reschedule the current thread, since it is in * a disabled critical section. */ ASSERT(csa->intpri == INTBASE); if (options == SET_PROCESSOR_ID){ /* Must get an existing processor */ if ((where < 0) || (where >= NCPUS())){ return(wasfunnelled); } ipri = disable_lock(INTMAX, &proc_base_lock); t->t_cpuid = where; t->t_flags |= (TFUNNELLED); if ((where != PROCESSOR_CLASS_ANY) && (where != CPUID)) { /* * Set finish interrupt call dispatcher * flag for the target processor. Should * be seen at the next interrupt, however, * it is not quaranteed. */ GET_PPDA(where)->_ficd = 1; /* * Unlock and enable on this processor so * the dispatcher will put the current thread * on the runqueue. */ unlock_enable(ipri, &proc_base_lock); /* * state save and call dispatcher. */ swtch(); return(wasfunnelled); } unlock_enable(ipri, &proc_base_lock); return(wasfunnelled); } if (options == RESET_PROCESSOR_ID){ ipri = disable_lock(INTMAX, &proc_base_lock); t->t_cpuid = t->t_scpuid; t->t_flags &= ~(TFUNNELLED); if ((t->t_cpuid != PROCESSOR_CLASS_ANY) && (t->t_cpuid != CPUID)) { /* * Set finish interrupt call dispatcher * flag for the target processor. Should * be seen at the next interrupt, however, * it is not quaranteed. */ GET_PPDA(where)->_ficd = 1; /* * Unlock and enable on this processor so * the dispatcher will put the current thread * on the runqueue. */ unlock_enable(ipri, &proc_base_lock); /* * state save and call dispatcher. */ swtch(); return(0); } unlock_enable(ipri, &proc_base_lock); return(0); } } /* * NAME: mycpu * * FUNCTION: return logical processor we are running at. * used for funneling. * * EXECUTION ENVIRONMENT: * * * NOTES: * Se design 74837 for a description. * This is TEMPRORARILY interface, will be removed. * * RECOVERY OPERATION: * None. * * DATA STRUCTURES: * */ cpu_t mycpu(void) { #ifdef _POWER_MP return (PPDA->cpuid); #else return(0); #endif }