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