📄 semsmlib.c
字号:
/* pending task is remote */ else { /* * Add the shared TCB that was pended on the semaphore * pend queue to the list of events to send to the * CPU where the pended task is located. */ eventList.head = (SM_DL_NODE *) htonl (LOC_TO_GLOB_ADRS (firstSmTcb)); eventList.tail = eventList.head; /* * Unlink the shared TCB from other shared TCBs of the semaphore * pend queue. */ firstSmTcb->qNode.next = NULL; /* notify remote CPU */ CACHE_PIPE_FLUSH (); /* CACHE FLUSH [SPR 68334] */ temp = firstSmTcb->ownerCpu; /* PCI bridge bug [SPR 68844]*/ if (smObjEventSend (&eventList, ntohl (firstSmTcb->ownerCpu)) != OK) { windExit (); /* EXIT KERNEL */ return (ERROR); } } windExit (); /* EXIT KERNEL */ return (OK); }/******************************************************************************* semSmTake - take shared memory binary or counting semaphore** Takes a shared semaphore. If the semaphore is empty, i.e., it has not been* given since the last semTake() or semInit(), this task will become pended* until the semaphore becomes available by some other local or remote task* doing a semGive() of it or if the optional <timeout> value is not NO_WAIT,* in which case the task will pend for no more than <timeout> system clock* ticks. If <timeout> is WAIT_FOREVER , the task may never return if a remote* task has the semaphore and crashes.** For binary semaphores, if the semaphore is already available, this* call will empty the semaphore, so that no other task can take it* until this task gives it back, and this task will continue running.** For counting semaphores, if the semaphore count is already greater* than 0, this call will decrement the semaphore count, and this task* will continue running.** The <smSemId> passed to this routine must be the local semaphore address.* * This routine is usually called by semTake() which first converts the* semaphore ID to the shared semaphore local address.** WARNING* This routine may not be used from interrupt level.** RETURNS: OK or ERROR.** ERRNO: S_objLib_OBJ_UNAVAILABLE, S_intLib_NOT_ISR_CALLABLE,* S_objLib_OBJ_ID_ERROR, S_objLib_OBJ_TIMEOUT, S_smObjLib_LOCK_TIMEOUT,* S_smMemLib_NOT_ENOUGH_MEMORY** NOMANUAL** INTERNAL* When semSmTake is called for the first time or after a timeout or a* RESTART signal occuring while this task was blocked on a shared* semaphore, a shared TCB is created in shared memory.* This is done by allocating a shared TCB structure* from a shared partition dedicated to shared TCBs. A pointer to this* shared TCB is then initialized in the local Task Control Block.*/STATUS semSmTake ( SM_SEM_ID smSemId, /* global semaphore ID to take */ int timeout /* timeout in ticks */ ) { Q_FIFO_G_HEAD pendQ; /* global FIFO to pend on */ int level; /* processor specific inLock return value */ int status; /* returned status */ WIND_TCB * pTcb; /* current task tcb pointer */ SM_SEM_ID volatile smSemIdv = (SM_SEM_ID volatile) smSemId; int temp; /* temp storage */ if (INT_RESTRICT () != OK) /* not ISR callable */ { return (ERROR); }again: CACHE_PIPE_FLUSH (); /* CACHE FLUSH [SPR 68334] */ temp = smSemIdv->verify; /* PCI bridge bug [SPR 68844]*/ if (SM_OBJ_VERIFY (smSemIdv) != OK) /* check semaphore */ { return (ERROR); } /* * If it's the first call to semSmBTake, or if a timeout or a * RESTART signal has occured while this task was blocked on a shared * semaphore, the pSmObjTcb field of the TCB is NULL, we allocate * and initialize a shared TCB. */ pTcb = (WIND_TCB *) taskIdCurrent; if (pTcb->pSmObjTcb == NULL) { if (smObjTcbInit () != OK) { return (ERROR); } } /* ENTER LOCKED SECTION */ if (SM_OBJ_LOCK_TAKE (&smSemId->lock, &level) != OK) { smObjTimeoutLogMsg ("semTake", (char *) &smSemId->lock); return (ERROR); /* can't take lock */ } CACHE_PIPE_FLUSH (); /* CACHE FLUSH [SPR 68334] */ temp = smSemIdv->objType; /* PCI bridge bug [SPR 68844]*/ if (ntohl (smSemIdv->objType) == SEM_TYPE_SM_BINARY) /* binary semaphore */ { if (ntohl (smSemIdv->state.flag) == SEM_FULL) { smSemIdv->state.flag = htonl (SEM_EMPTY); /* set sem unavailable */ SM_OBJ_LOCK_GIVE (&smSemId->lock, level); /* EXIT LOCKED SECTION */ return (OK); } } else /* counting samaphore */ { if (ntohl (smSemId->state.count) > 0) { /* decrement semaphore count */ smSemId->state.count = htonl (ntohl (smSemId->state.count) - 1); SM_OBJ_LOCK_GIVE(&smSemId->lock, level); /* EXIT LOCKED SECTION */ return (OK); } } kernelState = TRUE; /* ENTER KERNEL */ if (timeout == NO_WAIT) /* NO_WAIT = no block */ { SM_OBJ_LOCK_GIVE (&smSemId->lock, level); /* EXIT LOCKED SECTION */ errno = S_objLib_OBJ_UNAVAILABLE; /* resource gone */ windExit (); /* KERNEL EXIT */ return (ERROR); } /* * We get here if the semaphore is not available and we must wait. * The calling task must be added to the shared semaphore * queue using standard wind kernel functions. For that we * initialize a pseudo multi-way queue header that points to * the shared semaphore pend queue and which will be used * by the multi-way queue manipulation macros. */ /* * We optimize interrupt latency by doing only the Q_PUT on the * shared semaphore pend Q while interrupts are locked. * The remaining part of blocking the task is done in * windReadyQRemove() with interrupts unlocked. */ pendQ.pLock = NULL; /* we already have the lock */ pendQ.pFifoQ = &smSemId->smPendQ; /* address of actual queue */ pendQ.pQClass = qFifoGClassId; /* global fifo multi way Q */ Q_PUT (&pendQ, taskIdCurrent, taskIdCurrent->priority); SM_OBJ_LOCK_GIVE (&smSemId->lock, level); /* EXIT LOCKED SECTION */ pendQ.pLock = &smSemId->lock; /* now we don't have lock */ /* windview - level 2 event logging */ EVT_TASK_1 (EVENT_OBJ_SEMTAKE, smSemId); windReadyQRemove ((Q_HEAD *) &pendQ, timeout); /* block task */ if ((status = windExit ()) == RESTART) /* KERNEL EXIT */ { timeout = SIG_TIMEOUT_RECALC (timeout); goto again; } return (status); }/******************************************************************************* semSmFlush - flush shared binary or counting semaphore** Flush the shared semaphore. If one or more tasks have already taken* the semaphore (so that it is now pended waiting for it), those tasks* will now become ready to run. If a local higher priority task was* pending on the semaphore it will preempt the task that does the semFlush().* If the semaphore pend queue is empty, this call is essentially a no-op.* The smSemId passed to this routine must be the local semaphore address.** This routine is usually called by semFlush() which first converts the* semaphore ID to the shared semaphore local address.** WARNING* This routine may not be used from interrupt level.** RETURNS: OK or ERROR.** ERRNO: S_intLib_NOT_ISR_CALLABLE, S_objLib_OBJ_ID_ERROR,* S_smObjLib_LOCK_TIMEOUT** NOMANUAL*/STATUS semSmFlush ( SM_SEM_ID smSemId /* global semaphore ID to flush */ ) { SM_OBJ_TCB volatile * firstSmTcb; /* first pending shared memory TCB */ SM_OBJ_TCB volatile * pSmObjTcb; /* useful shared TCB pointer */ SM_OBJ_TCB volatile * pSmObjTcbTmp; /* useful shared TCB pointer */ int level; /* CPU specific inLock return value */ int cpuNum; /* loop counter for remote flush */ SM_DL_LIST flushQ [SM_OBJ_MAX_CPU]; /* per CPU flush Q */ SM_SEM_ID volatile smSemIdv = (SM_SEM_ID volatile) smSemId; int temp; /* temp storage */ if (INT_RESTRICT () != OK) /* not ISR callable */ { return (ERROR); } CACHE_PIPE_FLUSH (); /* CACHE FLUSH [SPR 68334] */ temp = smSemIdv->verify; /* PCI bridge bug [SPR 68844]*/ if (SM_OBJ_VERIFY (smSemIdv) != OK) /* check semaphore */ { return (ERROR); } /* ENTER LOCKED SECTION */ if (SM_OBJ_LOCK_TAKE (&smSemId->lock, &level) != OK) { smObjTimeoutLogMsg ("semFlush", (char *) &smSemId->lock); return (ERROR); /* can't take lock */ } /* * In order to reduce interrupt latency we get the first * shared TCB from the shared semaphore pend queue, then * we empty the pend queue by clearing the pend queue head. * All the previously pended shared TCBs are still linked * to the first pending TCB and we can give back lock access * to the shared semaphore. */ firstSmTcb = (SM_OBJ_TCB volatile *) SM_DL_FIRST (&smSemId->smPendQ); if (firstSmTcb == (SM_OBJ_TCB volatile *) LOC_NULL) /* pendQ is empty */ { SM_OBJ_LOCK_GIVE (&smSemId->lock, level);/* EXIT LOCKED SECTION */ return (OK); } smSemIdv->smPendQ.head = NULL; /* empty semaphore pend queue */ smSemIdv->smPendQ.tail = NULL; SM_OBJ_LOCK_GIVE (&smSemId->lock, level); /* EXIT LOCKED SECTION */ /* create a list of pending task per cpu */ bzero ((char *) flushQ, sizeof (flushQ)); /* clear flush Q table */ pSmObjTcb = firstSmTcb; do { /* get next shared TCB behind current shared TCB */ pSmObjTcbTmp = (SM_OBJ_TCB volatile *) SM_DL_NEXT (pSmObjTcb); /* add the shared TCB to its CPU flush Queue */ smDllAdd (&flushQ [ntohl (pSmObjTcb->ownerCpu)], (SM_DL_NODE *) pSmObjTcb); /* make current shared TCB be next shared TCB */ pSmObjTcb = pSmObjTcbTmp; } while (pSmObjTcbTmp != LOC_NULL); /* notify remote flush */ for (cpuNum = 0; cpuNum < SM_OBJ_MAX_CPU; cpuNum ++) { if ((SM_DL_FIRST (&flushQ [cpuNum]) != (int) LOC_NULL) && (cpuNum != smObjProcNum)) { /* * There is one or more task running on cpuNum to unblock and * cpuNum is not the local CPU so notify cpuNum. */ if (smObjEventSend (&flushQ [cpuNum], cpuNum) != OK) { return (ERROR); } } } /* perform local flush if needed */ if (SM_DL_FIRST (&flushQ [smObjProcNum]) != (int) LOC_NULL) { kernelState = TRUE; /* ENTER KERNEL */ /* * Since all tasks have been removed from the shared * semaphore pend queue, unblocking them now consists * of putting all the tasks for which the shared TCBs * are in the flush Queue in the ready Queue. * This is exacly what smObjEventProcess() does on the remote * side so we just call it. */ smObjEventProcess (&flushQ [smObjProcNum]); windExit (); /* EXIT KERNEL */ } return (OK); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -