/* semRWLibInlineP.h - inline reader/writer semaphore library header file */ /* * Copyright (c) 2013 Wind River Systems, Inc. * * The right to copy, distribute or otherwise make use of this software * may be licensed only pursuant to the terms of an applicable Wind River * license agreement. */ /* modification history -------------------- 01a,27jun13,dlk written. */ /* * This module is experimental and private. The interfaces it defines * are subject to change without notice. * * Inline implementation of reader/writer semaphores, (somewhat) scalable * according to provided semaphore options. Initially only "minimal" and * "fully featured" option combinations may be supported. * * These inline reader/writer semaphore APIs are supported only in the * kernel, not from RTPs. */ #ifndef __INCsemRWLibInlinePh #define __INCsemRWLibInlinePh #include #include #ifndef _ASMLANGUAGE #include #include #include #include /* * The following private header files are necessary for the implementation of * the inline APIs. The contents of these files are subject to change without * notice. */ #include #include #include #include #ifndef _WRS_CONFIG_SMP #include #include #endif /* WRS_CONFIG_SMP */ #endif /* _ASMLANGUAGE */ /* * Additional pseudo-options (not stored in semId->options). * These must not conflict in value with the options defined in semLibCommon.h. * Start from the most-significant bit and work down. */ #define SEM_RW_WRITER_CALL ((int)0x80000000) #define SEM_RW_READER_CALL (0x40000000) /* option masks for Reader-Writer semaphores*/ #define SEM_RW_OPTIONS_MASK_INLINE (SEM_Q_FIFO | \ SEM_Q_PRIORITY | \ SEM_DELETE_SAFE | \ SEM_INVERSION_SAFE | \ SEM_EVENTSEND_ERR_NOTIFY | \ SEM_INTERRUPTIBLE | \ SEM_NO_ID_VALIDATE | \ SEM_NO_ERROR_CHECK | \ SEM_NO_EVENT_SEND | \ SEM_NO_SYSTEM_VIEWER | \ SEM_NO_RECURSE) /* * 'Minimal' reader-writer semaphores have a fast-path that allows * them to be given or taken (under certain conditions) without entering * the kernel and without fetching the current task ID, but they do not * support deletion safety, inversion safety, recursive taking, or VxWorks * event sending. Some positive options must be disallowed, and some * 'negative' options must be enabled. */ #define SEM_RW_OPTIONS_MIN_DISALLOWED (SEM_DELETE_SAFE | \ SEM_INVERSION_SAFE | \ SEM_EVENTSEND_ERR_NOTIFY) /* * Required (negative) options for minimal implementation. * - Supporting recursion requires the reader task table (or a different API) * - Object ID validation requires object lock (entering the kernel in SMP) * - Don't want to support EVENT_SEND. */ #define SEM_RW_OPTIONS_MIN_REQUIRED (SEM_NO_RECURSE | \ SEM_NO_EVENT_SEND | \ SEM_NO_ID_VALIDATE) #define SEM_RW_OPTIONS_ARE_MINIMAL(x) \ (((x) & SEM_RW_OPTIONS_MIN_DISALLOWED) == 0 && \ ((x) & SEM_RW_OPTIONS_MIN_REQUIRED) == SEM_RW_OPTIONS_MIN_REQUIRED) #if !defined (_ASMLANGUAGE) enum SEM_RW_MODE { SEM_RW_MODE_UNOWNED = 0, SEM_RW_MODE_READ, SEM_RW_MODE_WRITE }; #define SEM_RW_INLINE_NDEBUG #ifdef SEM_RW_INLINE_NDEBUG #define semrw_kassert(ignore) ((void) 0) #else /* turn debugging on */ #include #include #define _SEMRW_KASSERT_STR(z) _SEMRW_KASSERT_TMP(z) #define _SEMRW_KASSERT_TMP(z) #z extern void __semrw_kassert (const char *msg); #define semrw_kassert(test) \ ((void) \ ((test) ? ((void) 0) : \ __semrw_kassert("Assertion failed: "#test", file " \ __FILE__ ", line "_SEMRW_KASSERT_STR(__LINE__)"\n"))) #endif /* SEM_RW_INLINE_NDEBUG */ /* function declarations (support routines in semRWMinLib.c) */ extern STATUS semRMinTakeHard (SEM_ID semId, int options); extern STATUS semWMinTakeHard (SEM_ID semId, int options); extern STATUS semRMinGiveHard (SEM_ID semId, int options); extern STATUS semWMinGiveHard (SEM_ID semId, int options); /*************************************************************************** * * _semRWIsrCheck - return ERROR if called from an ISR * * Returns OK if not called from an ISR, otherwise sets cpu-specific * errno value and returns ERROR. * * RETURNS: OK or ERROR * ERRNO: S_intLib_NOT_ISR_CALLABLE * NOMANUAL */ _WRS_INLINE STATUS _semRWIsrCheck (void) { #ifdef _WRS_CONFIG_SMP int cpuid; int intCnt; int level; level = KERNEL_INT_CPU_LOCK(); cpuid = _WRS_CPU_INDEX_GET (); intCnt = _WRS_KERNEL_CPU_GLOBAL_GET (cpuid, intCnt); if (intCnt != 0) { _WRS_KERNEL_CPU_GLOBAL_SET (cpuid, errno, S_intLib_NOT_ISR_CALLABLE); } KERNEL_INT_CPU_UNLOCK(level); return intCnt ? ERROR : 0; #else /* ! _WRS_CONFIG_SMP */ return _WRS_INT_RESTRICT (); #endif /* !_WRS_CONFIG_SMP */ } /******************************************************************************* * * _semRMinGive_inline - give a minimal reader/writer semaphore in read mode * * This routine is internal and should not be called directly. * * It handles the case of a call to semRWGive_inline() with 'minimal' * options by a reader task owning the semaphore. * * RETURNS: OK * ERRNO: N/A * \is * \ie * NOMANUAL */ _WRS_INLINE STATUS _semRMinGive_inline ( SEM_ID semId, /* semaphore ID to give */ int options /* semaphore options */ ) { atomic32_t *pSemVal = &SEM_RWMIN_VALUE(semId); atomic32Val_t v; /* Note, the required memory barrier was done in semRWGive_inline(). */ while (TRUE) { v = vxAtomic32Get_inline (pSemVal); semrw_kassert ((v & SEM_RWMIN_VAL_W_OWNS) == 0); semrw_kassert ((v & ~SEM_RWMIN_VAL_PEND) != 0); /* * Note, this is the only case were *pSemVal may be legitimately * modified outside of obj/kernel lock when SEM_RWMIN_VAL_PEND is set: * a reader-owner other than the last reader-owner may decrement the * reader count when giving the semaphore. */ if (v == (SEM_RWMIN_VAL_R_INC | SEM_RWMIN_VAL_PEND)) break; if (vxAtomic32Cas (pSemVal, v, v - SEM_RWMIN_VAL_R_INC)) return OK; } /* * This is the last reader-owner and there are tasks pended. * Have to enter the kernel. */ return semRMinGiveHard (semId, options); } /******************************************************************************* * * _semWMinGive_inline - give a minimal reader/writer semaphore in write mode * * This routine is internal and should not be called directly. * * It handles the case of a call to semRWGive_inline() with 'minimal' * options by a writer task owning the semaphore. * * RETURNS: OK * ERRNO: N/A * \is * \ie * NOMANUAL */ _WRS_INLINE STATUS _semWMinGive_inline ( SEM_ID semId, /* semaphore ID to give */ int options /* semaphore options */ ) { atomic32_t *pSemVal = &SEM_RWMIN_VALUE(semId); atomic32Val_t v; /* The required memory barrier was done in semRWGive_inline() */ while (TRUE) { v = vxAtomic32Get_inline (pSemVal); semrw_kassert ((v & SEM_RWMIN_VAL_W_OWNS) != 0); semrw_kassert ((v & ~(SEM_RWMIN_VAL_PEND|SEM_RWMIN_VAL_W_OWNS)) == 0); if ((v & SEM_RWMIN_VAL_PEND) != 0) break; if (vxAtomic32Cas (pSemVal, v, 0)) return OK; } /* There are tasks pended. Have to enter the kernel. */ return semWMinGiveHard (semId, options); } /******************************************************************************* * * semRWGive_inline - give a reader/writer semaphore (inline version) * * This routine gives a reader/writer semaphore. * * The behavior of this routine is similar to that of semRWGive() on a * reader/writer semaphore, differing in that an additional parameter, * , must be specified. The parameter allows scaling * support for particular semaphore features out of the call to this routine. * * The semaphore specified by must be of the reader/writer type. * * The options specified by are called 'minimal' if the following * options are not enabled: * SEM_DELETE_SAFE * SEM_INVERSION_SAFE * SEM_EVENTSEND_ERR_NOTIFY * and the following (negative) options are enabled: * SEM_NO_RECURSE * SEM_NO_EVENT_SEND * SEM_NO_ID_VALIDATE * * If the options are 'minimal', one of the special pseudo-options * SEM_RW_READER_CALL or SEM_RW_WRITER_CALL may also be specified to omit * (compile out) the dynamic test of whether the semaphore is currently * reader-owned or writer-owned, provided the calling code knows this. * * For best scalability, this inline routine should be called with * a compile-time-constant options value, so that the compiler may optimize * away inapplicable code branches. */ _WRS_INLINE STATUS semRWGive_inline ( SEM_ID semId, /* semaphore ID to give */ int options /* semaphore options */ ) { int level; atomic32_t *pSemVal; atomic32Val_t val; if ((options & ~(SEM_RW_OPTIONS_MASK_INLINE | SEM_RW_WRITER_CALL | SEM_RW_READER_CALL)) != 0) { errno = S_semLib_INVALID_OPTION; return (ERROR); } #ifdef _WRS_CONFIG_SV_INSTRUMENTATION if ((options & SEM_NO_SYSTEM_VIEWER) == 0) { _Vx_usr_arg_t owner; _Vx_usr_arg_t recurse; if (SEM_RW_OPTIONS_ARE_MINIMAL(options)) { /* * Note: We replace semId->semOwner with *pSemVal, * and replace semId->recurse with 0xabadcafe * for minimal semaphores. Hopefully sticking * with EVT_OBJ_3 (used by semGive()) will lead to less * confusion on the system-viewer host side and still * provide useful information, even if a couple of the * event fields are mislabelled in the display. * An alternative might be to introduce a new event type... */ pSemVal = &SEM_RWMIN_VALUE(semId); owner = (_Vx_usr_arg_t)vxAtomic32Get_inline(pSemVal); recurse = (_Vx_usr_arg_t)0xabadcafe; } else { /* * In the full-featured case, the generic semGive() code * would emit the object-level EVENT_SEMGIVE. Since * we call semRWGive() directly, we must do it here. */ owner = (_Vx_usr_arg_t)semId->semOwner; recurse = (_Vx_usr_arg_t)semId->recurse; } /* system viewer - level 1 event logging */ EVT_OBJ_3 (semId, semClassId, EVENT_SEMGIVE, semId, owner, recurse); } #endif /* _WRS_CONFIG_SV_INSTRUMENTATION */ if (SEM_RW_OPTIONS_ARE_MINIMAL(options)) { if ((options & SEM_NO_ERROR_CHECK) == 0) { if (_semRWIsrCheck() != OK) return ERROR; if (! SEM_IS_MINIMAL (semId)) { errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } /* * Ensure that loads or stores done while holding the lock * are globally seen as occurring before the stores that release * the lock. */ VX_MEM_BARRIER_RW(); if ((options & (SEM_RW_READER_CALL | SEM_RW_WRITER_CALL)) == 0) { /* * Will have to determine if the caller is a reader or writer * dynamically. */ pSemVal = &SEM_RWMIN_VALUE(semId); val = vxAtomic32Get_inline(pSemVal); if ((options & SEM_NO_ERROR_CHECK) == 0) { if (0 == (val & ~SEM_RWMIN_VAL_PEND)) { /* The semaphore appears unowned. */ errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } } else val = 0; if ((options & SEM_RW_WRITER_CALL) != 0 || (val & SEM_RWMIN_VAL_W_OWNS) != 0) return _semWMinGive_inline (semId, options & ~SEM_RW_WRITER_CALL); else return _semRMinGive_inline (semId, options & ~SEM_RW_READER_CALL); } else /* non-minimal options */ { /* * For now, we handle only the full-featured case by calling * semRWGive(). */ if ((options & SEM_NO_ERROR_CHECK) == 0) { if (SEM_IS_MINIMAL (semId)) { errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } return semRWGive (semId); } } /******************************************************************************* * * semWTake_inline - take a reader/writer semaphore in write mode (inline version) * * This routine takes a reader/writer semaphore in write mode. * * The behavior of this routine is similar to that of semWTake() on a * reader/writer semaphore, differing in that an additional parameter, * , must be specified. The parameter allows scaling * support for particular semaphore features out of the call to this routine. * * The semaphore specified by must be of the reader/writer type. */ _WRS_INLINE STATUS semWTake_inline ( SEM_ID semId, /* semaphore ID to take */ _Vx_ticks_t timeout, /* timeout in ticks */ int options /* semaphore options */ ) { if ((options & ~SEM_RW_OPTIONS_MASK_INLINE) != 0) { errno = S_semLib_INVALID_OPTION; return (ERROR); } if (SEM_RW_OPTIONS_ARE_MINIMAL(options)) { atomic32_t *pSemVal = &SEM_RWMIN_VALUE(semId); atomic32Val_t v; #ifdef _WRS_CONFIG_SV_INSTRUMENTATION if (0 == (options & SEM_NO_SYSTEM_VIEWER)) { /* system viewer - level 1 event logging */ /* * Note: We replace semId->semOwner with *pSemVal, * and replace semId->recurse with 0xabadcafe * for minimal semaphores. Hopefully sticking * with EVT_OBJ_3 (used by semWTake()) will lead to less * confusion on the system-viewer host side and still * provide useful information, even if a couple of the * event fields are mislabelled in the display. * An alternative might be to introduce a new event type... */ EVT_OBJ_3 (semId, semClassId, EVENT_SEMWTAKE, semId, *pSemVal, 0xabadcafe); } #endif /* _WRS_CONFIG_SV_INSTRUMENTATION */ if ((options & SEM_NO_ERROR_CHECK) == 0) { if (_semRWIsrCheck() != OK) return ERROR; if (timeout != WAIT_FOREVER || !SEM_IS_MINIMAL (semId)) { errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } while (TRUE) { v = vxAtomic32Get_inline (pSemVal); semrw_kassert ((v & SEM_RWMIN_VAL_W_OWNS) == 0 || (v & ~(SEM_RWMIN_VAL_W_OWNS | SEM_RWMIN_VAL_PEND)) == 0); if (v != 0) break; if (vxAtomic32Cas (pSemVal, 0, SEM_RWMIN_VAL_W_OWNS)) { VX_INSTR_BARRIER(); return OK; } } /* The semaphore appears to be owned; must enter the kernel. */ return semWMinTakeHard (semId, options); } else /* non-minimal options */ { /* * For now, we handle only the full-featured case by calling * semWTake(). */ if ((options & SEM_NO_ERROR_CHECK) == 0) { if (SEM_IS_MINIMAL (semId)) { errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } return semWTake (semId, timeout); } } /******************************************************************************* * * semRTake_inline - take a reader/writer semaphore in read mode (inline version) * * This routine takes a reader/writer semaphore in read mode. * * The behavior of this routine is similar to that of semRTake() on a * reader/writer semaphore, differing in that an additional parameter, * , must be specified. The parameter allows scaling * support for particular semaphore features out of the call to this routine. * * The semaphore specified by must be of the reader/writer type. */ _WRS_INLINE STATUS semRTake_inline ( SEM_ID semId, /* semaphore ID to take */ _Vx_ticks_t timeout, /* timeout in ticks */ int options /* semaphore options */ ) { if ((options & ~SEM_RW_OPTIONS_MASK_INLINE) != 0) { errno = S_semLib_INVALID_OPTION; return (ERROR); } if (SEM_RW_OPTIONS_ARE_MINIMAL(options)) { atomic32_t *pSemVal = &SEM_RWMIN_VALUE(semId); atomic32Val_t v; #ifdef _WRS_CONFIG_SV_INSTRUMENTATION if (0 == (options & SEM_NO_SYSTEM_VIEWER)) { /* system viewer - level 1 event logging */ /* * Note: We replace semId->semOwner with *pSemVal, * and replace semId->recurse with 0xabadcafe * for minimal semaphores. Hopefully sticking * with EVT_OBJ_3 (used by semRTake()) will lead to less * confusion on the system-viewer host side and still * provide useful information, even if a couple of the * event fields are mislabelled in the display. * An alternative might be to introduce a new event type... */ EVT_OBJ_3 (semId, semClassId, EVENT_SEMRTAKE, semId, *pSemVal, 0xabadcafe); } #endif /* _WRS_CONFIG_SV_INSTRUMENTATION */ if ((options & SEM_NO_ERROR_CHECK) == 0) { if (_semRWIsrCheck() != OK) return ERROR; if (timeout != WAIT_FOREVER || !SEM_IS_MINIMAL (semId)) { errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } while (TRUE) { v = vxAtomic32Get_inline (pSemVal); semrw_kassert ((v & SEM_RWMIN_VAL_W_OWNS) == 0 || (v & ~(SEM_RWMIN_VAL_W_OWNS | SEM_RWMIN_VAL_PEND)) == 0); if ((v & (SEM_RWMIN_VAL_W_OWNS | SEM_RWMIN_VAL_PEND)) != 0) break; if (vxAtomic32Cas (pSemVal, v, v + SEM_RWMIN_VAL_R_INC)) { VX_INSTR_BARRIER(); return OK; } } /* * A writer owns the semaphore, or there are pended tasks. Enter * the kernel. Note that the minimal case code does not * implement timeouts. */ return semRMinTakeHard (semId, options); } else /* non-minimal options */ { /* For now, handle the full-featured case by calling semRTake(). */ if ((options & SEM_NO_ERROR_CHECK) == 0) { if (SEM_IS_MINIMAL (semId)) { errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } return semRTake (semId, timeout); } } /*************************************************************************** * * semRWMinOwner_inline - return the owner type of a mimimal reader-writer semaphore * * This function returns a value that indicates whether the semaphore * is currently unowned, held by readers, or owned by a single writer. * * WARNING: * The value returned is only valid and stable if the caller is in fact * a task currently owning the semaphore as either a reader or a writer. * * RETURNS: * \is * \i 'SEM_RW_MODE_UNOWNED' * if the semaphore is currently unheld. * * \i 'SEM_RW_MODE_READ' * if the semaphore is held by one or more reader tasks. * * \i 'SEM_RW_MODE_WRITE' * if the semaphore is owned by a writer task. * \ie */ _WRS_INLINE int semRWOwner_inline ( SEM_ID semId, /* the RW semaphore */ int options /* semaphore options */ ) { if ((options & ~SEM_RW_OPTIONS_MASK_INLINE) != 0) { errno = S_semLib_INVALID_OPTION; return (ERROR); } if (SEM_RW_OPTIONS_ARE_MINIMAL(options)) { atomic32_t *pSemVal = &SEM_RWMIN_VALUE(semId); atomicVal_t v = vxAtomic32Get_inline (pSemVal); if ((options & SEM_NO_ERROR_CHECK) == 0) { if (! SEM_IS_MINIMAL (semId)) { errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } if (v == 0) /* PEND bit will not be set if un-owned... */ return SEM_RW_MODE_UNOWNED; /* no-one owns it */ if (v & SEM_RWMIN_VAL_W_OWNS) return SEM_RW_MODE_WRITE; /* writer owns it */ return SEM_RW_MODE_READ; /* reader owns it */ } else { if ((options & SEM_NO_ERROR_CHECK) == 0) { if (SEM_IS_MINIMAL (semId)) { errnoSet (S_semLib_INVALID_OPERATION); return ERROR; } } /* * If this task is an owner (either read or write) of the semaphore, * then semId->semOwner is stable, even though we have not entered the * kernel. */ if (semId->semOwner != NULL) return SEM_RW_MODE_WRITE; /* * If this task is a read-owner of the semaphore, then * SEMRW_READ_CNT(semId) is stably non-zero. */ if (SEMRW_READ_CNT(semId) != 0) return SEM_RW_MODE_READ; return SEM_RW_MODE_UNOWNED; } } #endif /* !defined (_ASMLANGUAGE) */ #endif /* __INCsemRWLibInlinePh */