exit-no-cleanup.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Test case testing exit without cleanup and io-wq work pending or queued.
  5. *
  6. * From Florian Fischer <florian.fl.fischer@fau.de>
  7. * Link: https://lore.kernel.org/io-uring/20211202165606.mqryio4yzubl7ms5@pasture/
  8. *
  9. */
  10. #include <assert.h>
  11. #include <err.h>
  12. #include <errno.h>
  13. #include <pthread.h>
  14. #include <semaphore.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <sys/sysinfo.h>
  18. #include <unistd.h>
  19. #include "liburing.h"
  20. #include "helpers.h"
  21. // on fast enough machines with enough cores, the first few threads will post
  22. // enough sem's to cause the main thread to exit while some threads are half way
  23. // initialization. This causes a null deference somewhere in thread cleanup,
  24. // which trips ASAN.
  25. #ifndef CONFIG_USE_SANITIZER
  26. #define IORING_ENTRIES 8
  27. static pthread_t *threads;
  28. static pthread_barrier_t init_barrier;
  29. static int sleep_fd, notify_fd;
  30. static sem_t sem;
  31. static void *thread_func(void *arg)
  32. {
  33. struct io_uring ring;
  34. int res;
  35. res = io_uring_queue_init(IORING_ENTRIES, &ring, 0);
  36. if (res)
  37. err(EXIT_FAILURE, "io_uring_queue_init failed");
  38. pthread_barrier_wait(&init_barrier);
  39. for(;;) {
  40. struct io_uring_cqe *cqe;
  41. struct io_uring_sqe *sqe;
  42. uint64_t buf;
  43. int res;
  44. sqe = io_uring_get_sqe(&ring);
  45. assert(sqe);
  46. io_uring_prep_read(sqe, sleep_fd, &buf, sizeof(buf), 0);
  47. res = io_uring_submit_and_wait(&ring, 1);
  48. if (res < 0)
  49. err(EXIT_FAILURE, "io_uring_submit_and_wait failed");
  50. res = io_uring_peek_cqe(&ring, &cqe);
  51. assert(!res);
  52. if (cqe->res < 0) {
  53. errno = -cqe->res;
  54. err(EXIT_FAILURE, "read failed");
  55. }
  56. assert(cqe->res == sizeof(buf));
  57. sem_post(&sem);
  58. io_uring_cqe_seen(&ring, cqe);
  59. }
  60. return NULL;
  61. }
  62. int main(int argc, char *argv[])
  63. {
  64. int res, fds[2], i, cpus;
  65. const uint64_t n = 0x42;
  66. if (argc > 1)
  67. return T_EXIT_SKIP;
  68. cpus = get_nprocs();
  69. res = pthread_barrier_init(&init_barrier, NULL, cpus);
  70. if (res)
  71. err(EXIT_FAILURE, "pthread_barrier_init failed");
  72. res = sem_init(&sem, 0, 0);
  73. if (res)
  74. err(EXIT_FAILURE, "sem_init failed");
  75. threads = t_malloc(sizeof(pthread_t) * cpus);
  76. res = pipe(fds);
  77. if (res)
  78. err(EXIT_FAILURE, "pipe failed");
  79. sleep_fd = fds[0];
  80. notify_fd = fds[1];
  81. for (i = 0; i < cpus; i++) {
  82. errno = pthread_create(&threads[i], NULL, thread_func, NULL);
  83. if (errno)
  84. err(EXIT_FAILURE, "pthread_create failed");
  85. }
  86. // Write #cpus notifications
  87. for (i = 0; i < cpus; i++) {
  88. res = write(notify_fd, &n, sizeof(n));
  89. if (res < 0)
  90. err(EXIT_FAILURE, "write failed");
  91. assert(res == sizeof(n));
  92. }
  93. // Await that all notifications were received
  94. for (i = 0; i < cpus; i++)
  95. sem_wait(&sem);
  96. // Exit without resource cleanup
  97. exit(EXIT_SUCCESS);
  98. }
  99. #else
  100. int main(int argc, char *argv[])
  101. {
  102. return T_EXIT_SKIP;
  103. }
  104. #endif