zephyr/kernel/microkernel/channel/K_ChProc.c
Allan Stephens 067a47558f Consolidate (most) microkernel non-public API declarations
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>
2016-02-05 20:13:55 -05:00

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;
}
/*****************************************************************************/