123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021/01/02 bernard the first version
- * 2023-07-25 Shell Remove usage of rt_hw_interrupt API in the lwp
- * Coding style: remove multiple `return` in a routine
- * 2023-08-08 Shell Fix return value of futex(wait); Fix ops that only
- * FUTEX_PRIVATE is supported currently
- * 2023-11-03 Shell Add Support for ~FUTEX_PRIVATE
- * 2023-11-16 xqyjlj Add Support for futex requeue and futex pi
- */
- #define __RT_IPC_SOURCE__
- #include "lwp_futex_internal.h"
- #include "sys/time.h"
- #include <stdatomic.h>
- struct rt_mutex _glob_futex;
- rt_err_t lwp_futex_init(void)
- {
- return rt_mutex_init(&_glob_futex, "glob_ftx", RT_IPC_FLAG_PRIO);
- }
- static void _futex_lock(rt_lwp_t lwp, int op_flags)
- {
- rt_err_t error;
- if (op_flags & FUTEX_PRIVATE)
- {
- LWP_LOCK(lwp);
- }
- else
- {
- error = lwp_mutex_take_safe(&_glob_futex, RT_WAITING_FOREVER, 0);
- if (error)
- {
- LOG_E("%s: Should not failed", __func__);
- RT_ASSERT(0);
- }
- }
- }
- static void _futex_unlock(rt_lwp_t lwp, int op_flags)
- {
- rt_err_t error;
- if (op_flags & FUTEX_PRIVATE)
- {
- LWP_UNLOCK(lwp);
- }
- else
- {
- error = lwp_mutex_release_safe(&_glob_futex);
- if (error)
- {
- LOG_E("%s: Should not failed", __func__);
- RT_ASSERT(0);
- }
- }
- }
- /**
- * Destroy a Private FuTeX (pftx)
- * Note: must have futex address_search_head taken
- */
- static rt_err_t _pftx_destroy_locked(void *data)
- {
- rt_err_t ret = -1;
- rt_futex_t futex = (rt_futex_t)data;
- if (futex)
- {
- /**
- * Brief: Delete the futex from lwp address_search_head
- *
- * Note: Critical Section
- * - the lwp (READ. share by thread)
- * - the lwp address_search_head (RW. protected by caller. for destroy
- * routine, it's always safe because it has already taken a write lock
- * to the lwp.)
- */
- lwp_avl_remove(&futex->node,
- (struct lwp_avl_struct **)futex->node.data);
- /* release object */
- if (futex->mutex)
- {
- rt_mutex_delete(futex->mutex);
- futex->mutex = RT_NULL;
- }
- rt_free(futex);
- ret = 0;
- }
- return ret;
- }
- /**
- * Create a Private FuTeX (pftx)
- * Note: must have futex address_search_head taken
- */
- static rt_futex_t _pftx_create_locked(int *uaddr, struct rt_lwp *lwp)
- {
- rt_futex_t futex = RT_NULL;
- struct rt_object *obj = RT_NULL;
- /**
- * Brief: Create a futex under current lwp
- *
- * Note: Critical Section
- * - lwp (READ; share with thread)
- */
- if (lwp)
- {
- futex = (rt_futex_t)rt_malloc(sizeof(struct rt_futex));
- if (futex)
- {
- /* Create a Private FuTeX (pftx) */
- obj = rt_custom_object_create("pftx", (void *)futex,
- _pftx_destroy_locked);
- if (!obj)
- {
- rt_free(futex);
- futex = RT_NULL;
- }
- else
- {
- /**
- * Brief: Add futex to user object tree for resource recycling
- *
- * Note: Critical Section
- * - lwp user object tree (RW; protected by API)
- * - futex (if the adding is successful, others can find the
- * unready futex. However, only the lwp_free will do this,
- * and this is protected by the ref taken by the lwp thread
- * that the lwp_free will never execute at the same time)
- */
- if (lwp_user_object_add(lwp, obj))
- {
- /* this will call a _pftx_destroy_locked, but that's okay */
- rt_object_delete(obj);
- rt_free(futex);
- futex = RT_NULL;
- }
- else
- {
- futex->node.avl_key = (avl_key_t)uaddr;
- futex->node.data = &lwp->address_search_head;
- futex->custom_obj = obj;
- futex->mutex = RT_NULL;
- rt_list_init(&(futex->waiting_thread));
- /**
- * Brief: Insert into futex head
- *
- * Note: Critical Section
- * - lwp address_search_head (RW; protected by caller)
- */
- lwp_avl_insert(&futex->node, &lwp->address_search_head);
- }
- }
- }
- }
- return futex;
- }
- /**
- * Get a Private FuTeX (pftx) match the (lwp, uaddr, op)
- */
- static rt_futex_t _pftx_get(void *uaddr, struct rt_lwp *lwp, int op,
- rt_err_t *rc)
- {
- struct lwp_avl_struct *node = RT_NULL;
- rt_futex_t futex = RT_NULL;
- rt_err_t error = -1;
- LWP_LOCK(lwp);
- /**
- * Note: Critical Section
- * protect lwp address_search_head (READ)
- */
- node = lwp_avl_find((avl_key_t)uaddr, lwp->address_search_head);
- if (node)
- {
- futex = rt_container_of(node, struct rt_futex, node);
- error = 0;
- }
- else
- {
- /* create a futex according to this uaddr */
- futex = _pftx_create_locked(uaddr, lwp);
- if (!futex)
- error = -ENOMEM;
- else
- error = 0;
- }
- LWP_UNLOCK(lwp);
- *rc = error;
- return futex;
- }
- /**
- * Destroy a Shared FuTeX (pftx)
- * Note: must have futex address_search_head taken
- */
- static rt_err_t _sftx_destroy(void *data)
- {
- rt_err_t ret = -1;
- rt_futex_t futex = (rt_futex_t)data;
- if (futex)
- {
- /* delete it even it's not in the table */
- futex_global_table_delete(&futex->entry.key);
- if (futex->mutex)
- {
- rt_mutex_delete(futex->mutex);
- futex->mutex = RT_NULL;
- }
- rt_free(futex);
- ret = 0;
- }
- return ret;
- }
- /**
- * Create a Shared FuTeX (sftx)
- */
- static rt_futex_t _sftx_create(struct shared_futex_key *key, struct rt_lwp *lwp)
- {
- rt_futex_t futex = RT_NULL;
- struct rt_object *obj = RT_NULL;
- if (lwp)
- {
- futex = (rt_futex_t)rt_calloc(1, sizeof(struct rt_futex));
- if (futex)
- {
- /* create a Shared FuTeX (sftx) */
- obj = rt_custom_object_create("sftx", (void *)futex, _sftx_destroy);
- if (!obj)
- {
- rt_free(futex);
- futex = RT_NULL;
- }
- else
- {
- if (futex_global_table_add(key, futex))
- {
- rt_object_delete(obj);
- rt_free(futex);
- futex = RT_NULL;
- }
- else
- {
- futex->mutex = RT_NULL;
- rt_list_init(&(futex->waiting_thread));
- futex->custom_obj = obj;
- }
- }
- }
- }
- return futex;
- }
- /**
- * Get a Shared FuTeX (sftx) match the (lwp, uaddr, op)
- */
- static rt_futex_t _sftx_get(void *uaddr, struct rt_lwp *lwp, int op,
- rt_err_t *rc)
- {
- rt_futex_t futex = RT_NULL;
- struct shared_futex_key key;
- rt_varea_t varea;
- rt_err_t error = -1;
- RD_LOCK(lwp->aspace);
- varea = rt_aspace_query(lwp->aspace, uaddr);
- if (varea)
- {
- key.mobj = varea->mem_obj;
- key.offset = ((varea->offset) << MM_PAGE_SHIFT) |
- ((long)uaddr & ((1 << MM_PAGE_SHIFT) - 1));
- RD_UNLOCK(lwp->aspace);
- /* query for the key */
- _futex_lock(lwp, op & ~FUTEX_PRIVATE);
- error = futex_global_table_find(&key, &futex);
- if (error != RT_EOK)
- {
- /* not found, do allocation */
- futex = _sftx_create(&key, lwp);
- if (!futex)
- error = -ENOMEM;
- else
- error = 0;
- }
- _futex_unlock(lwp, op & ~FUTEX_PRIVATE);
- }
- else
- {
- RD_UNLOCK(lwp->aspace);
- }
- *rc = error;
- return futex;
- }
- /* must have futex address_search_head taken */
- static rt_futex_t _futex_get(void *uaddr, struct rt_lwp *lwp, int op_flags,
- rt_err_t *rc)
- {
- rt_futex_t futex = RT_NULL;
- if (op_flags & FUTEX_PRIVATE)
- {
- futex = _pftx_get(uaddr, lwp, op_flags, rc);
- }
- else
- {
- futex = _sftx_get(uaddr, lwp, op_flags, rc);
- }
- return futex;
- }
- static rt_err_t _suspend_thread_timeout_locked(rt_thread_t thread,
- rt_futex_t futex,
- rt_tick_t timeout)
- {
- rt_err_t rc;
- /**
- * Brief: Add current thread into futex waiting thread list
- *
- * Note: Critical Section
- * - the futex waiting_thread list (RW)
- */
- rc = rt_thread_suspend_to_list(thread, &futex->waiting_thread,
- RT_IPC_FLAG_FIFO, RT_INTERRUPTIBLE);
- if (rc == RT_EOK)
- {
- /* start the timer of thread */
- rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME,
- &timeout);
- rt_timer_start(&(thread->thread_timer));
- rt_set_errno(ETIMEDOUT);
- }
- return rc;
- }
- static rt_err_t _suspend_thread_locked(rt_thread_t thread, rt_futex_t futex)
- {
- /**
- * Brief: Add current thread into futex waiting thread list
- *
- * Note: Critical Section
- * - the futex waiting_thread list (RW)
- */
- return rt_thread_suspend_to_list(thread, &futex->waiting_thread,
- RT_IPC_FLAG_FIFO, RT_INTERRUPTIBLE);
- }
- rt_inline int _futex_cmpxchg_value(int *curval, int *uaddr, int uval,
- int newval)
- {
- int err = 0;
- if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr)))
- {
- err = -EFAULT;
- goto exit;
- }
- if (!atomic_compare_exchange_strong(uaddr, &uval, newval))
- {
- *curval = uval;
- err = -EAGAIN;
- }
- exit:
- return err;
- }
- static int _futex_wait(rt_futex_t futex, struct rt_lwp *lwp, int *uaddr,
- int value, const struct timespec *timeout, int op_flags)
- {
- rt_tick_t to;
- rt_thread_t thread;
- rt_err_t rc = -RT_EINTR;
- /**
- * Brief: Remove current thread from scheduler, besides appends it to
- * the waiting thread list of the futex. If the timeout is specified
- * a timer will be setup for current thread
- *
- * Note: Critical Section
- * - futex.waiting (RW; Protected by lwp_lock)
- * - the local cpu
- */
- _futex_lock(lwp, op_flags);
- if (*uaddr == value)
- {
- thread = rt_thread_self();
- if (timeout)
- {
- to = timeout->tv_sec * RT_TICK_PER_SECOND;
- to +=
- (timeout->tv_nsec * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND;
- if (to < 0)
- {
- rc = -EINVAL;
- _futex_unlock(lwp, op_flags);
- }
- else
- {
- rt_enter_critical();
- rc = _suspend_thread_timeout_locked(thread, futex, to);
- _futex_unlock(lwp, op_flags);
- rt_exit_critical();
- }
- }
- else
- {
- rt_enter_critical();
- rc = _suspend_thread_locked(thread, futex);
- _futex_unlock(lwp, op_flags);
- rt_exit_critical();
- }
- if (rc == RT_EOK)
- {
- /* do schedule */
- rt_schedule();
- /* check errno */
- rc = rt_get_errno();
- rc = rc > 0 ? -rc : rc;
- }
- }
- else
- {
- _futex_unlock(lwp, op_flags);
- rc = -EAGAIN;
- rt_set_errno(EAGAIN);
- }
- return rc;
- }
- static long _futex_wake(rt_futex_t futex, struct rt_lwp *lwp, int number,
- int op_flags)
- {
- long woken_cnt = 0;
- int is_empty = 0;
- /**
- * Brief: Wakeup a suspended thread on the futex waiting thread list
- *
- * Note: Critical Section
- * - the futex waiting_thread list (RW)
- */
- while (number && !is_empty)
- {
- _futex_lock(lwp, op_flags);
- if (rt_susp_list_dequeue(&futex->waiting_thread, RT_EOK))
- {
- number--;
- woken_cnt++;
- is_empty = RT_FALSE;
- }
- else
- {
- is_empty = RT_TRUE;
- }
- _futex_unlock(lwp, op_flags);
- }
- /* do schedule */
- rt_schedule();
- return woken_cnt;
- }
- /**
- * Brief: Wake up to nr_wake futex1 threads.
- * If there are more waiters waiting on futex1 than nr_wake,
- * insert the remaining at most nr_requeue waiters waiting
- * on futex1 into the waiting queue of futex2.
- */
- static long _futex_requeue(rt_futex_t futex1, rt_futex_t futex2,
- struct rt_lwp *lwp, int nr_wake, int nr_requeue,
- int opflags)
- {
- long rtn;
- long woken_cnt = 0;
- int is_empty = 0;
- rt_thread_t thread;
- if (futex1 == futex2)
- {
- return -EINVAL;
- }
- /**
- * Brief: Wakeup a suspended thread on the futex waiting thread list
- *
- * Note: Critical Section
- * - the futex waiting_thread list (RW)
- */
- while (nr_wake && !is_empty)
- {
- if (rt_susp_list_dequeue(&futex1->waiting_thread, RT_EOK))
- {
- nr_wake--;
- woken_cnt++;
- is_empty = RT_FALSE;
- }
- else
- {
- is_empty = RT_TRUE;
- }
- }
- rtn = woken_cnt;
- /**
- * Brief: Requeue
- *
- * Note: Critical Section
- * - the futex waiting_thread list (RW)
- */
- while (!is_empty && nr_requeue)
- {
- rt_sched_lock_level_t slvl;
- rt_sched_lock(&slvl);
- /* moving from one susp list to another */
- is_empty = rt_list_isempty(&(futex1->waiting_thread));
- if (!is_empty)
- {
- thread = RT_THREAD_LIST_NODE_ENTRY(futex1->waiting_thread.next);
- rt_list_remove(&RT_THREAD_LIST_NODE(thread));
- rt_list_insert_before(&(futex2->waiting_thread),
- &RT_THREAD_LIST_NODE(thread));
- nr_requeue--;
- rtn++;
- }
- rt_sched_unlock(slvl);
- }
- /* do schedule */
- rt_schedule();
- return rtn;
- }
- /* timeout argument measured against the CLOCK_REALTIME clock. */
- static long _futex_lock_pi(rt_futex_t futex, struct rt_lwp *lwp, int *uaddr,
- const struct timespec *timeout, int op_flags,
- rt_bool_t trylock)
- {
- int word = 0, nword, cword;
- int tid = 0;
- rt_err_t err = 0;
- rt_thread_t thread = RT_NULL, current_thread = RT_NULL;
- rt_tick_t to = RT_WAITING_FOREVER;
- if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr)))
- {
- return -EFAULT;
- }
- current_thread = rt_thread_self();
- _futex_lock(lwp, op_flags);
- lwp_get_from_user(&word, (void *)uaddr, sizeof(int));
- tid = word & FUTEX_TID_MASK;
- if (word == 0)
- {
- /* If the value is 0, then the kernel tries
- to atomically set the futex value to the caller's TID. */
- nword = current_thread->tid;
- if (_futex_cmpxchg_value(&cword, uaddr, word, nword))
- {
- _futex_unlock(lwp, op_flags);
- return -EAGAIN;
- }
- _futex_unlock(lwp, op_flags);
- return 0;
- }
- else
- {
- thread = lwp_tid_get_thread_and_inc_ref(tid);
- if (thread == RT_NULL)
- {
- _futex_unlock(lwp, op_flags);
- return -ESRCH;
- }
- lwp_tid_dec_ref(thread);
- nword =
- word | FUTEX_WAITERS;
- if (_futex_cmpxchg_value(&cword, uaddr, word, nword))
- {
- _futex_unlock(lwp, op_flags);
- return -EAGAIN;
- }
- word = nword;
- }
- if (futex->mutex == RT_NULL)
- {
- futex->mutex = rt_mutex_create("futexpi", RT_IPC_FLAG_PRIO);
- if (futex->mutex == RT_NULL)
- {
- _futex_unlock(lwp, op_flags);
- return -ENOMEM;
- }
- /* set mutex->owner */
- rt_spin_lock(&(futex->mutex->spinlock));
- futex->mutex->owner = thread;
- futex->mutex->hold = 1;
- rt_spin_unlock(&(futex->mutex->spinlock));
- }
- if (timeout)
- {
- to = rt_timespec_to_tick(timeout);
- }
- if (trylock)
- {
- to = RT_WAITING_NO;
- }
- _futex_unlock(lwp, op_flags);
- err = rt_mutex_take_interruptible(futex->mutex, to);
- if (err == -RT_ETIMEOUT)
- {
- err = -EDEADLK;
- }
- _futex_lock(lwp, op_flags);
- nword = current_thread->tid | FUTEX_WAITERS;
- if (_futex_cmpxchg_value(&cword, uaddr, word, nword))
- {
- err = -EAGAIN;
- }
- _futex_unlock(lwp, op_flags);
- return err;
- }
- static long _futex_unlock_pi(rt_futex_t futex, struct rt_lwp *lwp, int op_flags)
- {
- rt_err_t err = 0;
- _futex_lock(lwp, op_flags);
- if (!futex->mutex)
- {
- _futex_unlock(lwp, op_flags);
- return -EPERM;
- }
- _futex_unlock(lwp, op_flags);
- err = rt_mutex_release(futex->mutex);
- return err;
- }
- #include <syscall_generic.h>
- rt_inline rt_bool_t _timeout_ignored(int op)
- {
- /**
- * if (op &
- * (FUTEX_WAKE|FUTEX_FD|FUTEX_WAKE_BITSET|FUTEX_TRYLOCK_PI|FUTEX_UNLOCK_PI))
- * was TRUE `timeout` should be ignored by implementation, according to
- * POSIX futex(2) manual. since only FUTEX_WAKE is implemented in rt-smart,
- * only FUTEX_WAKE was omitted currently
- */
- return ((op & (FUTEX_WAKE)) || (op & (FUTEX_REQUEUE)) ||
- (op & (FUTEX_CMP_REQUEUE)) || (op & (FUTEX_UNLOCK_PI)) ||
- (op & (FUTEX_TRYLOCK_PI)));
- }
- sysret_t sys_futex(int *uaddr, int op, int val, const struct timespec *timeout,
- int *uaddr2, int val3)
- {
- struct rt_lwp *lwp = RT_NULL;
- sysret_t ret = 0;
- if (!lwp_user_accessable(uaddr, sizeof(int)))
- {
- ret = -EFAULT;
- }
- else if (timeout && !_timeout_ignored(op) &&
- !lwp_user_accessable((void *)timeout, sizeof(struct timespec)))
- {
- ret = -EINVAL;
- }
- else
- {
- lwp = lwp_self();
- ret = lwp_futex(lwp, uaddr, op, val, timeout, uaddr2, val3);
- }
- return ret;
- }
- #define FUTEX_FLAGS (FUTEX_PRIVATE | FUTEX_CLOCK_REALTIME)
- rt_err_t lwp_futex(struct rt_lwp *lwp, int *uaddr, int op, int val,
- const struct timespec *timeout, int *uaddr2, int val3)
- {
- rt_futex_t futex, futex2;
- rt_err_t rc = 0;
- int op_type = op & ~FUTEX_FLAGS;
- int op_flags = op & FUTEX_FLAGS;
- futex = _futex_get(uaddr, lwp, op_flags, &rc);
- if (!rc)
- {
- switch (op_type)
- {
- case FUTEX_WAIT:
- rc = _futex_wait(futex, lwp, uaddr, val, timeout, op_flags);
- break;
- case FUTEX_WAKE:
- rc = _futex_wake(futex, lwp, val, op_flags);
- break;
- case FUTEX_REQUEUE:
- futex2 = _futex_get(uaddr2, lwp, op_flags, &rc);
- if (!rc)
- {
- _futex_lock(lwp, op_flags);
- rc = _futex_requeue(futex, futex2, lwp, val, (long)timeout,
- op_flags);
- _futex_unlock(lwp, op_flags);
- }
- break;
- case FUTEX_CMP_REQUEUE:
- futex2 = _futex_get(uaddr2, lwp, op_flags, &rc);
- _futex_lock(lwp, op_flags);
- if (*uaddr == val3)
- {
- rc = 0;
- }
- else
- {
- rc = -EAGAIN;
- }
- if (rc == 0)
- {
- rc = _futex_requeue(futex, futex2, lwp, val,
- (long)timeout, op_flags);
- }
- _futex_unlock(lwp, op_flags);
- break;
- case FUTEX_LOCK_PI:
- rc = _futex_lock_pi(futex, lwp, uaddr, timeout, op_flags,
- RT_FALSE);
- break;
- case FUTEX_UNLOCK_PI:
- rc = _futex_unlock_pi(futex, lwp, op_flags);
- break;
- case FUTEX_TRYLOCK_PI:
- rc = _futex_lock_pi(futex, lwp, uaddr, 0, op_flags, RT_TRUE);
- break;
- default:
- LOG_W("User require op=%d which is not implemented", op);
- rc = -ENOSYS;
- break;
- }
- }
- return rc;
- }
- rt_inline int _fetch_robust_entry(struct robust_list **entry,
- struct robust_list **head, rt_bool_t *is_pi)
- {
- unsigned long uentry;
- if (!lwp_user_accessable((void *)head, sizeof(*head)))
- {
- return -EFAULT;
- }
- if (lwp_get_from_user(&uentry, (void *)head, sizeof(*head)) !=
- sizeof(*head))
- {
- return -EFAULT;
- }
- *entry = (void *)(uentry & ~1UL);
- *is_pi = uentry & 1;
- return 0;
- }
- static int _handle_futex_death(int *uaddr, rt_thread_t thread, rt_bool_t is_pi,
- rt_bool_t is_pending_op)
- {
- int word, cword = 0, nword;
- rt_err_t rc;
- struct rt_lwp *lwp;
- rt_futex_t futex;
- /* Futex address must be 32bit aligned */
- if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0)
- return -1;
- lwp = thread->lwp;
- retry:
- if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr)))
- {
- return -1;
- }
- if (lwp_get_from_user(&word, (void *)uaddr, sizeof(*uaddr)) !=
- sizeof(*uaddr))
- {
- return -1;
- }
- futex = _futex_get(uaddr, lwp, FUTEX_PRIVATE, &rc);
- if (is_pending_op && !is_pi && !word)
- {
- _futex_wake(futex, lwp, 1, FUTEX_PRIVATE);
- return 0;
- }
- if ((word & FUTEX_TID_MASK) != thread->tid)
- return 0;
- nword = (word & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
- if ((rc = _futex_cmpxchg_value(&cword, uaddr, word, nword)))
- {
- switch (rc)
- {
- case -EFAULT:
- return -1;
- case -EAGAIN:
- rt_schedule();
- goto retry;
- default:
- LOG_W("unknown errno: %d in '%s'", rc, __FUNCTION__);
- return rc;
- }
- }
- if (cword != word)
- goto retry;
- if (!is_pi && (word & FUTEX_WAITERS))
- _futex_wake(futex, lwp, 1, FUTEX_PRIVATE);
- return 0;
- }
- /**
- * Brief: Walk thread->robust_list mark
- * any locks found there dead, and notify any waiters.
- *
- * note: very carefully, it's a userspace list!
- */
- void lwp_futex_exit_robust_list(rt_thread_t thread)
- {
- struct robust_list *entry = RT_NULL;
- struct robust_list *next_entry = RT_NULL;
- struct robust_list *pending = RT_NULL;
- struct robust_list_head *head;
- unsigned int limit = 2048;
- rt_bool_t pi, pip, next_pi;
- unsigned long futex_offset;
- int rc;
- head = thread->robust_list;
- if (head == RT_NULL)
- return;
- if (_fetch_robust_entry(&entry, &head->list.next, &pi))
- return;
- if (!lwp_user_accessable((void *)&head->futex_offset,
- sizeof(head->futex_offset)))
- {
- return;
- }
- if (lwp_get_from_user(&futex_offset, (void *)&head->futex_offset,
- sizeof(head->futex_offset)) !=
- sizeof(head->futex_offset))
- {
- return;
- }
- if (_fetch_robust_entry(&pending, &head->list_op_pending, &pip))
- {
- return;
- }
- while (entry != &head->list)
- {
- rc = _fetch_robust_entry(&next_entry, &entry->next, &next_pi);
- if (entry != pending)
- {
- if (_handle_futex_death((int *)((size_t)entry + futex_offset), thread, pi,
- RT_FALSE))
- return;
- }
- if (rc)
- return;
- entry = next_entry;
- pi = next_pi;
- if (!--limit)
- break;
- }
- if (pending)
- {
- _handle_futex_death((void *)pending + futex_offset, thread, pip,
- RT_TRUE);
- }
- }
|