Relocates the declarations for most non-public microkernel APIs into the main include file for such APIs (i.e. minik.h), then eliminates the per-subsystem include files. (The per-subsystem files don't serve any significant purpose now that the routines comprising each microkernel subsystem have been consolidated. In fact, there wasn't a single file that included one of these files that didn't already include minik.h!) Note: The channel APIs have not been consolidated yet, as they require further cleanup. Also, one mailbox API that is used only by the mailbox subsystem itself is moved there rather than being placed in minik.h. Change-Id: Ic7f1ac8a67bd39b685f70379dffff0d0caf1b290 Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
561 lines
16 KiB
C
561 lines
16 KiB
C
/* K_ChProc.c */
|
|
|
|
/*
|
|
* Copyright (c) 1997-2015 Wind River Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Wind River Systems nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <microkernel/k_struct.h>
|
|
#include <kchan.h>
|
|
#include <minik.h>
|
|
#include <toolchain.h>
|
|
#include <sections.h>
|
|
#include <misc/__assert.h>
|
|
#include <ch_buff.h>
|
|
|
|
/*
|
|
* - artefacts: ???
|
|
* - non-optimal:
|
|
* from single requester to multiple requesters : basic function is
|
|
K_ProcWR()
|
|
K_ProcWR() copies remaining data into buffer; better would be to
|
|
possibly copy the remaining data
|
|
to the next requester (if there is one)
|
|
* ...
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
int WriterInProgressIsBlocked(struct pipe_struct *pPipe, struct k_args *pWriter);
|
|
int ReaderInProgressIsBlocked(struct pipe_struct *pPipe, struct k_args *pReader);
|
|
|
|
void K_ChProc(struct pipe_struct *pPipe,
|
|
struct k_args *pNLWriter,
|
|
struct k_args *pNLReader) /* this is not a K_ function */
|
|
{
|
|
|
|
struct k_args *pReader = NULL;
|
|
struct k_args *pWriter = NULL;
|
|
|
|
__ASSERT_NO_MSG(!(pNLWriter && pNLReader)); /* both a pNLWriter and pNLReader, is
|
|
that allowed?
|
|
Code below has not been designed
|
|
for that.
|
|
Anyway, this will not happen in
|
|
current version. */
|
|
|
|
{
|
|
struct k_args *pNextReader;
|
|
struct k_args *pNextWriter;
|
|
|
|
do {
|
|
BOOL bALLNWriterNoGo = FALSE;
|
|
BOOL bALLNReaderNoGo = FALSE;
|
|
|
|
/* Reader:
|
|
*/
|
|
if (NULL != pNLReader) {
|
|
if (pReader != pNLReader) {
|
|
pNextReader = pPipe->Readers;
|
|
if (NULL == pNextReader) {
|
|
if (!(TERM_XXX &
|
|
ChReqGetStatus(
|
|
&(pNLReader->Args
|
|
.ChProc))))
|
|
pNextReader = pNLReader;
|
|
}
|
|
} else {
|
|
/* we already used the extra non-listed
|
|
* Reader */
|
|
if (TERM_XXX &
|
|
ChReqGetStatus(
|
|
&(pReader->Args.ChProc)))
|
|
pNextReader = NULL;
|
|
else
|
|
pNextReader =
|
|
pReader; /* == pNLReader
|
|
*/
|
|
}
|
|
} else {
|
|
pNextReader = pPipe->Readers;
|
|
}
|
|
|
|
/* Writer:
|
|
*/
|
|
if (NULL != pNLWriter) {
|
|
if (pWriter != pNLWriter) {
|
|
pNextWriter = pPipe->Writers;
|
|
if (NULL == pNextWriter) {
|
|
if (!(TERM_XXX &
|
|
ChReqGetStatus(
|
|
&(pNLWriter->Args
|
|
.ChProc))))
|
|
pNextWriter = pNLWriter;
|
|
}
|
|
} else {
|
|
/* we already used the extra non-listed
|
|
* Writer */
|
|
if (TERM_XXX &
|
|
ChReqGetStatus(
|
|
&(pWriter->Args.ChProc)))
|
|
pNextWriter = NULL;
|
|
else
|
|
pNextWriter = pWriter;
|
|
}
|
|
} else {
|
|
pNextWriter = pPipe->Writers;
|
|
}
|
|
|
|
/* check if there is uberhaupt something to do:
|
|
*/
|
|
if (NULL == pNextReader && NULL == pNextWriter)
|
|
return;
|
|
if (pNextReader == pReader && pNextWriter == pWriter)
|
|
break; /* nothing changed, so stop */
|
|
|
|
/* go with pNextReader and pNextWriter:
|
|
*/
|
|
pReader = pNextReader;
|
|
pWriter = pNextWriter;
|
|
|
|
if (pWriter) {
|
|
if (_ALL_N == ChxxxGetChOpt(&(pWriter->Args)) &&
|
|
!ChReqSizeXferred(
|
|
&(pWriter->Args.ChProc)) &&
|
|
_TIME_B !=
|
|
ChxxxGetTimeType((
|
|
K_ARGS_ARGS *)&(pWriter->Args))) {
|
|
/* investigate if there is a problem for
|
|
* his request to be satisfied
|
|
*/
|
|
int iSizeDataInWriter;
|
|
int iSpace2WriteinReaders,
|
|
iFreeBufferSpace;
|
|
int iTotalSpace2Write;
|
|
|
|
iSpace2WriteinReaders =
|
|
CalcFreeReaderSpace(
|
|
pPipe->Readers);
|
|
if (pNLReader)
|
|
iSpace2WriteinReaders +=
|
|
(pNLReader->Args.ChProc
|
|
.iSizeTotal -
|
|
pNLReader->Args.ChProc
|
|
.iSizeXferred);
|
|
BuffGetFreeSpaceTotal(
|
|
&(pPipe->Buff),
|
|
&iFreeBufferSpace);
|
|
iTotalSpace2Write =
|
|
iFreeBufferSpace +
|
|
iSpace2WriteinReaders;
|
|
iSizeDataInWriter =
|
|
pWriter->Args.ChProc
|
|
.iSizeTotal -
|
|
pWriter->Args.ChProc
|
|
.iSizeXferred;
|
|
|
|
if (iSizeDataInWriter >
|
|
iTotalSpace2Write)
|
|
bALLNWriterNoGo = TRUE;
|
|
}
|
|
}
|
|
if (pReader) {
|
|
if (_ALL_N == ChxxxGetChOpt(&(pReader->Args)) &&
|
|
!ChReqSizeXferred(
|
|
&(pReader->Args.ChProc)) &&
|
|
_TIME_B !=
|
|
ChxxxGetTimeType((
|
|
K_ARGS_ARGS *)&(pReader->Args))) {
|
|
/* investigate if there is a problem for
|
|
* his request to be satisfied
|
|
*/
|
|
int iSizeFreeSpaceInReader;
|
|
int iData2ReadFromWriters,
|
|
iAvailBufferData;
|
|
int iTotalData2Read;
|
|
|
|
iData2ReadFromWriters =
|
|
CalcAvailWriterData(
|
|
pPipe->Writers);
|
|
if (pNLWriter)
|
|
iData2ReadFromWriters +=
|
|
(pNLWriter->Args.ChProc
|
|
.iSizeTotal -
|
|
pNLWriter->Args.ChProc
|
|
.iSizeXferred);
|
|
BuffGetAvailDataTotal(
|
|
&(pPipe->Buff),
|
|
&iAvailBufferData);
|
|
iTotalData2Read = iAvailBufferData +
|
|
iData2ReadFromWriters;
|
|
iSizeFreeSpaceInReader =
|
|
pReader->Args.ChProc
|
|
.iSizeTotal -
|
|
pReader->Args.ChProc
|
|
.iSizeXferred;
|
|
|
|
if (iSizeFreeSpaceInReader >
|
|
iTotalData2Read)
|
|
bALLNReaderNoGo = TRUE;
|
|
}
|
|
}
|
|
|
|
__ASSERT_NO_MSG(!(bALLNWriterNoGo && bALLNReaderNoGo));
|
|
|
|
/************/
|
|
/* ACTION: */
|
|
/************/
|
|
|
|
if (bALLNWriterNoGo) {
|
|
/* investigate if we must force a transfer to
|
|
* avoid a stall
|
|
*/
|
|
if (!BuffEmpty(&(pPipe->Buff))) {
|
|
if (pReader) {
|
|
K_ChProcRO(pPipe, pReader);
|
|
continue;
|
|
} else
|
|
return; /* we could break as
|
|
well, but then
|
|
nothing else will
|
|
happen */
|
|
} else {
|
|
#ifdef FORCE_XFER_ON_STALL
|
|
if (pReader &&
|
|
_TIME_NB !=
|
|
ChxxxGetTimeType((
|
|
K_ARGS_ARGS *)&(pWriter->Args))) {/* force transfer (we make exception for non-blocked writer) */
|
|
K_ChProcWR(pPipe,
|
|
pWriter,
|
|
pReader);
|
|
continue;
|
|
} else
|
|
#endif
|
|
return; /* we could break as
|
|
well, but then
|
|
nothing else will
|
|
happen */
|
|
}
|
|
} else if (bALLNReaderNoGo) {
|
|
/* investigate if we must force a transfer to
|
|
* avoid a stall
|
|
*/
|
|
if (!BuffFull(&(pPipe->Buff))) {
|
|
if (pWriter) {
|
|
K_ChProcWO(pPipe, pWriter);
|
|
continue;
|
|
} else
|
|
return;
|
|
} else {
|
|
#ifdef FORCE_XFER_ON_STALL
|
|
if (pWriter &&
|
|
_TIME_NB !=
|
|
ChxxxGetTimeType((
|
|
K_ARGS_ARGS *)&(pReader->Args))) {/* force transfer (we make exception for non-blocked reader) */
|
|
K_ChProcWR(pPipe,
|
|
pWriter,
|
|
pReader);
|
|
continue;
|
|
} else
|
|
#endif
|
|
return;
|
|
}
|
|
} else {
|
|
/* no blocked reader and no blocked writer (if
|
|
there are any of them)
|
|
== NOMINAL operation
|
|
*/
|
|
if (pReader) {
|
|
if (pWriter) {
|
|
K_ChProcWR(pPipe,
|
|
pWriter,
|
|
pReader);
|
|
continue;
|
|
} else {
|
|
K_ChProcRO(pPipe, pReader);
|
|
continue;
|
|
}
|
|
} else {
|
|
if (pWriter) {
|
|
K_ChProcWO(pPipe, pWriter);
|
|
continue;
|
|
} else {
|
|
/* we should not come here */
|
|
__ASSERT_NO_MSG(1 == 0);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} while (1);
|
|
|
|
/* {We stopped processing because nothing changed anymore
|
|
(stall)}
|
|
Let's examine the situation a little bit further:
|
|
*/
|
|
pReader = pNextReader;
|
|
pWriter = pNextWriter;
|
|
}
|
|
/* if we come here, it is b/c pReader and pWriter did not change
|
|
anymore.
|
|
- Normally one of them is NULL, which means only a writer, resp. a
|
|
reader remained.
|
|
- The case that none of them is NULL is a special case which 'normally'
|
|
does not occur.
|
|
A remaining pReader and/or pWriter are expected to be not-completed.
|
|
|
|
Note that in the case there is only a reader or there is only a
|
|
writer, it can be a ALL_N request.
|
|
This happens when his request has not been processed completely yet
|
|
(b/c of a copy in and copy out
|
|
conflict in the buffer e.g.), but is expected to be processed
|
|
completely somewhat later (must be !)
|
|
*/
|
|
|
|
/* in the sequel, we will:
|
|
1. check the hypothesis that an existing pReader/pWriter is not
|
|
completed
|
|
2. check if we can force the termination of a X_TO_N request when
|
|
some data transfer took place
|
|
3. check if we have to cancel a timer when the (first) data has been
|
|
Xferred
|
|
4. Check if we have to kick out a queued request because its
|
|
processing is really blocked (for some reason)
|
|
*/
|
|
if (pReader && pWriter) {
|
|
__ASSERT_NO_MSG(!(TERM_XXX & ChReqGetStatus(&(pReader->Args.ChProc))) &&
|
|
!(TERM_XXX & ChReqGetStatus(&(pWriter->Args.ChProc))));
|
|
/* this could be possible when data Xfer operations are jammed
|
|
(out of data Xfer resources e.g.) */
|
|
|
|
/* later on, at least one of them will be completed.
|
|
Force termination of X_TO_N request?
|
|
- If one of the requesters is X_TO_N and the other one is
|
|
ALL_N, we cannot force termination
|
|
of the X_TO_N request
|
|
- If they are both X_TO_N, we can do so (but can this
|
|
situation be?)
|
|
|
|
In this version, we will NOT do so and try to transfer data
|
|
as much as possible as
|
|
there are now 2 parties present to exchange data, possibly
|
|
directly
|
|
(this is an implementation choice, but I think it is best for
|
|
overall application performance)
|
|
*/
|
|
;
|
|
} else if (pReader) {
|
|
__ASSERT_NO_MSG(!(TERM_XXX & ChReqGetStatus(&(pReader->Args.ChProc))));
|
|
|
|
/* check if this lonely reader is really blocked, then we will
|
|
delist him
|
|
(if he was listed uberhaupt) == EMERGENCY BREAK */
|
|
if (ReaderInProgressIsBlocked(pPipe, pReader)) {
|
|
if (_X_TO_N & ChxxxGetChOpt(&(pReader->Args)) &&
|
|
ChReqSizeXferred(&(pReader->Args.ChProc))) {
|
|
ChReqSetStatus(&(pReader->Args.ChProc),
|
|
TERM_SATISFIED);
|
|
} else {
|
|
ChReqSetStatus(&(pReader->Args.ChProc),
|
|
TERM_FORCED); /* in all other
|
|
cases: forced
|
|
termination */
|
|
}
|
|
|
|
if (pReader->Head) {
|
|
DeListWaiter(pReader);
|
|
myfreetimer(&(pReader->Time.timer));
|
|
}
|
|
if (0 == pReader->Args.ChProc.iNbrPendXfers) {
|
|
pReader->Comm = CHDEQ_RPL;
|
|
K_ChRecvRpl(
|
|
pReader); /* if terminated and no
|
|
pending Xfers anymore, we
|
|
have to reply */
|
|
}
|
|
} else {
|
|
/* temporary stall (must be, processing will continue
|
|
* later on) */
|
|
}
|
|
} else if (pWriter) {
|
|
__ASSERT_NO_MSG(!(TERM_SATISFIED &
|
|
ChReqGetStatus(&(pWriter->Args.ChProc))));
|
|
|
|
/* check if this lonely Writer is really blocked, then we will
|
|
delist him
|
|
(if he was listed uberhaupt) == EMERGENCY BREAK */
|
|
if (WriterInProgressIsBlocked(pPipe, pWriter)) {
|
|
if (_X_TO_N & ChxxxGetChOpt(&(pWriter->Args)) &&
|
|
ChReqSizeXferred(&(pWriter->Args.ChProc))) {
|
|
ChReqSetStatus(&(pWriter->Args.ChProc),
|
|
TERM_SATISFIED);
|
|
} else {
|
|
ChReqSetStatus(&(pWriter->Args.ChProc),
|
|
TERM_FORCED); /* in all other
|
|
cases: forced
|
|
termination */
|
|
}
|
|
|
|
if (pWriter->Head) {
|
|
DeListWaiter(pWriter);
|
|
myfreetimer(&(pWriter->Time.timer));
|
|
}
|
|
if (0 == pWriter->Args.ChProc.iNbrPendXfers) {
|
|
pWriter->Comm = CHENQ_RPL;
|
|
K_ChSendRpl(
|
|
pWriter); /* if terminated and no
|
|
pending Xfers anymore, we
|
|
have to reply */
|
|
}
|
|
|
|
} else {
|
|
/* temporary stall (must be, processing will continue
|
|
* later on) */
|
|
}
|
|
} else {
|
|
__ASSERT_NO_MSG(1 == 0); /* we should not come ... here :-) */
|
|
}
|
|
|
|
/* check if we have to cancel a timer for a request:
|
|
*/
|
|
|
|
#ifdef CANCEL_TIMERS
|
|
|
|
if (pReader) {
|
|
if (ChReqSizeXferred(&(pReader->Args.ChProc))) {
|
|
if (pReader->Head)
|
|
myfreetimer(
|
|
&(pReader->Time.timer)); /* do not
|
|
delist
|
|
however */
|
|
}
|
|
}
|
|
if (pWriter) {
|
|
if (ChReqSizeXferred(&(pWriter->Args.ChProc))) {
|
|
if (pWriter->Head)
|
|
myfreetimer(
|
|
&(pWriter->Time.timer)); /* do not
|
|
delist
|
|
however */
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int WriterInProgressIsBlocked(struct pipe_struct *pPipe,
|
|
struct k_args *pWriter)
|
|
{
|
|
int iSizeDataInWriter;
|
|
int iFreeBufferSpace;
|
|
TIME_TYPE TimeType;
|
|
K_PIPE_OPTION option;
|
|
|
|
/* premises: */
|
|
/*__ASSERT_NO_MSG( NULL==pReader); */
|
|
|
|
/* first condition: request cannot wait any longer: must be -
|
|
* (non-blocked) or a finite timed wait with a killed timer */
|
|
TimeType = ChxxxGetTimeType((K_ARGS_ARGS *)&(pWriter->Args));
|
|
option = ChxxxGetChOpt((K_ARGS_ARGS *)&(pWriter->Args));
|
|
if (((_TIME_B == TimeType) && (_ALL_N == option)) ||
|
|
((_TIME_B == TimeType) && (_X_TO_N & option) &&
|
|
!(pWriter->Args.ChProc.iSizeXferred))
|
|
#ifdef CANCEL_TIMERS
|
|
||
|
|
((_TIME_BT == TimeType) && pWriter->Time.timer)
|
|
#endif
|
|
) {
|
|
return 0; /* requester can still wait (for some time or
|
|
forever), no problem for now */
|
|
}
|
|
|
|
/* second condition: buffer activity is null */
|
|
if (0 != pPipe->Buff.iNbrPendingWrites ||
|
|
0 != pPipe->Buff.iNbrPendingReads)
|
|
return 0; /* buffer activity detected, can't say now that
|
|
processing is blocked */
|
|
|
|
/* third condition: */
|
|
iSizeDataInWriter = pWriter->Args.ChProc.iSizeTotal -
|
|
pWriter->Args.ChProc.iSizeXferred;
|
|
BuffGetFreeSpaceTotal(&(pPipe->Buff), &iFreeBufferSpace);
|
|
if (iFreeBufferSpace >= iSizeDataInWriter)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int ReaderInProgressIsBlocked(struct pipe_struct *pPipe,
|
|
struct k_args *pReader)
|
|
{
|
|
int iSizeSpaceInReader;
|
|
int iAvailBufferData;
|
|
TIME_TYPE TimeType;
|
|
K_PIPE_OPTION option;
|
|
|
|
/* premises: */
|
|
/*__ASSERT_NO_MSG( NULL==pWriter); */
|
|
|
|
/* first condition: request cannot wait any longer: must be -
|
|
* (non-blocked) or a finite timed wait with a killed timer */
|
|
TimeType = ChxxxGetTimeType((K_ARGS_ARGS *)&(pReader->Args));
|
|
option = ChxxxGetChOpt((K_ARGS_ARGS *)&(pReader->Args));
|
|
if (((_TIME_B == TimeType) && (_ALL_N == option)) ||
|
|
((_TIME_B == TimeType) && (_X_TO_N & option) &&
|
|
!(pReader->Args.ChProc.iSizeXferred))
|
|
#ifdef CANCEL_TIMERS
|
|
||
|
|
((_TIME_BT == TimeType) && pReader->Time.timer)
|
|
#endif
|
|
) {
|
|
return 0; /* requester can still wait (for some time or
|
|
forever), no problem for now */
|
|
}
|
|
|
|
/* second condition: buffer activity is null */
|
|
if (0 != pPipe->Buff.iNbrPendingWrites ||
|
|
0 != pPipe->Buff.iNbrPendingReads)
|
|
return 0; /* buffer activity detected, can't say now that
|
|
processing is blocked */
|
|
|
|
/* third condition: */
|
|
iSizeSpaceInReader = pReader->Args.ChProc.iSizeTotal -
|
|
pReader->Args.ChProc.iSizeXferred;
|
|
BuffGetAvailDataTotal(&(pPipe->Buff), &iAvailBufferData);
|
|
if (iAvailBufferData >= iSizeSpaceInReader)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
/*****************************************************************************/
|