blob: 1f2dc3915257725a52696ca543c285f2287045df [file] [log] [blame]
/* 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 "kmemcpy.h"
#include "minik.h"
#include "kticks.h"
#include <toolchain.h>
#include <sections.h>
#include <misc/__assert.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 WT 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 WT 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;
}
/*****************************************************************************/