lwp_futex.c 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202
  1. /*
  2. * Copyright (c) 2006-2025 RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2021/01/02 bernard the first version
  9. * 2023-07-25 Shell Remove usage of rt_hw_interrupt API in the lwp
  10. * Coding style: remove multiple `return` in a routine
  11. * 2023-08-08 Shell Fix return value of futex(wait); Fix ops that only
  12. * FUTEX_PRIVATE is supported currently
  13. * 2023-11-03 Shell Add Support for ~FUTEX_PRIVATE
  14. * 2023-11-16 xqyjlj Add Support for futex requeue and futex pi
  15. */
  16. #define __RT_IPC_SOURCE__
  17. #include "lwp_futex_internal.h"
  18. #include "sys/time.h"
  19. #include <stdatomic.h>
  20. struct rt_mutex _glob_futex;
  21. /**
  22. * @brief Initialize the global futex lock
  23. *
  24. * @return rt_err_t Returns RT_EOK on success, error code on failure
  25. */
  26. rt_err_t lwp_futex_init(void)
  27. {
  28. return rt_mutex_init(&_glob_futex, "glob_ftx", RT_IPC_FLAG_PRIO);
  29. }
  30. /**
  31. * @brief Locks the global futex with specified operation flags
  32. *
  33. * @param[in] lwp Pointer to the lightweight process structure
  34. * @param[in] op_flags Operation flags (e.g., FUTEX_PRIVATE)
  35. *
  36. * @note This function handles locking of futexes, either using process-local locking
  37. * (when FUTEX_PRIVATE flag is set) or global futex locking mechanism.
  38. */
  39. static void _futex_lock(rt_lwp_t lwp, int op_flags)
  40. {
  41. rt_err_t error;
  42. if (op_flags & FUTEX_PRIVATE)
  43. {
  44. LWP_LOCK(lwp);
  45. }
  46. else
  47. {
  48. error = lwp_mutex_take_safe(&_glob_futex, RT_WAITING_FOREVER, 0);
  49. if (error)
  50. {
  51. LOG_E("%s: Should not failed", __func__);
  52. RT_ASSERT(0);
  53. }
  54. }
  55. }
  56. /**
  57. * @brief Unlocks the global futex with specified operation flags
  58. *
  59. * @param[in] lwp Pointer to the lightweight process structure
  60. * @param[in] op_flags Operation flags (e.g., FUTEX_PRIVATE)
  61. *
  62. * @note This function handles unlocking of futexes, either using process-local unlocking
  63. * (when FUTEX_PRIVATE flag is set) or global futex unlocking mechanism.
  64. */
  65. static void _futex_unlock(rt_lwp_t lwp, int op_flags)
  66. {
  67. rt_err_t error;
  68. if (op_flags & FUTEX_PRIVATE)
  69. {
  70. LWP_UNLOCK(lwp);
  71. }
  72. else
  73. {
  74. error = lwp_mutex_release_safe(&_glob_futex);
  75. if (error)
  76. {
  77. LOG_E("%s: Should not failed", __func__);
  78. RT_ASSERT(0);
  79. }
  80. }
  81. }
  82. /**
  83. * @brief Destroys a private futex and releases its resources
  84. *
  85. * @param[in] data Pointer to the futex to be destroyed (cast from void* to rt_futex_t)
  86. *
  87. * @return rt_err_t Returns 0 on success, -1 on failure
  88. *
  89. * @note This function removes the futex from the lwp address search tree,
  90. * deletes its mutex, and frees the futex memory.
  91. */
  92. static rt_err_t _pftx_destroy_locked(void *data)
  93. {
  94. rt_err_t ret = -1;
  95. rt_futex_t futex = (rt_futex_t)data;
  96. if (futex)
  97. {
  98. /**
  99. * Brief: Delete the futex from lwp address_search_head
  100. *
  101. * Note: Critical Section
  102. * - the lwp (READ. share by thread)
  103. * - the lwp address_search_head (RW. protected by caller. for destroy
  104. * routine, it's always safe because it has already taken a write lock
  105. * to the lwp.)
  106. */
  107. lwp_avl_remove(&futex->node,
  108. (struct lwp_avl_struct **)futex->node.data);
  109. /* release object */
  110. if (futex->mutex)
  111. {
  112. rt_mutex_delete(futex->mutex);
  113. futex->mutex = RT_NULL;
  114. }
  115. rt_free(futex);
  116. ret = 0;
  117. }
  118. return ret;
  119. }
  120. /**
  121. * @brief Creates and initializes a private futex
  122. *
  123. * @param[in] uaddr Pointer to the user-space address used as futex key
  124. * @param[in] lwp Pointer to the lightweight process structure
  125. *
  126. * @return rt_futex_t Returns pointer to created futex on success, NULL on failure
  127. *
  128. * @note This function allocates memory, creates a custom object,
  129. * adds it to the lwp user object tree, and initializes the futex structure.
  130. * The created futex will be automatically destroyed when the lwp is freed.
  131. */
  132. static rt_futex_t _pftx_create_locked(int *uaddr, struct rt_lwp *lwp)
  133. {
  134. rt_futex_t futex = RT_NULL;
  135. struct rt_object *obj = RT_NULL;
  136. /**
  137. * Brief: Create a futex under current lwp
  138. *
  139. * Note: Critical Section
  140. * - lwp (READ; share with thread)
  141. */
  142. if (lwp)
  143. {
  144. futex = (rt_futex_t)rt_malloc(sizeof(struct rt_futex));
  145. if (futex)
  146. {
  147. /* Create a Private FuTeX (pftx) */
  148. obj = rt_custom_object_create("pftx", (void *)futex,
  149. _pftx_destroy_locked);
  150. if (!obj)
  151. {
  152. rt_free(futex);
  153. futex = RT_NULL;
  154. }
  155. else
  156. {
  157. /**
  158. * Brief: Add futex to user object tree for resource recycling
  159. *
  160. * Note: Critical Section
  161. * - lwp user object tree (RW; protected by API)
  162. * - futex (if the adding is successful, others can find the
  163. * unready futex. However, only the lwp_free will do this,
  164. * and this is protected by the ref taken by the lwp thread
  165. * that the lwp_free will never execute at the same time)
  166. */
  167. if (lwp_user_object_add(lwp, obj))
  168. {
  169. /* this will call a _pftx_destroy_locked, but that's okay */
  170. rt_object_delete(obj);
  171. rt_free(futex);
  172. futex = RT_NULL;
  173. }
  174. else
  175. {
  176. futex->node.avl_key = (avl_key_t)uaddr;
  177. futex->node.data = &lwp->address_search_head;
  178. futex->custom_obj = obj;
  179. futex->mutex = RT_NULL;
  180. rt_list_init(&(futex->waiting_thread));
  181. /**
  182. * Brief: Insert into futex head
  183. *
  184. * Note: Critical Section
  185. * - lwp address_search_head (RW; protected by caller)
  186. */
  187. lwp_avl_insert(&futex->node, &lwp->address_search_head);
  188. }
  189. }
  190. }
  191. }
  192. return futex;
  193. }
  194. /**
  195. * @brief Gets or creates a private futex for the given user address
  196. *
  197. * @param[in] uaddr User-space address used as futex key
  198. * @param[in] lwp Pointer to the lightweight process structure
  199. * @param[in] op Operation flags (unused in current implementation)
  200. * @param[out] rc Pointer to store the operation result code
  201. *
  202. * @return rt_futex_t Returns pointer to existing or newly created futex on success,
  203. * NULL on failure (with error code stored in rc)
  204. *
  205. * @note If the futex doesn't exist, it creates a new one using _pftx_create_locked.
  206. */
  207. static rt_futex_t _pftx_get(void *uaddr, struct rt_lwp *lwp, int op,
  208. rt_err_t *rc)
  209. {
  210. struct lwp_avl_struct *node = RT_NULL;
  211. rt_futex_t futex = RT_NULL;
  212. rt_err_t error = -1;
  213. LWP_LOCK(lwp);
  214. /**
  215. * Note: Critical Section
  216. * protect lwp address_search_head (READ)
  217. */
  218. node = lwp_avl_find((avl_key_t)uaddr, lwp->address_search_head);
  219. if (node)
  220. {
  221. futex = rt_container_of(node, struct rt_futex, node);
  222. error = 0;
  223. }
  224. else
  225. {
  226. /* create a futex according to this uaddr */
  227. futex = _pftx_create_locked(uaddr, lwp);
  228. if (!futex)
  229. error = -ENOMEM;
  230. else
  231. error = 0;
  232. }
  233. LWP_UNLOCK(lwp);
  234. *rc = error;
  235. return futex;
  236. }
  237. /**
  238. * @brief Destroy a Shared FuTeX (sftx)
  239. *
  240. * @param[in] data Pointer to the futex to be destroyed (cast from void* to rt_futex_t)
  241. *
  242. * @return RT_EOK (0) on success, -1 on failure
  243. *
  244. * @note This function:
  245. * - Deletes the futex from global table.
  246. * - Deletes and nullifies the futex mutex.
  247. * - Frees the futex memory.
  248. */
  249. static rt_err_t _sftx_destroy(void *data)
  250. {
  251. rt_err_t ret = -1;
  252. rt_futex_t futex = (rt_futex_t)data;
  253. if (futex)
  254. {
  255. /* delete it even it's not in the table */
  256. futex_global_table_delete(&futex->entry.key);
  257. if (futex->mutex)
  258. {
  259. rt_mutex_delete(futex->mutex);
  260. futex->mutex = RT_NULL;
  261. }
  262. rt_free(futex);
  263. ret = 0;
  264. }
  265. return ret;
  266. }
  267. /**
  268. * @brief Create a Shared FuTeX (sftx)
  269. *
  270. * @param[in] key Pointer to shared futex key structure
  271. * @param[in] lwp Pointer to lightweight process structure
  272. *
  273. * @return Pointer to created futex on success, NULL on failure
  274. *
  275. * @note This function:
  276. * - Allocates memory for new futex
  277. * - Creates custom object with _sftx_destroy as destructor
  278. * - Adds futex to global table
  279. * - Initializes futex members (mutex, waiting_thread list)
  280. */
  281. static rt_futex_t _sftx_create(struct shared_futex_key *key, struct rt_lwp *lwp)
  282. {
  283. rt_futex_t futex = RT_NULL;
  284. struct rt_object *obj = RT_NULL;
  285. if (lwp)
  286. {
  287. futex = (rt_futex_t)rt_calloc(1, sizeof(struct rt_futex));
  288. if (futex)
  289. {
  290. /* create a Shared FuTeX (sftx) */
  291. obj = rt_custom_object_create("sftx", (void *)futex, _sftx_destroy);
  292. if (!obj)
  293. {
  294. rt_free(futex);
  295. futex = RT_NULL;
  296. }
  297. else
  298. {
  299. if (futex_global_table_add(key, futex))
  300. {
  301. rt_object_delete(obj);
  302. rt_free(futex);
  303. futex = RT_NULL;
  304. }
  305. else
  306. {
  307. futex->mutex = RT_NULL;
  308. rt_list_init(&(futex->waiting_thread));
  309. futex->custom_obj = obj;
  310. }
  311. }
  312. }
  313. }
  314. return futex;
  315. }
  316. /**
  317. * @brief Get or create a Shared FuTeX (sftx) for given user address
  318. *
  319. * @param[in] uaddr User-space address to lookup/create futex for
  320. * @param[in] lwp Pointer to lightweight process structure
  321. * @param[in] op Operation flags (e.g. FUTEX_PRIVATE)
  322. * @param[out] rc Pointer to store result code (0 on success, error code on failure)
  323. *
  324. * @return Pointer to existing/new futex on success, NULL on failure
  325. *
  326. * @note This function:
  327. * - Queries address space for valid varea
  328. * - Constructs shared futex key from varea info
  329. * - Locks global futex table
  330. * - Looks up existing futex or creates new one if not found
  331. * - Unlocks global futex table
  332. * - Returns futex pointer and sets result code
  333. */
  334. static rt_futex_t _sftx_get(void *uaddr, struct rt_lwp *lwp, int op,
  335. rt_err_t *rc)
  336. {
  337. rt_futex_t futex = RT_NULL;
  338. struct shared_futex_key key;
  339. rt_varea_t varea;
  340. rt_err_t error = -1;
  341. RD_LOCK(lwp->aspace);
  342. varea = rt_aspace_query(lwp->aspace, uaddr);
  343. if (varea)
  344. {
  345. key.mobj = varea->mem_obj;
  346. key.offset = ((varea->offset) << MM_PAGE_SHIFT) |
  347. ((long)uaddr & ((1 << MM_PAGE_SHIFT) - 1));
  348. RD_UNLOCK(lwp->aspace);
  349. /* query for the key */
  350. _futex_lock(lwp, op & ~FUTEX_PRIVATE);
  351. error = futex_global_table_find(&key, &futex);
  352. if (error != RT_EOK)
  353. {
  354. /* not found, do allocation */
  355. futex = _sftx_create(&key, lwp);
  356. if (!futex)
  357. error = -ENOMEM;
  358. else
  359. error = 0;
  360. }
  361. _futex_unlock(lwp, op & ~FUTEX_PRIVATE);
  362. }
  363. else
  364. {
  365. RD_UNLOCK(lwp->aspace);
  366. }
  367. *rc = error;
  368. return futex;
  369. }
  370. /**
  371. * @brief Get a futex (private or shared) for given user address
  372. *
  373. * @param[in] uaddr User-space address to lookup futex for
  374. * @param[in] lwp Pointer to lightweight process structure
  375. * @param[in] op_flags Operation flags (FUTEX_PRIVATE for private futex)
  376. * @param[out] rc Pointer to store result code (0 on success, error code on failure)
  377. *
  378. * @return Pointer to existing/new futex on success, NULL on failure
  379. *
  380. * @note This function routes the request to either:
  381. * - _pftx_get for private futexes (FUTEX_PRIVATE flag set)
  382. * - _sftx_get for shared futexes (FUTEX_PRIVATE flag not set)
  383. */
  384. static rt_futex_t _futex_get(void *uaddr, struct rt_lwp *lwp, int op_flags,
  385. rt_err_t *rc)
  386. {
  387. rt_futex_t futex = RT_NULL;
  388. if (op_flags & FUTEX_PRIVATE)
  389. {
  390. futex = _pftx_get(uaddr, lwp, op_flags, rc);
  391. }
  392. else
  393. {
  394. futex = _sftx_get(uaddr, lwp, op_flags, rc);
  395. }
  396. return futex;
  397. }
  398. /**
  399. * @brief Suspend a thread with timeout and add it to futex waiting list
  400. *
  401. * @param[in] thread Thread to suspend
  402. * @param[in] futex Futex to add thread to waiting list
  403. * @param[in] timeout Timeout value in ticks
  404. *
  405. * @return RT_EOK on success, error code on failure
  406. *
  407. * @note This function:
  408. * - Adds thread to futex's waiting_thread list (FIFO order)
  409. * - Sets thread timer with specified timeout
  410. * - Sets errno to ETIMEDOUT on success
  411. */
  412. static rt_err_t _suspend_thread_timeout_locked(rt_thread_t thread,
  413. rt_futex_t futex,
  414. rt_tick_t timeout)
  415. {
  416. rt_err_t rc;
  417. /**
  418. * Brief: Add current thread into futex waiting thread list
  419. *
  420. * Note: Critical Section
  421. * - the futex waiting_thread list (RW)
  422. */
  423. rc = rt_thread_suspend_to_list(thread, &futex->waiting_thread,
  424. RT_IPC_FLAG_FIFO, RT_INTERRUPTIBLE);
  425. if (rc == RT_EOK)
  426. {
  427. /* start the timer of thread */
  428. rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME,
  429. &timeout);
  430. rt_timer_start(&(thread->thread_timer));
  431. rt_set_errno(ETIMEDOUT);
  432. }
  433. return rc;
  434. }
  435. /**
  436. * @brief Add current thread into futex waiting thread list
  437. *
  438. * @param[in] thread The thread to be suspended
  439. * @param[in] futex The futex to add thread to waiting list
  440. *
  441. * @return rt_err_t Returns RT_EOK on success or error code on failure
  442. */
  443. static rt_err_t _suspend_thread_locked(rt_thread_t thread, rt_futex_t futex)
  444. {
  445. /**
  446. * Brief: Add current thread into futex waiting thread list
  447. *
  448. * Note: Critical Section
  449. * - the futex waiting_thread list (RW)
  450. */
  451. return rt_thread_suspend_to_list(thread, &futex->waiting_thread,
  452. RT_IPC_FLAG_FIFO, RT_INTERRUPTIBLE);
  453. }
  454. /**
  455. * @brief Compare and exchange futex value atomically
  456. *
  457. * @param[out] curval Pointer to store the current value if exchange fails
  458. * @param[in] uaddr User-space address of the futex value
  459. * @param[in] uval Expected value to compare against
  460. * @param[in] newval New value to set if comparison succeeds
  461. *
  462. * @return int Returns 0 on success, -EFAULT if address is inaccessible,
  463. * or -EAGAIN if comparison fails
  464. *
  465. * @note Checks user address accessibility before operation
  466. */
  467. rt_inline int _futex_cmpxchg_value(int *curval, int *uaddr, int uval,
  468. int newval)
  469. {
  470. int err = 0;
  471. if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr)))
  472. {
  473. err = -EFAULT;
  474. goto exit;
  475. }
  476. if (!atomic_compare_exchange_strong(uaddr, &uval, newval))
  477. {
  478. *curval = uval;
  479. err = -EAGAIN;
  480. }
  481. exit:
  482. return err;
  483. }
  484. /**
  485. * @brief Wait on a futex if its value matches the expected value.
  486. *
  487. * @param[in] futex The futex to wait on
  488. * @param[in] lwp Lightweight process structure
  489. * @param[in] uaddr User-space address of futex value
  490. * @param[in] value Expected value to compare against
  491. * @param[in] timeout Optional timeout specification
  492. * @param[in] op_flags Operation flags (e.g. FUTEX_PRIVATE)
  493. *
  494. * @return int Returns 0 on success, negative error code on failure
  495. * - ETIMEDOUT if timeout expires
  496. * - EAGAIN if value does not match
  497. */
  498. static int _futex_wait(rt_futex_t futex, struct rt_lwp *lwp, int *uaddr,
  499. int value, const struct timespec *timeout, int op_flags)
  500. {
  501. rt_tick_t to;
  502. rt_thread_t thread;
  503. rt_err_t rc = -RT_EINTR;
  504. /**
  505. * Brief: Remove current thread from scheduler, besides appends it to
  506. * the waiting thread list of the futex. If the timeout is specified
  507. * a timer will be setup for current thread
  508. *
  509. * Note: Critical Section
  510. * - futex.waiting (RW; Protected by lwp_lock)
  511. * - the local cpu
  512. */
  513. _futex_lock(lwp, op_flags);
  514. if (*uaddr == value)
  515. {
  516. thread = rt_thread_self();
  517. if (timeout)
  518. {
  519. to = timeout->tv_sec * RT_TICK_PER_SECOND;
  520. to +=
  521. (timeout->tv_nsec * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND;
  522. if (to < 0)
  523. {
  524. rc = -EINVAL;
  525. _futex_unlock(lwp, op_flags);
  526. }
  527. else
  528. {
  529. rt_enter_critical();
  530. rc = _suspend_thread_timeout_locked(thread, futex, to);
  531. _futex_unlock(lwp, op_flags);
  532. rt_exit_critical();
  533. }
  534. }
  535. else
  536. {
  537. rt_enter_critical();
  538. rc = _suspend_thread_locked(thread, futex);
  539. _futex_unlock(lwp, op_flags);
  540. rt_exit_critical();
  541. }
  542. if (rc == RT_EOK)
  543. {
  544. /* do schedule */
  545. rt_schedule();
  546. /* check errno */
  547. rc = rt_get_errno();
  548. rc = rc > 0 ? -rc : rc;
  549. }
  550. }
  551. else
  552. {
  553. _futex_unlock(lwp, op_flags);
  554. rc = -EAGAIN;
  555. rt_set_errno(EAGAIN);
  556. }
  557. return rc;
  558. }
  559. /**
  560. * @brief Wake up suspended threads from futex waiting list
  561. *
  562. * @param[in] futex The futex containing waiting threads
  563. * @param[in] lwp Lightweight process structure
  564. * @param[in] number Maximum number of threads to wake up
  565. * @param[in] op_flags Operation flags (e.g. FUTEX_PRIVATE)
  566. *
  567. * @return long Number of threads actually woken up
  568. *
  569. * @note The actual number of woken threads may be less than 'number'
  570. * (e.g., if fewer threads are waiting).
  571. * It performs a schedule after waking threads.
  572. */
  573. static long _futex_wake(rt_futex_t futex, struct rt_lwp *lwp, int number,
  574. int op_flags)
  575. {
  576. long woken_cnt = 0;
  577. int is_empty = 0;
  578. /**
  579. * Brief: Wakeup a suspended thread on the futex waiting thread list
  580. *
  581. * Note: Critical Section
  582. * - the futex waiting_thread list (RW)
  583. */
  584. while (number && !is_empty)
  585. {
  586. _futex_lock(lwp, op_flags);
  587. if (rt_susp_list_dequeue(&futex->waiting_thread, RT_EOK))
  588. {
  589. number--;
  590. woken_cnt++;
  591. is_empty = RT_FALSE;
  592. }
  593. else
  594. {
  595. is_empty = RT_TRUE;
  596. }
  597. _futex_unlock(lwp, op_flags);
  598. }
  599. /* do schedule */
  600. rt_schedule();
  601. return woken_cnt;
  602. }
  603. /**
  604. * @brief Requeue threads from one futex waiting list to another
  605. *
  606. * @param[in] futex1 Source futex containing threads to wake/requeue
  607. * @param[in] futex2 Destination futex for requeued threads
  608. * @param[in] lwp Lightweight process structure
  609. * @param[in] nr_wake Maximum number of threads to wake from futex1
  610. * @param[in] nr_requeue Maximum number of threads to requeue to futex2
  611. * @param[in] opflags Operation flags (e.g. FUTEX_PRIVATE)
  612. *
  613. * @return long Number of threads actually woken and requeued
  614. *
  615. * @note Wake up to nr_wake futex1 threads.
  616. * If there are more waiters waiting on futex1 than nr_wake,
  617. * insert the remaining at most nr_requeue waiters waiting
  618. * on futex1 into the waiting queue of futex2.
  619. */
  620. static long _futex_requeue(rt_futex_t futex1, rt_futex_t futex2,
  621. struct rt_lwp *lwp, int nr_wake, int nr_requeue,
  622. int opflags)
  623. {
  624. long rtn;
  625. long woken_cnt = 0;
  626. int is_empty = 0;
  627. rt_thread_t thread;
  628. if (futex1 == futex2)
  629. {
  630. return -EINVAL;
  631. }
  632. /**
  633. * Brief: Wakeup a suspended thread on the futex waiting thread list
  634. *
  635. * Note: Critical Section
  636. * - the futex waiting_thread list (RW)
  637. */
  638. while (nr_wake && !is_empty)
  639. {
  640. if (rt_susp_list_dequeue(&futex1->waiting_thread, RT_EOK))
  641. {
  642. nr_wake--;
  643. woken_cnt++;
  644. is_empty = RT_FALSE;
  645. }
  646. else
  647. {
  648. is_empty = RT_TRUE;
  649. }
  650. }
  651. rtn = woken_cnt;
  652. /**
  653. * Brief: Requeue
  654. *
  655. * Note: Critical Section
  656. * - the futex waiting_thread list (RW)
  657. */
  658. while (!is_empty && nr_requeue)
  659. {
  660. rt_sched_lock_level_t slvl;
  661. rt_sched_lock(&slvl);
  662. /* moving from one susp list to another */
  663. is_empty = rt_list_isempty(&(futex1->waiting_thread));
  664. if (!is_empty)
  665. {
  666. thread = RT_THREAD_LIST_NODE_ENTRY(futex1->waiting_thread.next);
  667. rt_list_remove(&RT_THREAD_LIST_NODE(thread));
  668. rt_list_insert_before(&(futex2->waiting_thread),
  669. &RT_THREAD_LIST_NODE(thread));
  670. nr_requeue--;
  671. rtn++;
  672. }
  673. rt_sched_unlock(slvl);
  674. }
  675. /* do schedule */
  676. rt_schedule();
  677. return rtn;
  678. }
  679. /**
  680. * @brief Lock a futex with priority inheritance (PI)
  681. *
  682. * @param[in] futex Futex object to lock
  683. * @param[in] lwp Lightweight process structure
  684. * @param[in] uaddr User-space address of futex value
  685. * @param[in] timeout Optional timeout specification (CLOCK_REALTIME)
  686. * @param[in] op_flags Operation flags (e.g. FUTEX_PRIVATE)
  687. * @param[in] trylock If true, performs non-blocking trylock operation
  688. *
  689. * @return long 0 on success, negative error code on failure
  690. *
  691. * @note Critical sections:
  692. * - Accesses futex value atomically using cmpxchg
  693. * - Uses mutex with priority inheritance for waiting
  694. */
  695. static long _futex_lock_pi(rt_futex_t futex, struct rt_lwp *lwp, int *uaddr,
  696. const struct timespec *timeout, int op_flags,
  697. rt_bool_t trylock)
  698. {
  699. int word = 0, nword, cword;
  700. int tid = 0;
  701. rt_err_t err = 0;
  702. rt_thread_t thread = RT_NULL, current_thread = RT_NULL;
  703. rt_tick_t to = RT_WAITING_FOREVER;
  704. if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr)))
  705. {
  706. return -EFAULT;
  707. }
  708. current_thread = rt_thread_self();
  709. _futex_lock(lwp, op_flags);
  710. lwp_get_from_user(&word, (void *)uaddr, sizeof(int));
  711. tid = word & FUTEX_TID_MASK;
  712. if (word == 0)
  713. {
  714. /* If the value is 0, then the kernel tries
  715. to atomically set the futex value to the caller's TID. */
  716. nword = current_thread->tid;
  717. if (_futex_cmpxchg_value(&cword, uaddr, word, nword))
  718. {
  719. _futex_unlock(lwp, op_flags);
  720. return -EAGAIN;
  721. }
  722. _futex_unlock(lwp, op_flags);
  723. return 0;
  724. }
  725. else
  726. {
  727. thread = lwp_tid_get_thread_and_inc_ref(tid);
  728. if (thread == RT_NULL)
  729. {
  730. _futex_unlock(lwp, op_flags);
  731. return -ESRCH;
  732. }
  733. lwp_tid_dec_ref(thread);
  734. nword =
  735. word | FUTEX_WAITERS;
  736. if (_futex_cmpxchg_value(&cword, uaddr, word, nword))
  737. {
  738. _futex_unlock(lwp, op_flags);
  739. return -EAGAIN;
  740. }
  741. word = nword;
  742. }
  743. if (futex->mutex == RT_NULL)
  744. {
  745. futex->mutex = rt_mutex_create("futexpi", RT_IPC_FLAG_PRIO);
  746. if (futex->mutex == RT_NULL)
  747. {
  748. _futex_unlock(lwp, op_flags);
  749. return -ENOMEM;
  750. }
  751. /* set mutex->owner */
  752. rt_spin_lock(&(futex->mutex->spinlock));
  753. futex->mutex->owner = thread;
  754. futex->mutex->hold = 1;
  755. rt_spin_unlock(&(futex->mutex->spinlock));
  756. }
  757. if (timeout)
  758. {
  759. to = rt_timespec_to_tick(timeout);
  760. }
  761. if (trylock)
  762. {
  763. to = RT_WAITING_NO;
  764. }
  765. _futex_unlock(lwp, op_flags);
  766. err = rt_mutex_take_interruptible(futex->mutex, to);
  767. if (err == -RT_ETIMEOUT)
  768. {
  769. err = -EDEADLK;
  770. }
  771. _futex_lock(lwp, op_flags);
  772. nword = current_thread->tid | FUTEX_WAITERS;
  773. if (_futex_cmpxchg_value(&cword, uaddr, word, nword))
  774. {
  775. err = -EAGAIN;
  776. }
  777. _futex_unlock(lwp, op_flags);
  778. return err;
  779. }
  780. /**
  781. * @brief Releases a priority inheritance futex lock
  782. *
  783. * @param[in] futex The futex object to unlock
  784. * @param[in] lwp The lightweight process structure
  785. * @param[in] op_flags Operation flags for the futex
  786. *
  787. * @return Returns 0 on success, or negative error code on failure
  788. * -EPERM If the futex mutex is not initialized
  789. */
  790. static long _futex_unlock_pi(rt_futex_t futex, struct rt_lwp *lwp, int op_flags)
  791. {
  792. rt_err_t err = 0;
  793. _futex_lock(lwp, op_flags);
  794. if (!futex->mutex)
  795. {
  796. _futex_unlock(lwp, op_flags);
  797. return -EPERM;
  798. }
  799. _futex_unlock(lwp, op_flags);
  800. err = rt_mutex_release(futex->mutex);
  801. return err;
  802. }
  803. #include <syscall_generic.h>
  804. rt_inline rt_bool_t _timeout_ignored(int op)
  805. {
  806. /**
  807. * if (op &
  808. * (FUTEX_WAKE|FUTEX_FD|FUTEX_WAKE_BITSET|FUTEX_TRYLOCK_PI|FUTEX_UNLOCK_PI))
  809. * was TRUE `timeout` should be ignored by implementation, according to
  810. * POSIX futex(2) manual. since only FUTEX_WAKE is implemented in rt-smart,
  811. * only FUTEX_WAKE was omitted currently
  812. */
  813. return ((op & (FUTEX_WAKE)) || (op & (FUTEX_REQUEUE)) ||
  814. (op & (FUTEX_CMP_REQUEUE)) || (op & (FUTEX_UNLOCK_PI)) ||
  815. (op & (FUTEX_TRYLOCK_PI)));
  816. }
  817. /**
  818. * @brief System call interface for futex operations
  819. *
  820. * @param[in] uaddr Pointer to the futex word in user space
  821. * @param[in] op Futex operation code
  822. * @param[in] val Operation-specific value
  823. * @param[in] timeout Pointer to timeout specification (can be NULL)
  824. * @param[in] uaddr2 Second futex word pointer for certain operations
  825. * @param[in] val3 Third operation-specific value
  826. *
  827. * @return System call return value
  828. * - 0 on success
  829. * -EFAULT if uaddr is not accessible
  830. * -EINVAL if timeout is invalid or not accessible
  831. *
  832. * @note This function provides the user-space interface for futex operations,
  833. * performing necessary access checks before delegating to the LWP futex handler.
  834. */
  835. sysret_t sys_futex(int *uaddr, int op, int val, const struct timespec *timeout,
  836. int *uaddr2, int val3)
  837. {
  838. struct rt_lwp *lwp = RT_NULL;
  839. sysret_t ret = 0;
  840. if (!lwp_user_accessable(uaddr, sizeof(int)))
  841. {
  842. ret = -EFAULT;
  843. }
  844. else if (timeout && !_timeout_ignored(op) &&
  845. !lwp_user_accessable((void *)timeout, sizeof(struct timespec)))
  846. {
  847. ret = -EINVAL;
  848. }
  849. else
  850. {
  851. lwp = lwp_self();
  852. ret = lwp_futex(lwp, uaddr, op, val, timeout, uaddr2, val3);
  853. }
  854. return ret;
  855. }
  856. #define FUTEX_FLAGS (FUTEX_PRIVATE | FUTEX_CLOCK_REALTIME)
  857. /**
  858. * @brief Main futex operation handler for lightweight processes
  859. *
  860. * @param[in] lwp The lightweight process structure
  861. * @param[in] uaddr Pointer to the futex word in user space
  862. * @param[in] op Futex operation code (type + flags)
  863. * @param[in] val Operation-specific value
  864. * @param[in] timeout Pointer to timeout specification (can be NULL)
  865. * @param[in] uaddr2 Second futex word pointer for certain operations
  866. * @param[in] val3 Third operation-specific value (used for comparison)
  867. *
  868. * @return Operation result
  869. * - 0 on success
  870. * - Negative error code on failure
  871. *
  872. * @note This function handles all futex operations by dispatching to appropriate
  873. * futex sub-functions based on the operation type. It performs basic validation
  874. * and manages futex objects.
  875. */
  876. rt_err_t lwp_futex(struct rt_lwp *lwp, int *uaddr, int op, int val,
  877. const struct timespec *timeout, int *uaddr2, int val3)
  878. {
  879. rt_futex_t futex, futex2;
  880. rt_err_t rc = 0;
  881. int op_type = op & ~FUTEX_FLAGS;
  882. int op_flags = op & FUTEX_FLAGS;
  883. futex = _futex_get(uaddr, lwp, op_flags, &rc);
  884. if (!rc)
  885. {
  886. switch (op_type)
  887. {
  888. case FUTEX_WAIT:
  889. rc = _futex_wait(futex, lwp, uaddr, val, timeout, op_flags);
  890. break;
  891. case FUTEX_WAKE:
  892. rc = _futex_wake(futex, lwp, val, op_flags);
  893. break;
  894. case FUTEX_REQUEUE:
  895. futex2 = _futex_get(uaddr2, lwp, op_flags, &rc);
  896. if (!rc)
  897. {
  898. _futex_lock(lwp, op_flags);
  899. rc = _futex_requeue(futex, futex2, lwp, val, (long)timeout,
  900. op_flags);
  901. _futex_unlock(lwp, op_flags);
  902. }
  903. break;
  904. case FUTEX_CMP_REQUEUE:
  905. futex2 = _futex_get(uaddr2, lwp, op_flags, &rc);
  906. _futex_lock(lwp, op_flags);
  907. if (*uaddr == val3)
  908. {
  909. rc = 0;
  910. }
  911. else
  912. {
  913. rc = -EAGAIN;
  914. }
  915. if (rc == 0)
  916. {
  917. rc = _futex_requeue(futex, futex2, lwp, val,
  918. (long)timeout, op_flags);
  919. }
  920. _futex_unlock(lwp, op_flags);
  921. break;
  922. case FUTEX_LOCK_PI:
  923. rc = _futex_lock_pi(futex, lwp, uaddr, timeout, op_flags,
  924. RT_FALSE);
  925. break;
  926. case FUTEX_UNLOCK_PI:
  927. rc = _futex_unlock_pi(futex, lwp, op_flags);
  928. break;
  929. case FUTEX_TRYLOCK_PI:
  930. rc = _futex_lock_pi(futex, lwp, uaddr, 0, op_flags, RT_TRUE);
  931. break;
  932. default:
  933. LOG_W("User require op=%d which is not implemented", op);
  934. rc = -ENOSYS;
  935. break;
  936. }
  937. }
  938. return rc;
  939. }
  940. /**
  941. * @brief Fetches a robust futex list entry from user space
  942. *
  943. * @param[out] entry Pointer to store the retrieved robust list entry
  944. * @param[in] head Pointer to the head of the robust list in user space
  945. * @param[out] is_pi Pointer to store the PI flag status
  946. *
  947. * @return Operation status
  948. * - 0 on success
  949. * - EFAULT if user space access fails
  950. *
  951. * @note This helper function safely retrieves a robust futex list entry pointer
  952. * from user space and extracts the PI (Priority Inheritance) flag.
  953. */
  954. rt_inline int _fetch_robust_entry(struct robust_list **entry,
  955. struct robust_list **head, rt_bool_t *is_pi)
  956. {
  957. unsigned long uentry;
  958. if (!lwp_user_accessable((void *)head, sizeof(*head)))
  959. {
  960. return -EFAULT;
  961. }
  962. if (lwp_get_from_user(&uentry, (void *)head, sizeof(*head)) !=
  963. sizeof(*head))
  964. {
  965. return -EFAULT;
  966. }
  967. *entry = (void *)(uentry & ~1UL);
  968. *is_pi = uentry & 1;
  969. return 0;
  970. }
  971. /**
  972. * @brief Handles futex cleanup when a thread dies
  973. *
  974. * @param[in] uaddr Pointer to the futex word in user space
  975. * @param[in] thread The thread that is terminating
  976. * @param[in] is_pi Flag indicating if this is a priority inheritance futex
  977. * @param[in] is_pending_op Flag indicating if there are pending operations
  978. *
  979. * @return Operation status
  980. * - 0 on success
  981. * - -1 on invalid address or access failure
  982. * - Other negative error codes for specific failures
  983. *
  984. * @note This function performs cleanup operations for futexes when a thread terminates,
  985. * including marking the owner as dead and waking any waiting threads if necessary.
  986. */
  987. static int _handle_futex_death(int *uaddr, rt_thread_t thread, rt_bool_t is_pi,
  988. rt_bool_t is_pending_op)
  989. {
  990. int word, cword = 0, nword;
  991. rt_err_t rc;
  992. struct rt_lwp *lwp;
  993. rt_futex_t futex;
  994. /* Futex address must be 32bit aligned */
  995. if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0)
  996. return -1;
  997. lwp = thread->lwp;
  998. retry:
  999. if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr)))
  1000. {
  1001. return -1;
  1002. }
  1003. if (lwp_get_from_user(&word, (void *)uaddr, sizeof(*uaddr)) !=
  1004. sizeof(*uaddr))
  1005. {
  1006. return -1;
  1007. }
  1008. futex = _futex_get(uaddr, lwp, FUTEX_PRIVATE, &rc);
  1009. if (is_pending_op && !is_pi && !word)
  1010. {
  1011. _futex_wake(futex, lwp, 1, FUTEX_PRIVATE);
  1012. return 0;
  1013. }
  1014. if ((word & FUTEX_TID_MASK) != thread->tid)
  1015. return 0;
  1016. nword = (word & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
  1017. if ((rc = _futex_cmpxchg_value(&cword, uaddr, word, nword)))
  1018. {
  1019. switch (rc)
  1020. {
  1021. case -EFAULT:
  1022. return -1;
  1023. case -EAGAIN:
  1024. rt_schedule();
  1025. goto retry;
  1026. default:
  1027. LOG_W("unknown errno: %d in '%s'", rc, __FUNCTION__);
  1028. return rc;
  1029. }
  1030. }
  1031. if (cword != word)
  1032. goto retry;
  1033. if (!is_pi && (word & FUTEX_WAITERS))
  1034. _futex_wake(futex, lwp, 1, FUTEX_PRIVATE);
  1035. return 0;
  1036. }
  1037. /**
  1038. * @brief Handle thread exit cleanup for robust futex list and notify waiters.
  1039. *
  1040. * @param[in] thread The exiting thread containing the robust list
  1041. *
  1042. * @note This function is called during thread termination to ensure
  1043. * proper cleanup of futexes owned by the exiting thread.
  1044. * It implements the robust futex mechanism to prevent deadlocks
  1045. * when threads terminate while holding futex locks.
  1046. * It handles both the main robust list and any pending operations.
  1047. * Do it very carefully, it's a userspace list!
  1048. */
  1049. void lwp_futex_exit_robust_list(rt_thread_t thread)
  1050. {
  1051. struct robust_list *entry = RT_NULL;
  1052. struct robust_list *next_entry = RT_NULL;
  1053. struct robust_list *pending = RT_NULL;
  1054. struct robust_list_head *head;
  1055. unsigned int limit = 2048;
  1056. rt_bool_t pi, pip, next_pi;
  1057. unsigned long futex_offset;
  1058. int rc;
  1059. head = thread->robust_list;
  1060. if (head == RT_NULL)
  1061. return;
  1062. if (_fetch_robust_entry(&entry, &head->list.next, &pi))
  1063. return;
  1064. if (!lwp_user_accessable((void *)&head->futex_offset,
  1065. sizeof(head->futex_offset)))
  1066. {
  1067. return;
  1068. }
  1069. if (lwp_get_from_user(&futex_offset, (void *)&head->futex_offset,
  1070. sizeof(head->futex_offset)) !=
  1071. sizeof(head->futex_offset))
  1072. {
  1073. return;
  1074. }
  1075. if (_fetch_robust_entry(&pending, &head->list_op_pending, &pip))
  1076. {
  1077. return;
  1078. }
  1079. while (entry != &head->list)
  1080. {
  1081. rc = _fetch_robust_entry(&next_entry, &entry->next, &next_pi);
  1082. if (entry != pending)
  1083. {
  1084. if (_handle_futex_death((int *)((size_t)entry + futex_offset), thread, pi,
  1085. RT_FALSE))
  1086. return;
  1087. }
  1088. if (rc)
  1089. return;
  1090. entry = next_entry;
  1091. pi = next_pi;
  1092. if (!--limit)
  1093. break;
  1094. }
  1095. if (pending)
  1096. {
  1097. _handle_futex_death((void *)pending + futex_offset, thread, pip,
  1098. RT_TRUE);
  1099. }
  1100. }