timerfd.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. /*
  2. * Copyright (c) 2006-2024 RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2023-09-20 zmq810150896 first version
  9. */
  10. #include <rtthread.h>
  11. #include <dfs_file.h>
  12. #include <stdint.h>
  13. #include <poll.h>
  14. #include <sys/timerfd.h>
  15. #define DBG_TAG "TIMERFD"
  16. #define DBG_LVL DBG_INFO
  17. #include <rtdbg.h>
  18. #define INIT_PERIODIC 0
  19. #define OPEN_PERIODIC 1
  20. #define ENTER_PERIODIC 2
  21. #define SEC_TO_MSEC 1000
  22. #define MSEC_TO_NSEC 1000000
  23. #define SEC_TO_NSEC 1000000000
  24. #define TIME_INT32_MAX 0x7FFFFFFF
  25. #define TIMERFD_MUTEX_NAME "TIMERFD"
  26. #define TFD_SHARED_FCNTL_FLAGS (TFD_CLOEXEC | TFD_NONBLOCK)
  27. struct rt_timerfd
  28. {
  29. rt_wqueue_t timerfd_queue;
  30. struct itimerspec ittimer;
  31. rt_timer_t timer;
  32. struct rt_mutex lock;
  33. struct timespec pre_time;
  34. rt_atomic_t timeout_num;
  35. struct rt_wqueue_node wqn;
  36. rt_atomic_t ticks;
  37. int clockid;
  38. int isperiodic;
  39. int tick_out;
  40. };
  41. static int timerfd_close(struct dfs_file *file);
  42. static int timerfd_poll(struct dfs_file *file, struct rt_pollreq *req);
  43. #ifndef RT_USING_DFS_V2
  44. static ssize_t timerfd_read(struct dfs_file *file, void *buf, size_t count);
  45. #else
  46. static ssize_t timerfd_read(struct dfs_file *file, void *buf, size_t count, off_t *pos);
  47. #endif
  48. static const struct dfs_file_ops timerfd_fops =
  49. {
  50. .close = timerfd_close,
  51. .poll = timerfd_poll,
  52. .read = timerfd_read,
  53. };
  54. static int timerfd_close(struct dfs_file *file)
  55. {
  56. struct rt_timerfd *tfd;
  57. if (file->vnode->ref_count != 1)
  58. return 0;
  59. tfd = file->vnode->data;
  60. if (tfd)
  61. {
  62. if (tfd->timer != RT_NULL)
  63. {
  64. rt_timer_stop(tfd->timer);
  65. rt_timer_delete(tfd->timer);
  66. tfd->timer = RT_NULL;
  67. }
  68. if (tfd->wqn.wqueue)
  69. {
  70. rt_wqueue_remove(&tfd->wqn);
  71. }
  72. rt_mutex_detach(&tfd->lock);
  73. rt_free(tfd);
  74. }
  75. return 0;
  76. }
  77. static int timerfd_poll(struct dfs_file *file, struct rt_pollreq *req)
  78. {
  79. struct rt_timerfd *tfd;
  80. int events = 0;
  81. tfd = file->vnode->data;
  82. rt_mutex_take(&tfd->lock, RT_WAITING_FOREVER);
  83. rt_poll_add(&tfd->timerfd_queue, req);
  84. rt_mutex_release(&tfd->lock);
  85. if (rt_atomic_load(&(tfd->ticks)) > 0)
  86. {
  87. events |= POLLIN;
  88. }
  89. return events;
  90. }
  91. #ifndef RT_USING_DFS_V2
  92. static ssize_t timerfd_read(struct dfs_file *file, void *buf, size_t count)
  93. #else
  94. static ssize_t timerfd_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
  95. #endif
  96. {
  97. struct rt_timerfd *tfd;
  98. rt_uint64_t *buffer;
  99. int ret = 0;
  100. buffer = (rt_uint64_t *)buf;
  101. if (sizeof(buffer) > count)
  102. {
  103. rt_set_errno(EINVAL);
  104. return -1;
  105. }
  106. tfd = file->vnode->data;
  107. if (!tfd)
  108. {
  109. rt_set_errno(EINVAL);
  110. return -1;
  111. }
  112. if ((rt_atomic_load(&(tfd->ticks)) == 0) && (file->flags & O_NONBLOCK))
  113. {
  114. rt_set_errno(EAGAIN);
  115. return -EAGAIN;
  116. }
  117. else
  118. {
  119. if (rt_atomic_load(&(tfd->ticks)) == 0)
  120. {
  121. tfd->wqn.polling_thread = rt_thread_self();
  122. if (tfd->wqn.wqueue)
  123. {
  124. rt_wqueue_remove(&tfd->wqn);
  125. }
  126. rt_wqueue_add(&tfd->timerfd_queue, &tfd->wqn);
  127. ret = rt_thread_suspend_with_flag(tfd->wqn.polling_thread, RT_INTERRUPTIBLE);
  128. if (ret == RT_EOK)
  129. {
  130. rt_schedule();
  131. }
  132. else
  133. {
  134. return ret;
  135. }
  136. }
  137. (*buffer) = rt_atomic_load(&(tfd->timeout_num));
  138. rt_atomic_store(&(tfd->timeout_num), 0);
  139. rt_atomic_store(&(tfd->ticks), 0);
  140. }
  141. return sizeof(buffer);
  142. }
  143. static int timerfd_wqueue_callback(struct rt_wqueue_node *wait, void *key)
  144. {
  145. return 0;
  146. }
  147. static int timerfd_do_create(int clockid, int flags)
  148. {
  149. struct rt_timerfd *tfd = RT_NULL;
  150. struct dfs_file *df;
  151. rt_err_t ret = -1;
  152. int fd = -1;
  153. if ((flags & ~TFD_SHARED_FCNTL_FLAGS) ||
  154. (clockid != CLOCK_MONOTONIC &&
  155. clockid != CLOCK_REALTIME &&
  156. clockid != CLOCK_REALTIME_ALARM &&
  157. clockid != CLOCK_BOOTTIME &&
  158. clockid != CLOCK_BOOTTIME_ALARM))
  159. {
  160. rt_set_errno(EINVAL);
  161. return -1;
  162. }
  163. if ((clockid == CLOCK_REALTIME_ALARM ||
  164. clockid == CLOCK_BOOTTIME_ALARM))
  165. {
  166. rt_set_errno(EPERM);
  167. return -1;
  168. }
  169. fd = fd_new();
  170. if (fd < 0)
  171. {
  172. rt_set_errno(EINVAL);
  173. return -1;
  174. }
  175. ret = fd;
  176. df = fd_get(fd);
  177. if (df)
  178. {
  179. df->flags |= flags;
  180. tfd = (struct rt_timerfd *)rt_calloc(1, sizeof(struct rt_timerfd));
  181. if (tfd)
  182. {
  183. rt_mutex_init(&tfd->lock, TIMERFD_MUTEX_NAME, RT_IPC_FLAG_FIFO);
  184. rt_wqueue_init(&tfd->timerfd_queue);
  185. tfd->isperiodic = INIT_PERIODIC;
  186. tfd->ticks = 0;
  187. tfd->timeout_num = 0;
  188. tfd->tick_out = 0;
  189. tfd->clockid = clockid;
  190. tfd->timer = RT_NULL;
  191. tfd->pre_time.tv_sec = 0;
  192. tfd->pre_time.tv_nsec = 0;
  193. tfd->wqn.polling_thread = rt_thread_self();
  194. rt_list_init(&(tfd->wqn.list));
  195. tfd->wqn.wakeup = timerfd_wqueue_callback;
  196. df->vnode = (struct dfs_vnode *)rt_malloc(sizeof(struct dfs_vnode));
  197. if (df->vnode)
  198. {
  199. dfs_vnode_init(df->vnode, FT_REGULAR, &timerfd_fops);
  200. df->vnode->data = tfd;
  201. #ifdef RT_USING_DFS_V2
  202. df->fops = &timerfd_fops;
  203. #endif
  204. }
  205. else
  206. {
  207. rt_free(tfd);
  208. fd_release(fd);
  209. rt_set_errno(ENOMEM);
  210. ret = -1;
  211. }
  212. }
  213. else
  214. {
  215. fd_release(fd);
  216. rt_set_errno(ENOMEM);
  217. ret = -1;
  218. }
  219. }
  220. else
  221. {
  222. fd_release(fd);
  223. ret = -1;
  224. }
  225. return ret;
  226. }
  227. static int get_current_time(struct rt_timerfd *tfd, struct timespec *time)
  228. {
  229. int ret = 0;
  230. struct timespec *cur_time = RT_NULL;
  231. if (time == RT_NULL)
  232. {
  233. cur_time = &tfd->pre_time;
  234. }
  235. else
  236. {
  237. cur_time = time;
  238. }
  239. if (tfd->clockid >= 0)
  240. {
  241. ret = clock_gettime(tfd->clockid, cur_time);
  242. }
  243. else
  244. {
  245. ret = clock_gettime(CLOCK_MONOTONIC, cur_time);
  246. }
  247. return ret;
  248. }
  249. static void timerfd_timeout(void *parameter)
  250. {
  251. struct rt_timerfd *tfd = RT_NULL;
  252. tfd = (struct rt_timerfd *)parameter;
  253. if (tfd == RT_NULL)
  254. {
  255. return ;
  256. }
  257. rt_wqueue_wakeup(&tfd->timerfd_queue, (void *)POLLIN);
  258. rt_atomic_store(&(tfd->ticks), 1);
  259. rt_atomic_add(&(tfd->timeout_num), 1);
  260. rt_mutex_take(&tfd->lock, RT_WAITING_FOREVER);
  261. get_current_time(tfd, RT_NULL);
  262. if (tfd->isperiodic == OPEN_PERIODIC)
  263. {
  264. if (tfd->timer)
  265. {
  266. rt_timer_stop(tfd->timer);
  267. rt_timer_delete(tfd->timer);
  268. tfd->timer = RT_NULL;
  269. }
  270. tfd->isperiodic = ENTER_PERIODIC;
  271. tfd->timer = rt_timer_create(TIMERFD_MUTEX_NAME, timerfd_timeout,
  272. tfd, tfd->tick_out,
  273. RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
  274. if (tfd->timer == RT_NULL)
  275. {
  276. LOG_E("rt_timer_create fail \n");
  277. rt_mutex_release(&tfd->lock);
  278. return ;
  279. }
  280. rt_timer_start(tfd->timer);
  281. }
  282. rt_mutex_release(&tfd->lock);
  283. }
  284. static void timerfd_time_operation(time_t *sec, long *nsec)
  285. {
  286. if (*nsec < 0)
  287. {
  288. if (*sec > 0)
  289. {
  290. *sec -= 1;
  291. *nsec = 1 * SEC_TO_NSEC + *nsec;
  292. }
  293. }
  294. if (*sec < 0 || *nsec < 0)
  295. {
  296. *sec = 0;
  297. *nsec = 0;
  298. }
  299. }
  300. static int timerfd_do_settime(int fd, int flags, const struct itimerspec *new, struct itimerspec *old)
  301. {
  302. int ret = 0;
  303. struct rt_timerfd *tfd;
  304. struct dfs_file *df;
  305. struct timespec current_time;
  306. int tick_out;
  307. rt_int64_t value_msec;
  308. rt_int64_t interval_msec;
  309. rt_int64_t cur_time = 0;
  310. if (fd < 0)
  311. {
  312. rt_set_errno(EINVAL);
  313. return -EINVAL;
  314. }
  315. df = fd_get(fd);
  316. if (!df)
  317. return -EINVAL;
  318. tfd = df->vnode->data;
  319. rt_atomic_store(&(tfd->ticks), 0);
  320. rt_atomic_store(&(tfd->timeout_num), 0);
  321. rt_mutex_take(&tfd->lock, RT_WAITING_FOREVER);
  322. tfd->isperiodic = INIT_PERIODIC;
  323. if (old)
  324. {
  325. old->it_interval.tv_nsec = tfd->ittimer.it_interval.tv_nsec;
  326. old->it_interval.tv_sec = tfd->ittimer.it_interval.tv_sec;
  327. old->it_value.tv_nsec = tfd->ittimer.it_value.tv_nsec;
  328. old->it_value.tv_sec = tfd->ittimer.it_value.tv_sec;
  329. }
  330. if (new)
  331. {
  332. if (tfd->timer != RT_NULL)
  333. {
  334. rt_timer_stop(tfd->timer);
  335. rt_timer_delete(tfd->timer);
  336. tfd->timer = RT_NULL;
  337. }
  338. if (new->it_value.tv_nsec == 0 && new->it_value.tv_sec == 0)
  339. {
  340. rt_mutex_release(&tfd->lock);
  341. return 0;
  342. }
  343. value_msec = (new->it_value.tv_nsec / MSEC_TO_NSEC) + (new->it_value.tv_sec * SEC_TO_MSEC);
  344. interval_msec = (new->it_interval.tv_nsec / MSEC_TO_NSEC) + (new->it_interval.tv_sec * SEC_TO_MSEC);
  345. current_time.tv_nsec = 0;
  346. current_time.tv_sec = 0;
  347. if (flags == TFD_TIMER_ABSTIME)
  348. {
  349. ret = get_current_time(tfd, &current_time);
  350. if (ret < 0)
  351. {
  352. rt_mutex_release(&tfd->lock);
  353. return ret;
  354. }
  355. cur_time = current_time.tv_sec * SEC_TO_MSEC + (current_time.tv_nsec / MSEC_TO_NSEC);
  356. value_msec = value_msec - cur_time;
  357. }
  358. tfd->ittimer.it_interval.tv_nsec = new->it_interval.tv_nsec;
  359. tfd->ittimer.it_interval.tv_sec = new->it_interval.tv_sec;
  360. tfd->ittimer.it_value.tv_sec = new->it_value.tv_sec - current_time.tv_sec;
  361. tfd->ittimer.it_value.tv_nsec = new->it_value.tv_nsec - current_time.tv_nsec;
  362. timerfd_time_operation(&tfd->ittimer.it_value.tv_sec, &tfd->ittimer.it_value.tv_nsec);
  363. if ((interval_msec > 0) && (interval_msec <= TIME_INT32_MAX))
  364. {
  365. tfd->tick_out = rt_tick_from_millisecond(interval_msec);
  366. if (tfd->tick_out < 0)
  367. {
  368. rt_mutex_release(&tfd->lock);
  369. return -EINVAL;
  370. }
  371. tfd->isperiodic = OPEN_PERIODIC;
  372. }
  373. get_current_time(tfd, RT_NULL);
  374. if (value_msec > 0)
  375. {
  376. if (value_msec > TIME_INT32_MAX)
  377. {
  378. rt_mutex_release(&tfd->lock);
  379. return -EINVAL;
  380. }
  381. tick_out = rt_tick_from_millisecond(value_msec);
  382. if (tick_out < 0)
  383. {
  384. rt_mutex_release(&tfd->lock);
  385. return -EINVAL;
  386. }
  387. tfd->timer = rt_timer_create(TIMERFD_MUTEX_NAME, timerfd_timeout,
  388. tfd, tick_out,
  389. RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER);
  390. if (tfd->timer == RT_NULL)
  391. {
  392. LOG_E("rt_timer_create fail \n");
  393. rt_mutex_release(&tfd->lock);
  394. return -ENOMEM;
  395. }
  396. rt_timer_start(tfd->timer);
  397. }
  398. else
  399. {
  400. timerfd_timeout(tfd);
  401. }
  402. }
  403. else
  404. {
  405. rt_set_errno(EINVAL);
  406. ret = -1;
  407. }
  408. rt_mutex_release(&tfd->lock);
  409. return ret;
  410. }
  411. static int timerfd_do_gettime(int fd, struct itimerspec *cur)
  412. {
  413. struct rt_timerfd *tfd;
  414. struct dfs_file *df = RT_NULL;
  415. struct timespec cur_time;
  416. rt_int64_t tv_sec = 0;
  417. rt_int64_t tv_nsec = 0;
  418. df = fd_get(fd);
  419. if (df == RT_NULL)
  420. {
  421. rt_set_errno(EINVAL);
  422. return -1;
  423. }
  424. tfd = df->vnode->data;
  425. get_current_time(tfd, &cur_time);
  426. rt_mutex_take(&tfd->lock, RT_WAITING_FOREVER);
  427. tv_sec = cur_time.tv_sec - tfd->pre_time.tv_sec;
  428. tv_nsec = cur_time.tv_nsec - tfd->pre_time.tv_nsec;
  429. timerfd_time_operation(&tv_sec, &tv_nsec);
  430. cur->it_interval.tv_nsec = tfd->ittimer.it_interval.tv_nsec;
  431. cur->it_interval.tv_sec = tfd->ittimer.it_interval.tv_sec;
  432. if (tfd->isperiodic == ENTER_PERIODIC)
  433. {
  434. cur->it_value.tv_nsec = tfd->ittimer.it_interval.tv_nsec - tv_nsec;
  435. cur->it_value.tv_sec = tfd->ittimer.it_interval.tv_sec - tv_sec;
  436. timerfd_time_operation(&cur->it_value.tv_sec, &cur->it_value.tv_nsec);
  437. }
  438. else
  439. {
  440. if (rt_atomic_load(&(tfd->timeout_num)) == 1)
  441. {
  442. cur->it_value.tv_nsec = 0;
  443. cur->it_value.tv_sec = 0;
  444. }
  445. else
  446. {
  447. cur->it_value.tv_nsec = tfd->ittimer.it_value.tv_nsec - tv_nsec;
  448. cur->it_value.tv_sec = tfd->ittimer.it_value.tv_sec - tv_sec;
  449. timerfd_time_operation(&cur->it_value.tv_sec, &cur->it_value.tv_nsec);
  450. }
  451. }
  452. rt_mutex_release(&tfd->lock);
  453. return 0;
  454. }
  455. /**
  456. * @brief Creates a file descriptor for a timer.
  457. *
  458. * The `timerfd_create` function creates a new timer object that generates
  459. * timer expiration notifications via a file descriptor.
  460. *
  461. * @param clockid The clock ID that specifies the clock to be used as the
  462. * timing base for the timer. Common values include:
  463. * - `CLOCK_REALTIME`: A system-wide clock representing
  464. * wall-clock time.
  465. * - `CLOCK_MONOTONIC`: A clock that cannot be set and
  466. * represents monotonic time since some unspecified
  467. * starting point.
  468. * @param flags A bitmask that can include the following flags:
  469. * - `TFD_CLOEXEC`: Close the file descriptor on `execve`.
  470. * - `TFD_NONBLOCK`: Set the file descriptor to non-blocking mode.
  471. *
  472. * @return On success, returns a file descriptor for the timer. On error,
  473. * returns -1 and sets `errno` appropriately.
  474. *
  475. * @note The file descriptor can be used with select, poll, or epoll to wait
  476. * for timer expirations.
  477. *
  478. * @warning The timerfd interface is Linux-specific and may not be available
  479. * on other operating systems.
  480. *
  481. * @see timerfd_settime, timerfd_gettime
  482. */
  483. int timerfd_create(int clockid, int flags)
  484. {
  485. return timerfd_do_create(clockid, flags);
  486. }
  487. /**
  488. * @brief Sets the time for a timer file descriptor.
  489. *
  490. * The `timerfd_settime` function starts or modifies the timer associated
  491. * with the specified timer file descriptor.
  492. *
  493. * @param fd The file descriptor of the timer, obtained from
  494. * `timerfd_create`.
  495. * @param flags Flags that control the behavior of the timer. Possible
  496. * values include:
  497. * - `0`: Relative time is specified in `new`.
  498. * - `TFD_TIMER_ABSTIME`: Use absolute time instead of
  499. * relative time.
  500. * @param new A pointer to a `itimerspec` structure that specifies the
  501. * new timer settings:
  502. * - `it_value`: The initial expiration time. A zero value
  503. * means the timer is disabled.
  504. * - `it_interval`: The interval for periodic timers. A zero
  505. * value means the timer is not periodic.
  506. * @param old A pointer to a `itimerspec` structure to store the
  507. * previous timer settings. Can be `NULL` if this information
  508. * is not needed.
  509. *
  510. * @return On success, returns 0. On error, returns -1 and sets `errno`
  511. * appropriately.
  512. *
  513. * @note The timer starts counting down immediately after this call if
  514. * `it_value` is non-zero.
  515. *
  516. * @warning If the timer is set to a very short interval, high-frequency
  517. * events may impact system performance.
  518. *
  519. * @see timerfd_create, timerfd_gettime
  520. */
  521. int timerfd_settime(int fd, int flags, const struct itimerspec *new, struct itimerspec *old)
  522. {
  523. return timerfd_do_settime(fd, flags, new, old);
  524. }
  525. /**
  526. * @brief Retrieves the current value and interval of a timer.
  527. *
  528. * The `timerfd_gettime` function queries the settings of the timer associated
  529. * with the specified timer file descriptor.
  530. *
  531. * @param fd The file descriptor of the timer, obtained from `timerfd_create`.
  532. * @param cur A pointer to a `itimerspec` structure where the current timer
  533. * settings will be stored:
  534. * - `it_value`: The time remaining until the next expiration.
  535. * If zero, the timer is disabled.
  536. * - `it_interval`: The interval for periodic timers. Zero if the
  537. * timer is not periodic.
  538. *
  539. * @return On success, returns 0. On error, returns -1 and sets `errno`
  540. * appropriately.
  541. *
  542. * @note This function does not reset or modify the timer; it only retrieves
  543. * the current settings.
  544. *
  545. * @see timerfd_create, timerfd_settime
  546. */
  547. int timerfd_gettime(int fd, struct itimerspec *cur)
  548. {
  549. return timerfd_do_gettime(fd, cur);
  550. }