1005 lines
30 KiB
C
1005 lines
30 KiB
C
/*
|
|
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
|
|
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
|
|
*
|
|
* 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 the copyright holder 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 "los_futex_pri.h"
|
|
#include "los_process_pri.h"
|
|
#include "los_hash.h"
|
|
#include "los_sys_pri.h"
|
|
#include "los_sched_pri.h"
|
|
#include "los_mp.h"
|
|
#include "los_exc.h"
|
|
#include "los_mux_pri.h"
|
|
#include "user_copy.h"
|
|
|
|
|
|
#ifdef LOSCFG_KERNEL_VM
|
|
|
|
#define OS_FUTEX_FROM_FUTEXLIST(ptr) LOS_DL_LIST_ENTRY(ptr, FutexNode, futexList)
|
|
#define OS_FUTEX_FROM_QUEUELIST(ptr) LOS_DL_LIST_ENTRY(ptr, FutexNode, queueList)
|
|
#define OS_FUTEX_KEY_BASE USER_ASPACE_BASE
|
|
#define OS_FUTEX_KEY_MAX (USER_ASPACE_BASE + USER_ASPACE_SIZE)
|
|
|
|
/* private: 0~63 hash index_num
|
|
* shared: 64~79 hash index_num */
|
|
#define FUTEX_INDEX_PRIVATE_MAX 64
|
|
#define FUTEX_INDEX_SHARED_MAX 16
|
|
#define FUTEX_INDEX_MAX (FUTEX_INDEX_PRIVATE_MAX + FUTEX_INDEX_SHARED_MAX)
|
|
|
|
#define FUTEX_INDEX_SHARED_POS FUTEX_INDEX_PRIVATE_MAX
|
|
#define FUTEX_HASH_PRIVATE_MASK (FUTEX_INDEX_PRIVATE_MAX - 1)
|
|
#define FUTEX_HASH_SHARED_MASK (FUTEX_INDEX_SHARED_MAX - 1)
|
|
|
|
typedef struct {
|
|
LosMux listLock;
|
|
LOS_DL_LIST lockList;
|
|
} FutexHash;
|
|
|
|
FutexHash g_futexHash[FUTEX_INDEX_MAX];
|
|
|
|
STATIC INT32 OsFutexLock(LosMux *lock)
|
|
{
|
|
UINT32 ret = LOS_MuxLock(lock, LOS_WAIT_FOREVER);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("Futex lock failed! ERROR: 0x%x!\n", ret);
|
|
return LOS_EINVAL;
|
|
}
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 OsFutexUnlock(LosMux *lock)
|
|
{
|
|
UINT32 ret = LOS_MuxUnlock(lock);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("Futex unlock failed! ERROR: 0x%x!\n", ret);
|
|
return LOS_EINVAL;
|
|
}
|
|
return LOS_OK;
|
|
}
|
|
|
|
UINT32 OsFutexInit(VOID)
|
|
{
|
|
INT32 count;
|
|
UINT32 ret;
|
|
|
|
for (count = 0; count < FUTEX_INDEX_MAX; count++) {
|
|
LOS_ListInit(&g_futexHash[count].lockList);
|
|
ret = LOS_MuxInit(&(g_futexHash[count].listLock), NULL);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
#ifdef LOS_FUTEX_DEBUG
|
|
STATIC VOID OsFutexShowTaskNodeAttr(const LOS_DL_LIST *futexList)
|
|
{
|
|
FutexNode *tempNode = NULL;
|
|
FutexNode *lastNode = NULL;
|
|
LosTaskCB *taskCB = NULL;
|
|
LOS_DL_LIST *queueList = NULL;
|
|
|
|
tempNode = OS_FUTEX_FROM_FUTEXLIST(futexList);
|
|
PRINTK("key(pid) : 0x%x(%d) : ->", tempNode->key, tempNode->pid);
|
|
|
|
for (queueList = &tempNode->queueList; ;) {
|
|
lastNode = OS_FUTEX_FROM_QUEUELIST(queueList);
|
|
if (!LOS_ListEmpty(&(lastNode->pendList))) {
|
|
taskCB = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(lastNode->pendList)));
|
|
PRINTK(" %d(%d) ->", taskCB->taskID, taskCB->priority);
|
|
} else {
|
|
taskCB = LOS_DL_LIST_ENTRY(lastNode, LosTaskCB, futex);
|
|
PRINTK(" %d(%d) ->", taskCB->taskID, -1);
|
|
}
|
|
queueList = queueList->pstNext;
|
|
if (queueList == &tempNode->queueList) {
|
|
break;
|
|
}
|
|
}
|
|
PRINTK("\n");
|
|
}
|
|
|
|
VOID OsFutexHashShow(VOID)
|
|
{
|
|
LOS_DL_LIST *futexList = NULL;
|
|
INT32 count;
|
|
/* The maximum number of barrels of a hash table */
|
|
INT32 hashNodeMax = FUTEX_INDEX_MAX;
|
|
PRINTK("#################### los_futex_pri.hash ####################\n");
|
|
for (count = 0; count < hashNodeMax; count++) {
|
|
futexList = &(g_futexHash[count].lockList);
|
|
if (LOS_ListEmpty(futexList)) {
|
|
continue;
|
|
}
|
|
PRINTK("hash -> index : %d\n", count);
|
|
for (futexList = futexList->pstNext;
|
|
futexList != &(g_futexHash[count].lockList);
|
|
futexList = futexList->pstNext) {
|
|
OsFutexShowTaskNodeAttr(futexList);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
STATIC INLINE UINTPTR OsFutexFlagsToKey(const UINT32 *userVaddr, const UINT32 flags)
|
|
{
|
|
UINTPTR futexKey;
|
|
|
|
if (flags & FUTEX_PRIVATE) {
|
|
futexKey = (UINTPTR)userVaddr;
|
|
} else {
|
|
futexKey = (UINTPTR)LOS_PaddrQuery((UINT32 *)userVaddr);
|
|
}
|
|
|
|
return futexKey;
|
|
}
|
|
|
|
STATIC INLINE UINT32 OsFutexKeyToIndex(const UINTPTR futexKey, const UINT32 flags)
|
|
{
|
|
UINT32 index = LOS_HashFNV32aBuf(&futexKey, sizeof(UINTPTR), FNV1_32A_INIT);
|
|
|
|
if (flags & FUTEX_PRIVATE) {
|
|
index &= FUTEX_HASH_PRIVATE_MASK;
|
|
} else {
|
|
index &= FUTEX_HASH_SHARED_MASK;
|
|
index += FUTEX_INDEX_SHARED_POS;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
STATIC INLINE VOID OsFutexSetKey(UINTPTR futexKey, UINT32 flags, FutexNode *node)
|
|
{
|
|
node->key = futexKey;
|
|
node->index = OsFutexKeyToIndex(futexKey, flags);
|
|
node->pid = (flags & FUTEX_PRIVATE) ? LOS_GetCurrProcessID() : OS_INVALID;
|
|
}
|
|
|
|
STATIC INLINE VOID OsFutexDeinitFutexNode(FutexNode *node)
|
|
{
|
|
node->index = OS_INVALID_VALUE;
|
|
node->pid = 0;
|
|
LOS_ListDelete(&node->queueList);
|
|
}
|
|
|
|
STATIC INLINE VOID OsFutexReplaceQueueListHeadNode(FutexNode *oldHeadNode, FutexNode *newHeadNode)
|
|
{
|
|
LOS_DL_LIST *futexList = oldHeadNode->futexList.pstPrev;
|
|
LOS_ListDelete(&oldHeadNode->futexList);
|
|
LOS_ListHeadInsert(futexList, &newHeadNode->futexList);
|
|
}
|
|
|
|
STATIC INLINE VOID OsFutexDeleteKeyFromFutexList(FutexNode *node)
|
|
{
|
|
LOS_ListDelete(&node->futexList);
|
|
}
|
|
|
|
STATIC VOID OsFutexDeleteKeyNodeFromHash(FutexNode *node, BOOL isDeleteHead, FutexNode **headNode, BOOL *queueFlags)
|
|
{
|
|
FutexNode *nextNode = NULL;
|
|
|
|
if (node->index >= FUTEX_INDEX_MAX) {
|
|
return;
|
|
}
|
|
|
|
if (LOS_ListEmpty(&node->queueList)) {
|
|
OsFutexDeleteKeyFromFutexList(node);
|
|
if (queueFlags != NULL) {
|
|
*queueFlags = TRUE;
|
|
}
|
|
goto EXIT;
|
|
}
|
|
|
|
/* FutexList is not NULL, but the header node of queueList */
|
|
if (node->futexList.pstNext != NULL) {
|
|
if (isDeleteHead == TRUE) {
|
|
nextNode = OS_FUTEX_FROM_QUEUELIST(LOS_DL_LIST_FIRST(&node->queueList));
|
|
OsFutexReplaceQueueListHeadNode(node, nextNode);
|
|
if (headNode != NULL) {
|
|
*headNode = nextNode;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
EXIT:
|
|
OsFutexDeinitFutexNode(node);
|
|
return;
|
|
}
|
|
|
|
VOID OsFutexNodeDeleteFromFutexHash(FutexNode *node, BOOL isDeleteHead, FutexNode **headNode, BOOL *queueFlags)
|
|
{
|
|
FutexHash *hashNode = NULL;
|
|
|
|
UINT32 index = OsFutexKeyToIndex(node->key, (node->pid == OS_INVALID) ? 0 : FUTEX_PRIVATE);
|
|
if (index >= FUTEX_INDEX_MAX) {
|
|
return;
|
|
}
|
|
|
|
hashNode = &g_futexHash[index];
|
|
if (OsMuxLockUnsafe(&hashNode->listLock, LOS_WAIT_FOREVER)) {
|
|
return;
|
|
}
|
|
|
|
if (node->index != index) {
|
|
goto EXIT;
|
|
}
|
|
|
|
OsFutexDeleteKeyNodeFromHash(node, isDeleteHead, headNode, queueFlags);
|
|
|
|
EXIT:
|
|
if (OsMuxUnlockUnsafe(OsCurrTaskGet(), &hashNode->listLock, NULL)) {
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
STATIC FutexNode *OsFutexDeleteAlreadyWakeTaskAndGetNext(const FutexNode *node, FutexNode **headNode, BOOL isDeleteHead)
|
|
{
|
|
FutexNode *tempNode = (FutexNode *)node;
|
|
FutexNode *nextNode = NULL;
|
|
BOOL queueFlag = FALSE;
|
|
|
|
while (LOS_ListEmpty(&(tempNode->pendList))) { /* already weak */
|
|
if (!LOS_ListEmpty(&(tempNode->queueList))) { /* It's not a head node */
|
|
nextNode = OS_FUTEX_FROM_QUEUELIST(LOS_DL_LIST_FIRST(&(tempNode->queueList)));
|
|
}
|
|
|
|
OsFutexDeleteKeyNodeFromHash(tempNode, isDeleteHead, headNode, &queueFlag);
|
|
if (queueFlag) {
|
|
return NULL;
|
|
}
|
|
|
|
tempNode = nextNode;
|
|
}
|
|
|
|
return tempNode;
|
|
}
|
|
|
|
STATIC VOID OsFutexInsertNewFutexKeyToHash(FutexNode *node)
|
|
{
|
|
FutexNode *headNode = NULL;
|
|
FutexNode *tailNode = NULL;
|
|
LOS_DL_LIST *futexList = NULL;
|
|
FutexHash *hashNode = &g_futexHash[node->index];
|
|
|
|
if (LOS_ListEmpty(&hashNode->lockList)) {
|
|
LOS_ListHeadInsert(&(hashNode->lockList), &(node->futexList));
|
|
goto EXIT;
|
|
}
|
|
|
|
headNode = OS_FUTEX_FROM_FUTEXLIST(LOS_DL_LIST_FIRST(&(hashNode->lockList)));
|
|
/* The small key is at the front of the queue */
|
|
if (node->key < headNode->key) {
|
|
LOS_ListHeadInsert(&(hashNode->lockList), &(node->futexList));
|
|
goto EXIT;
|
|
}
|
|
|
|
tailNode = OS_FUTEX_FROM_FUTEXLIST(LOS_DL_LIST_LAST(&(hashNode->lockList)));
|
|
if (node->key > tailNode->key) {
|
|
LOS_ListTailInsert(&(hashNode->lockList), &(node->futexList));
|
|
goto EXIT;
|
|
}
|
|
|
|
for (futexList = hashNode->lockList.pstNext;
|
|
futexList != &(hashNode->lockList);
|
|
futexList = futexList->pstNext) {
|
|
headNode = OS_FUTEX_FROM_FUTEXLIST(futexList);
|
|
if (node->key <= headNode->key) {
|
|
LOS_ListTailInsert(&(headNode->futexList), &(node->futexList));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
EXIT:
|
|
return;
|
|
}
|
|
|
|
STATIC INT32 OsFutexInsertFindFormBackToFront(LOS_DL_LIST *queueList, const LosTaskCB *runTask, FutexNode *node)
|
|
{
|
|
LOS_DL_LIST *listHead = queueList;
|
|
LOS_DL_LIST *listTail = queueList->pstPrev;
|
|
FutexNode *tempNode = NULL;
|
|
LosTaskCB *taskTail = NULL;
|
|
|
|
for (; listHead != listTail; listTail = listTail->pstPrev) {
|
|
tempNode = OS_FUTEX_FROM_QUEUELIST(listTail);
|
|
tempNode = OsFutexDeleteAlreadyWakeTaskAndGetNext(tempNode, NULL, FALSE);
|
|
if (tempNode == NULL) {
|
|
return LOS_NOK;
|
|
}
|
|
taskTail = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(tempNode->pendList)));
|
|
if (runTask->priority >= taskTail->priority) {
|
|
LOS_ListHeadInsert(&(tempNode->queueList), &(node->queueList));
|
|
return LOS_OK;
|
|
} else if (runTask->priority < taskTail->priority) {
|
|
if (listTail->pstPrev == listHead) {
|
|
LOS_ListTailInsert(&(tempNode->queueList), &(node->queueList));
|
|
return LOS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
return LOS_NOK;
|
|
}
|
|
|
|
STATIC INT32 OsFutexInsertFindFromFrontToBack(LOS_DL_LIST *queueList, const LosTaskCB *runTask, FutexNode *node)
|
|
{
|
|
LOS_DL_LIST *listHead = queueList;
|
|
LOS_DL_LIST *listTail = queueList->pstPrev;
|
|
FutexNode *tempNode = NULL;
|
|
LosTaskCB *taskHead = NULL;
|
|
|
|
for (; listHead != listTail; listHead = listHead->pstNext) {
|
|
tempNode = OS_FUTEX_FROM_QUEUELIST(listHead);
|
|
tempNode = OsFutexDeleteAlreadyWakeTaskAndGetNext(tempNode, NULL, FALSE);
|
|
if (tempNode == NULL) {
|
|
return LOS_NOK;
|
|
}
|
|
taskHead = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(tempNode->pendList)));
|
|
/* High priority comes before low priority,
|
|
* in the case of the same priority, after the current node
|
|
*/
|
|
if (runTask->priority >= taskHead->priority) {
|
|
if (listHead->pstNext == listTail) {
|
|
LOS_ListHeadInsert(&(tempNode->queueList), &(node->queueList));
|
|
return LOS_OK;
|
|
}
|
|
continue;
|
|
} else if (runTask->priority < taskHead->priority) {
|
|
LOS_ListTailInsert(&(tempNode->queueList), &(node->queueList));
|
|
return LOS_OK;
|
|
}
|
|
}
|
|
|
|
return LOS_NOK;
|
|
}
|
|
|
|
STATIC INT32 OsFutexRecycleAndFindHeadNode(FutexNode *headNode, FutexNode *node, FutexNode **firstNode)
|
|
{
|
|
UINT32 intSave;
|
|
|
|
SCHEDULER_LOCK(intSave);
|
|
*firstNode = OsFutexDeleteAlreadyWakeTaskAndGetNext(headNode, NULL, TRUE);
|
|
SCHEDULER_UNLOCK(intSave);
|
|
|
|
/* The head node is removed and there was originally only one node under the key */
|
|
if (*firstNode == NULL) {
|
|
OsFutexInsertNewFutexKeyToHash(node);
|
|
LOS_ListInit(&(node->queueList));
|
|
return LOS_OK;
|
|
}
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 OsFutexInsertTasktoPendList(FutexNode **firstNode, FutexNode *node, const LosTaskCB *run)
|
|
{
|
|
LosTaskCB *taskHead = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&((*firstNode)->pendList)));
|
|
LOS_DL_LIST *queueList = &((*firstNode)->queueList);
|
|
FutexNode *tailNode = NULL;
|
|
LosTaskCB *taskTail = NULL;
|
|
|
|
if (run->priority < taskHead->priority) {
|
|
/* The one with the highest priority is inserted at the top of the queue */
|
|
LOS_ListTailInsert(queueList, &(node->queueList));
|
|
OsFutexReplaceQueueListHeadNode(*firstNode, node);
|
|
*firstNode = node;
|
|
return LOS_OK;
|
|
}
|
|
|
|
if (LOS_ListEmpty(queueList) && (run->priority >= taskHead->priority)) {
|
|
/* Insert the next position in the queue with equal priority */
|
|
LOS_ListHeadInsert(queueList, &(node->queueList));
|
|
return LOS_OK;
|
|
}
|
|
|
|
tailNode = OS_FUTEX_FROM_QUEUELIST(LOS_DL_LIST_LAST(queueList));
|
|
taskTail = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(tailNode->pendList)));
|
|
if ((run->priority >= taskTail->priority) ||
|
|
((run->priority - taskHead->priority) > (taskTail->priority - run->priority))) {
|
|
return OsFutexInsertFindFormBackToFront(queueList, run, node);
|
|
}
|
|
|
|
return OsFutexInsertFindFromFrontToBack(queueList, run, node);
|
|
}
|
|
|
|
STATIC FutexNode *OsFindFutexNode(const FutexNode *node)
|
|
{
|
|
FutexHash *hashNode = &g_futexHash[node->index];
|
|
LOS_DL_LIST *futexList = &(hashNode->lockList);
|
|
FutexNode *headNode = NULL;
|
|
|
|
for (futexList = futexList->pstNext;
|
|
futexList != &(hashNode->lockList);
|
|
futexList = futexList->pstNext) {
|
|
headNode = OS_FUTEX_FROM_FUTEXLIST(futexList);
|
|
if ((headNode->key == node->key) && (headNode->pid == node->pid)) {
|
|
return headNode;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
STATIC INT32 OsFindAndInsertToHash(FutexNode *node)
|
|
{
|
|
FutexNode *headNode = NULL;
|
|
FutexNode *firstNode = NULL;
|
|
UINT32 intSave;
|
|
INT32 ret;
|
|
|
|
headNode = OsFindFutexNode(node);
|
|
if (headNode == NULL) {
|
|
OsFutexInsertNewFutexKeyToHash(node);
|
|
LOS_ListInit(&(node->queueList));
|
|
return LOS_OK;
|
|
}
|
|
|
|
ret = OsFutexRecycleAndFindHeadNode(headNode, node, &firstNode);
|
|
if (ret != LOS_OK) {
|
|
return ret;
|
|
} else if (firstNode == NULL) {
|
|
return ret;
|
|
}
|
|
|
|
SCHEDULER_LOCK(intSave);
|
|
ret = OsFutexInsertTasktoPendList(&firstNode, node, OsCurrTaskGet());
|
|
SCHEDULER_UNLOCK(intSave);
|
|
|
|
return ret;
|
|
}
|
|
|
|
STATIC INT32 OsFutexKeyShmPermCheck(const UINT32 *userVaddr, const UINT32 flags)
|
|
{
|
|
PADDR_T paddr;
|
|
|
|
/* Check whether the futexKey is a shared lock */
|
|
if (!(flags & FUTEX_PRIVATE)) {
|
|
paddr = (UINTPTR)LOS_PaddrQuery((UINT32 *)userVaddr);
|
|
if (paddr == 0) return LOS_NOK;
|
|
}
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 OsFutexWaitParamCheck(const UINT32 *userVaddr, UINT32 flags, UINT32 absTime)
|
|
{
|
|
VADDR_T vaddr = (VADDR_T)(UINTPTR)userVaddr;
|
|
|
|
if (OS_INT_ACTIVE) {
|
|
return LOS_EINTR;
|
|
}
|
|
|
|
if (flags & (~FUTEX_PRIVATE)) {
|
|
PRINT_ERR("Futex wait param check failed! error flags: 0x%x\n", flags);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if ((vaddr % sizeof(INT32)) || (vaddr < OS_FUTEX_KEY_BASE) || (vaddr >= OS_FUTEX_KEY_MAX)) {
|
|
PRINT_ERR("Futex wait param check failed! error userVaddr: 0x%x\n", vaddr);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if (flags && (OsFutexKeyShmPermCheck(userVaddr, flags) != LOS_OK)) {
|
|
PRINT_ERR("Futex wait param check failed! error shared memory perm userVaddr: 0x%x\n", userVaddr);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if (!absTime) {
|
|
PRINT_ERR("Futex wait param check failed! error absTime: %u\n", absTime);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 OsFutexDeleteTimeoutTaskNode(FutexHash *hashNode, FutexNode *node)
|
|
{
|
|
UINT32 intSave;
|
|
if (OsFutexLock(&hashNode->listLock)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if (node->index < FUTEX_INDEX_MAX) {
|
|
SCHEDULER_LOCK(intSave);
|
|
(VOID)OsFutexDeleteAlreadyWakeTaskAndGetNext(node, NULL, TRUE);
|
|
SCHEDULER_UNLOCK(intSave);
|
|
}
|
|
|
|
#ifdef LOS_FUTEX_DEBUG
|
|
OsFutexHashShow();
|
|
#endif
|
|
|
|
if (OsFutexUnlock(&hashNode->listLock)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
return LOS_ETIMEDOUT;
|
|
}
|
|
|
|
STATIC INT32 OsFutexInsertTaskToHash(LosTaskCB **taskCB, FutexNode **node, const UINTPTR futexKey, const UINT32 flags)
|
|
{
|
|
INT32 ret;
|
|
*taskCB = OsCurrTaskGet();
|
|
*node = &((*taskCB)->futex);
|
|
OsFutexSetKey(futexKey, flags, *node);
|
|
|
|
ret = OsFindAndInsertToHash(*node);
|
|
if (ret) {
|
|
return LOS_NOK;
|
|
}
|
|
|
|
LOS_ListInit(&((*node)->pendList));
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC INT32 OsFutexWaitTask(const UINT32 *userVaddr, const UINT32 flags, const UINT32 val, const UINT32 timeOut)
|
|
{
|
|
INT32 futexRet;
|
|
UINT32 intSave, lockVal;
|
|
LosTaskCB *taskCB = NULL;
|
|
FutexNode *node = NULL;
|
|
UINTPTR futexKey = OsFutexFlagsToKey(userVaddr, flags);
|
|
UINT32 index = OsFutexKeyToIndex(futexKey, flags);
|
|
FutexHash *hashNode = &g_futexHash[index];
|
|
|
|
if (OsFutexLock(&hashNode->listLock)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if (LOS_ArchCopyFromUser(&lockVal, userVaddr, sizeof(UINT32))) {
|
|
PRINT_ERR("Futex wait param check failed! copy from user failed!\n");
|
|
futexRet = LOS_EINVAL;
|
|
goto EXIT_ERR;
|
|
}
|
|
|
|
if (lockVal != val) {
|
|
futexRet = LOS_EBADF;
|
|
goto EXIT_ERR;
|
|
}
|
|
|
|
if (OsFutexInsertTaskToHash(&taskCB, &node, futexKey, flags)) {
|
|
futexRet = LOS_NOK;
|
|
goto EXIT_ERR;
|
|
}
|
|
|
|
SCHEDULER_LOCK(intSave);
|
|
OsTaskWaitSetPendMask(OS_TASK_WAIT_FUTEX, futexKey, timeOut);
|
|
OsSchedTaskWait(&(node->pendList), timeOut, FALSE);
|
|
OsPercpuGet()->taskLockCnt++;
|
|
LOS_SpinUnlock(&g_taskSpin);
|
|
|
|
futexRet = OsFutexUnlock(&hashNode->listLock);
|
|
if (futexRet) {
|
|
OsPercpuGet()->taskLockCnt--;
|
|
LOS_IntRestore(intSave);
|
|
goto EXIT_UNLOCK_ERR;
|
|
}
|
|
|
|
LOS_SpinLock(&g_taskSpin);
|
|
OsPercpuGet()->taskLockCnt--;
|
|
|
|
/*
|
|
* it will immediately do the scheduling, so there's no need to release the
|
|
* task spinlock. when this task's been rescheduled, it will be holding the spinlock.
|
|
*/
|
|
OsSchedResched();
|
|
|
|
if (taskCB->taskStatus & OS_TASK_STATUS_TIMEOUT) {
|
|
taskCB->taskStatus &= ~OS_TASK_STATUS_TIMEOUT;
|
|
SCHEDULER_UNLOCK(intSave);
|
|
return OsFutexDeleteTimeoutTaskNode(hashNode, node);
|
|
}
|
|
|
|
SCHEDULER_UNLOCK(intSave);
|
|
return LOS_OK;
|
|
|
|
EXIT_ERR:
|
|
(VOID)OsFutexUnlock(&hashNode->listLock);
|
|
EXIT_UNLOCK_ERR:
|
|
return futexRet;
|
|
}
|
|
|
|
INT32 OsFutexWait(const UINT32 *userVaddr, UINT32 flags, UINT32 val, UINT32 absTime)
|
|
{
|
|
INT32 ret;
|
|
UINT32 timeOut = LOS_WAIT_FOREVER;
|
|
|
|
ret = OsFutexWaitParamCheck(userVaddr, flags, absTime);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
if (absTime != LOS_WAIT_FOREVER) {
|
|
timeOut = OsUS2Tick(absTime);
|
|
}
|
|
|
|
return OsFutexWaitTask(userVaddr, flags, val, timeOut);
|
|
}
|
|
|
|
STATIC INT32 OsFutexWakeParamCheck(const UINT32 *userVaddr, UINT32 flags)
|
|
{
|
|
VADDR_T vaddr = (VADDR_T)(UINTPTR)userVaddr;
|
|
|
|
if ((flags & (~FUTEX_PRIVATE)) != FUTEX_WAKE) {
|
|
PRINT_ERR("Futex wake param check failed! error flags: 0x%x\n", flags);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if ((vaddr % sizeof(INT32)) || (vaddr < OS_FUTEX_KEY_BASE) || (vaddr >= OS_FUTEX_KEY_MAX)) {
|
|
PRINT_ERR("Futex wake param check failed! error userVaddr: 0x%x\n", userVaddr);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if (flags && (OsFutexKeyShmPermCheck(userVaddr, flags) != LOS_OK)) {
|
|
PRINT_ERR("Futex wake param check failed! error shared memory perm userVaddr: 0x%x\n", userVaddr);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
/* Check to see if the task to be awakened has timed out
|
|
* if time out, to weak next pend task.
|
|
*/
|
|
STATIC VOID OsFutexCheckAndWakePendTask(FutexNode *headNode, const INT32 wakeNumber,
|
|
FutexHash *hashNode, FutexNode **nextNode, BOOL *wakeAny)
|
|
{
|
|
INT32 count;
|
|
LosTaskCB *taskCB = NULL;
|
|
FutexNode *node = headNode;
|
|
for (count = 0; count < wakeNumber; count++) {
|
|
/* Ensure the integrity of the head */
|
|
*nextNode = OsFutexDeleteAlreadyWakeTaskAndGetNext(node, NULL, FALSE);
|
|
if (*nextNode == NULL) {
|
|
/* The last node in queuelist is invalid or the entire list is invalid */
|
|
return;
|
|
}
|
|
node = *nextNode;
|
|
taskCB = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(node->pendList)));
|
|
OsTaskWakeClearPendMask(taskCB);
|
|
OsSchedTaskWake(taskCB);
|
|
*wakeAny = TRUE;
|
|
*nextNode = OS_FUTEX_FROM_QUEUELIST(LOS_DL_LIST_FIRST(&(node->queueList)));
|
|
if (node != headNode) {
|
|
OsFutexDeinitFutexNode(node);
|
|
}
|
|
|
|
if (LOS_ListEmpty(&headNode->queueList)) {
|
|
/* Wakes up the entire linked list node */
|
|
*nextNode = NULL;
|
|
return;
|
|
}
|
|
|
|
node = *nextNode;
|
|
}
|
|
return;
|
|
}
|
|
|
|
STATIC INT32 OsFutexWakeTask(UINTPTR futexKey, UINT32 flags, INT32 wakeNumber, FutexNode **newHeadNode, BOOL *wakeAny)
|
|
{
|
|
UINT32 intSave;
|
|
FutexNode *node = NULL;
|
|
FutexNode *headNode = NULL;
|
|
UINT32 index = OsFutexKeyToIndex(futexKey, flags);
|
|
FutexHash *hashNode = &g_futexHash[index];
|
|
FutexNode tempNode = {
|
|
.key = futexKey,
|
|
.index = index,
|
|
.pid = (flags & FUTEX_PRIVATE) ? LOS_GetCurrProcessID() : OS_INVALID,
|
|
};
|
|
|
|
node = OsFindFutexNode(&tempNode);
|
|
if (node == NULL) {
|
|
return LOS_EBADF;
|
|
}
|
|
|
|
headNode = node;
|
|
|
|
SCHEDULER_LOCK(intSave);
|
|
OsFutexCheckAndWakePendTask(headNode, wakeNumber, hashNode, newHeadNode, wakeAny);
|
|
if ((*newHeadNode) != NULL) {
|
|
OsFutexReplaceQueueListHeadNode(headNode, *newHeadNode);
|
|
OsFutexDeinitFutexNode(headNode);
|
|
} else if (headNode->index < FUTEX_INDEX_MAX) {
|
|
OsFutexDeleteKeyFromFutexList(headNode);
|
|
OsFutexDeinitFutexNode(headNode);
|
|
}
|
|
SCHEDULER_UNLOCK(intSave);
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
INT32 OsFutexWake(const UINT32 *userVaddr, UINT32 flags, INT32 wakeNumber)
|
|
{
|
|
INT32 ret, futexRet;
|
|
UINTPTR futexKey;
|
|
UINT32 index;
|
|
FutexHash *hashNode = NULL;
|
|
FutexNode *headNode = NULL;
|
|
BOOL wakeAny = FALSE;
|
|
|
|
if (OsFutexWakeParamCheck(userVaddr, flags)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
futexKey = OsFutexFlagsToKey(userVaddr, flags);
|
|
index = OsFutexKeyToIndex(futexKey, flags);
|
|
|
|
hashNode = &g_futexHash[index];
|
|
if (OsFutexLock(&hashNode->listLock)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
ret = OsFutexWakeTask(futexKey, flags, wakeNumber, &headNode, &wakeAny);
|
|
if (ret) {
|
|
goto EXIT_ERR;
|
|
}
|
|
|
|
#ifdef LOS_FUTEX_DEBUG
|
|
OsFutexHashShow();
|
|
#endif
|
|
|
|
futexRet = OsFutexUnlock(&hashNode->listLock);
|
|
if (futexRet) {
|
|
goto EXIT_UNLOCK_ERR;
|
|
}
|
|
|
|
if (wakeAny == TRUE) {
|
|
LOS_MpSchedule(OS_MP_CPU_ALL);
|
|
LOS_Schedule();
|
|
}
|
|
|
|
return LOS_OK;
|
|
|
|
EXIT_ERR:
|
|
futexRet = OsFutexUnlock(&hashNode->listLock);
|
|
EXIT_UNLOCK_ERR:
|
|
if (futexRet) {
|
|
return futexRet;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
STATIC INT32 OsFutexRequeueInsertNewKey(UINTPTR newFutexKey, INT32 newIndex, FutexNode *oldHeadNode)
|
|
{
|
|
INT32 ret;
|
|
UINT32 intSave;
|
|
LosTaskCB *task = NULL;
|
|
FutexNode *nextNode = NULL;
|
|
FutexNode newTempNode = {
|
|
.key = newFutexKey,
|
|
.index = newIndex,
|
|
.pid = (newIndex < FUTEX_INDEX_SHARED_POS) ? LOS_GetCurrProcessID() : OS_INVALID,
|
|
};
|
|
LOS_DL_LIST *queueList = &oldHeadNode->queueList;
|
|
FutexNode *newHeadNode = OsFindFutexNode(&newTempNode);
|
|
if (newHeadNode == NULL) {
|
|
OsFutexInsertNewFutexKeyToHash(oldHeadNode);
|
|
return LOS_OK;
|
|
}
|
|
|
|
do {
|
|
nextNode = OS_FUTEX_FROM_QUEUELIST(queueList);
|
|
SCHEDULER_LOCK(intSave);
|
|
if (LOS_ListEmpty(&nextNode->pendList)) {
|
|
queueList = queueList->pstNext;
|
|
OsFutexDeinitFutexNode(nextNode);
|
|
SCHEDULER_UNLOCK(intSave);
|
|
if (queueList->pstNext != NULL) {
|
|
continue;
|
|
} else {
|
|
return LOS_OK;
|
|
}
|
|
}
|
|
|
|
task = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(nextNode->pendList)));
|
|
queueList = queueList->pstNext;
|
|
LOS_ListDelete(&nextNode->queueList);
|
|
ret = OsFutexInsertTasktoPendList(&newHeadNode, nextNode, task);
|
|
SCHEDULER_UNLOCK(intSave);
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("Futex requeue insert new key failed!\n");
|
|
}
|
|
} while (queueList->pstNext != NULL);
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC VOID OsFutexRequeueSplitTwoLists(FutexHash *oldHashNode, FutexNode *oldHeadNode,
|
|
UINT32 flags, UINTPTR futexKey, INT32 count)
|
|
{
|
|
LOS_DL_LIST *queueList = &oldHeadNode->queueList;
|
|
FutexNode *tailNode = OS_FUTEX_FROM_QUEUELIST(LOS_DL_LIST_LAST(queueList));
|
|
INT32 newIndex = OsFutexKeyToIndex(futexKey, flags);
|
|
FutexNode *nextNode = NULL;
|
|
FutexNode *newHeadNode = NULL;
|
|
LOS_DL_LIST *futexList = NULL;
|
|
BOOL isAll = FALSE;
|
|
INT32 i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
nextNode = OS_FUTEX_FROM_QUEUELIST(queueList);
|
|
nextNode->key = futexKey;
|
|
nextNode->index = newIndex;
|
|
if (queueList->pstNext == &oldHeadNode->queueList) {
|
|
isAll = TRUE;
|
|
break;
|
|
}
|
|
|
|
queueList = queueList->pstNext;
|
|
}
|
|
|
|
futexList = oldHeadNode->futexList.pstPrev;
|
|
LOS_ListDelete(&oldHeadNode->futexList);
|
|
if (isAll == TRUE) {
|
|
return;
|
|
}
|
|
|
|
newHeadNode = OS_FUTEX_FROM_QUEUELIST(queueList);
|
|
LOS_ListHeadInsert(futexList, &newHeadNode->futexList);
|
|
oldHeadNode->queueList.pstPrev = &nextNode->queueList;
|
|
nextNode->queueList.pstNext = &oldHeadNode->queueList;
|
|
newHeadNode->queueList.pstPrev = &tailNode->queueList;
|
|
tailNode->queueList.pstNext = &newHeadNode->queueList;
|
|
return;
|
|
}
|
|
|
|
STATIC FutexNode *OsFutexRequeueRemoveOldKeyAndGetHead(UINTPTR oldFutexKey, UINT32 flags, INT32 wakeNumber,
|
|
UINTPTR newFutexKey, INT32 requeueCount, BOOL *wakeAny)
|
|
{
|
|
INT32 ret;
|
|
INT32 oldIndex = OsFutexKeyToIndex(oldFutexKey, flags);
|
|
FutexNode *oldHeadNode = NULL;
|
|
FutexHash *oldHashNode = &g_futexHash[oldIndex];
|
|
FutexNode oldTempNode = {
|
|
.key = oldFutexKey,
|
|
.index = oldIndex,
|
|
.pid = (flags & FUTEX_PRIVATE) ? LOS_GetCurrProcessID() : OS_INVALID,
|
|
};
|
|
|
|
if (wakeNumber > 0) {
|
|
ret = OsFutexWakeTask(oldFutexKey, flags, wakeNumber, &oldHeadNode, wakeAny);
|
|
if ((ret != LOS_OK) || (oldHeadNode == NULL)) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (requeueCount <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (oldHeadNode == NULL) {
|
|
oldHeadNode = OsFindFutexNode(&oldTempNode);
|
|
if (oldHeadNode == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
OsFutexRequeueSplitTwoLists(oldHashNode, oldHeadNode, flags, newFutexKey, requeueCount);
|
|
|
|
return oldHeadNode;
|
|
}
|
|
|
|
STATIC INT32 OsFutexRequeueParamCheck(const UINT32 *oldUserVaddr, UINT32 flags, const UINT32 *newUserVaddr)
|
|
{
|
|
VADDR_T oldVaddr = (VADDR_T)(UINTPTR)oldUserVaddr;
|
|
VADDR_T newVaddr = (VADDR_T)(UINTPTR)newUserVaddr;
|
|
|
|
if (oldVaddr == newVaddr) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if ((flags & (~FUTEX_PRIVATE)) != FUTEX_REQUEUE) {
|
|
PRINT_ERR("Futex requeue param check failed! error flags: 0x%x\n", flags);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if ((oldVaddr % sizeof(INT32)) || (oldVaddr < OS_FUTEX_KEY_BASE) || (oldVaddr >= OS_FUTEX_KEY_MAX)) {
|
|
PRINT_ERR("Futex requeue param check failed! error old userVaddr: 0x%x\n", oldUserVaddr);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if ((newVaddr % sizeof(INT32)) || (newVaddr < OS_FUTEX_KEY_BASE) || (newVaddr >= OS_FUTEX_KEY_MAX)) {
|
|
PRINT_ERR("Futex requeue param check failed! error new userVaddr: 0x%x\n", newUserVaddr);
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
return LOS_OK;
|
|
}
|
|
|
|
INT32 OsFutexRequeue(const UINT32 *userVaddr, UINT32 flags, INT32 wakeNumber, INT32 count, const UINT32 *newUserVaddr)
|
|
{
|
|
INT32 ret;
|
|
UINTPTR oldFutexKey;
|
|
UINTPTR newFutexKey;
|
|
INT32 oldIndex;
|
|
INT32 newIndex;
|
|
FutexHash *oldHashNode = NULL;
|
|
FutexHash *newHashNode = NULL;
|
|
FutexNode *oldHeadNode = NULL;
|
|
BOOL wakeAny = FALSE;
|
|
|
|
if (OsFutexRequeueParamCheck(userVaddr, flags, newUserVaddr)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
oldFutexKey = OsFutexFlagsToKey(userVaddr, flags);
|
|
newFutexKey = OsFutexFlagsToKey(newUserVaddr, flags);
|
|
oldIndex = OsFutexKeyToIndex(oldFutexKey, flags);
|
|
newIndex = OsFutexKeyToIndex(newFutexKey, flags);
|
|
|
|
oldHashNode = &g_futexHash[oldIndex];
|
|
if (OsFutexLock(&oldHashNode->listLock)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
oldHeadNode = OsFutexRequeueRemoveOldKeyAndGetHead(oldFutexKey, flags, wakeNumber, newFutexKey, count, &wakeAny);
|
|
if (oldHeadNode == NULL) {
|
|
(VOID)OsFutexUnlock(&oldHashNode->listLock);
|
|
if (wakeAny == TRUE) {
|
|
ret = LOS_OK;
|
|
goto EXIT;
|
|
}
|
|
return LOS_EBADF;
|
|
}
|
|
|
|
newHashNode = &g_futexHash[newIndex];
|
|
if (oldIndex != newIndex) {
|
|
if (OsFutexUnlock(&oldHashNode->listLock)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
if (OsFutexLock(&newHashNode->listLock)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
}
|
|
|
|
ret = OsFutexRequeueInsertNewKey(newFutexKey, newIndex, oldHeadNode);
|
|
|
|
if (OsFutexUnlock(&newHashNode->listLock)) {
|
|
return LOS_EINVAL;
|
|
}
|
|
|
|
EXIT:
|
|
if (wakeAny == TRUE) {
|
|
LOS_MpSchedule(OS_MP_CPU_ALL);
|
|
LOS_Schedule();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|