reg-wait.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: Test that registered waits work
  5. */
  6. #include <errno.h>
  7. #include <stdio.h>
  8. #include <unistd.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <fcntl.h>
  12. #include <sys/time.h>
  13. #include <sys/mman.h>
  14. #include <linux/mman.h>
  15. #include "liburing.h"
  16. #include "helpers.h"
  17. #include "test.h"
  18. #include "../src/syscall.h"
  19. static const struct io_uring_reg_wait brief_wait = {
  20. .flags = IORING_REG_WAIT_TS,
  21. .ts.tv_sec = 0,
  22. .ts.tv_nsec = 1000,
  23. };
  24. static bool has_kernel_regions;
  25. static int test_wait_reg_offset(struct io_uring *ring,
  26. unsigned wait_nr, unsigned long offset)
  27. {
  28. return __sys_io_uring_enter2(ring->ring_fd, 0, wait_nr,
  29. IORING_ENTER_GETEVENTS |
  30. IORING_ENTER_EXT_ARG |
  31. IORING_ENTER_EXT_ARG_REG,
  32. (void *)offset,
  33. sizeof(struct io_uring_reg_wait));
  34. }
  35. static int __init_ring_with_region(struct io_uring *ring, unsigned ring_flags,
  36. struct io_uring_mem_region_reg *pr,
  37. bool disabled)
  38. {
  39. int flags = disabled ? IORING_SETUP_R_DISABLED : 0;
  40. int ret;
  41. ret = io_uring_queue_init(8, ring, flags);
  42. if (ret) {
  43. if (ret != -EINVAL)
  44. fprintf(stderr, "ring setup failed: %d\n", ret);
  45. return ret;
  46. }
  47. ret = io_uring_register_region(ring, pr);
  48. if (ret)
  49. goto err;
  50. if (disabled) {
  51. ret = io_uring_enable_rings(ring);
  52. if (ret) {
  53. fprintf(stderr, "io_uring_enable_rings failure %i\n", ret);
  54. goto err;
  55. }
  56. }
  57. return 0;
  58. err:
  59. io_uring_queue_exit(ring);
  60. return ret;
  61. }
  62. static int page_size;
  63. static struct io_uring_reg_wait *reg;
  64. static int test_invalid_sig(struct io_uring *ring)
  65. {
  66. struct io_uring_cqe *cqe;
  67. sigset_t sig;
  68. int ret;
  69. memset(reg, 0, sizeof(*reg));
  70. reg->ts.tv_sec = 1;
  71. reg->ts.tv_nsec = 0;
  72. reg->sigmask = (unsigned long) &sig;
  73. reg->sigmask_sz = 1;
  74. ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, 0);
  75. if (ret != -EINVAL) {
  76. fprintf(stderr, "sigmask_sz failed: %d\n", ret);
  77. return T_EXIT_FAIL;
  78. }
  79. memset(reg, 0, sizeof(*reg));
  80. reg->ts.tv_sec = 1;
  81. reg->ts.tv_nsec = 0;
  82. reg->sigmask = 100;
  83. reg->sigmask_sz = 8;
  84. ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, 0);
  85. if (ret != -EFAULT) {
  86. fprintf(stderr, "sigmask invalid failed: %d\n", ret);
  87. return T_EXIT_FAIL;
  88. }
  89. return T_EXIT_PASS;
  90. }
  91. static int test_offsets(struct io_uring *ring, struct io_uring_reg_wait *base,
  92. size_t size, bool overallocated)
  93. {
  94. struct io_uring_cqe *cqe;
  95. int max_index = size / sizeof(struct io_uring_reg_wait);
  96. struct io_uring_reg_wait *rw;
  97. unsigned long offset;
  98. int copy_size;
  99. int ret;
  100. rw = base;
  101. memcpy(rw, &brief_wait, sizeof(brief_wait));
  102. ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, 0);
  103. if (ret != -ETIME) {
  104. fprintf(stderr, "0 index failed: %d\n", ret);
  105. return T_EXIT_FAIL;
  106. }
  107. if (overallocated) {
  108. rw = base + max_index;
  109. memcpy(rw, &brief_wait, sizeof(brief_wait));
  110. }
  111. ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, max_index);
  112. if (ret != -EFAULT) {
  113. fprintf(stderr, "max+1 index failed: %d\n", ret);
  114. return T_EXIT_FAIL;
  115. }
  116. rw = base + max_index - 1;
  117. memcpy(rw, &brief_wait, sizeof(brief_wait));
  118. ret = io_uring_submit_and_wait_reg(ring, &cqe, 1, max_index - 1);
  119. if (ret != -ETIME) {
  120. fprintf(stderr, "last index failed: %d\n", ret);
  121. return T_EXIT_FAIL;
  122. }
  123. offset = 0UL - sizeof(long);
  124. ret = test_wait_reg_offset(ring, 1, offset);
  125. if (ret != -EFAULT) {
  126. fprintf(stderr, "overflow offset failed: %d\n", ret);
  127. return T_EXIT_FAIL;
  128. }
  129. offset = size - sizeof(long);
  130. rw = (void *)base + offset;
  131. copy_size = overallocated ? sizeof(brief_wait) : sizeof(long);
  132. memcpy(rw, &brief_wait, copy_size);
  133. ret = test_wait_reg_offset(ring, 1, offset);
  134. if (ret != -EFAULT) {
  135. fprintf(stderr, "OOB offset failed: %d\n", ret);
  136. return T_EXIT_FAIL;
  137. }
  138. offset = 1;
  139. rw = (void *)base + offset;
  140. memcpy(rw, &brief_wait, sizeof(brief_wait));
  141. /* undefined behaviour, check the kernel doesn't crash */
  142. (void)test_wait_reg_offset(ring, 1, offset);
  143. return 0;
  144. }
  145. static int test_basic(struct io_uring *ring)
  146. {
  147. struct io_uring_cqe *cqe;
  148. struct timeval tv;
  149. int ret;
  150. memset(reg, 0, sizeof(*reg));
  151. reg->ts.tv_sec = 1;
  152. reg->ts.tv_nsec = 100000000ULL;
  153. reg->flags = IORING_REG_WAIT_TS;
  154. gettimeofday(&tv, NULL);
  155. ret = io_uring_submit_and_wait_reg(ring, &cqe, 2, 0);
  156. if (ret != -ETIME) {
  157. fprintf(stderr, "submit_and_wait_reg: %d\n", ret);
  158. goto err;
  159. }
  160. ret = mtime_since_now(&tv);
  161. /* allow some slack, should be around 1.1s */
  162. if (ret < 1000 || ret > 1200) {
  163. fprintf(stderr, "wait too long or short: %d\n", ret);
  164. goto err;
  165. }
  166. return T_EXIT_PASS;
  167. err:
  168. return T_EXIT_FAIL;
  169. }
  170. static int test_wait_arg(void)
  171. {
  172. struct io_uring_region_desc rd = {};
  173. struct io_uring_mem_region_reg mr = {};
  174. struct io_uring ring;
  175. void *buffer;
  176. int ret;
  177. ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
  178. if (ret) {
  179. if (ret == -EINVAL) {
  180. printf("IORING_SETUP_R_DISABLED not supported, skip\n");
  181. return 0;
  182. }
  183. fprintf(stderr, "ring setup failed: %d\n", ret);
  184. return T_EXIT_FAIL;
  185. }
  186. buffer = aligned_alloc(page_size, page_size * 4);
  187. if (!buffer) {
  188. fprintf(stderr, "allocation failed\n");
  189. return T_EXIT_FAIL;
  190. }
  191. rd.user_addr = (__u64)(unsigned long)buffer;
  192. rd.size = page_size;
  193. rd.flags = IORING_MEM_REGION_TYPE_USER;
  194. mr.region_uptr = (__u64)(unsigned long)&rd;
  195. mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
  196. ret = io_uring_register_region(&ring, &mr);
  197. if (ret) {
  198. fprintf(stderr, "region reg failed %i\n", ret);
  199. return 1;
  200. }
  201. ret = io_uring_enable_rings(&ring);
  202. if (ret) {
  203. fprintf(stderr, "io_uring_enable_rings failure %i\n", ret);
  204. return T_EXIT_FAIL;
  205. }
  206. reg = buffer;
  207. ret = test_basic(&ring);
  208. if (ret == T_EXIT_FAIL) {
  209. fprintf(stderr, "test failed\n");
  210. goto err;
  211. }
  212. ret = test_invalid_sig(&ring);
  213. if (ret == T_EXIT_FAIL) {
  214. fprintf(stderr, "test_invalid sig failed\n");
  215. goto err;
  216. }
  217. ret = test_offsets(&ring, buffer, page_size, true);
  218. if (ret == T_EXIT_FAIL) {
  219. fprintf(stderr, "test_offsets failed\n");
  220. goto err;
  221. }
  222. err:
  223. free(buffer);
  224. io_uring_queue_exit(&ring);
  225. return ret;
  226. }
  227. static int test_try_register_region(struct io_uring_mem_region_reg *pr,
  228. bool disabled)
  229. {
  230. struct io_uring ring;
  231. int ret;
  232. ret = __init_ring_with_region(&ring, 0, pr, disabled);
  233. if (!ret)
  234. io_uring_queue_exit(&ring);
  235. return ret;
  236. }
  237. static int test_regions(void)
  238. {
  239. struct io_uring_region_desc rd = {};
  240. struct io_uring_mem_region_reg mr = {};
  241. void *buffer;
  242. int ret;
  243. buffer = aligned_alloc(page_size, page_size * 4);
  244. if (!buffer) {
  245. fprintf(stderr, "allocation failed\n");
  246. return T_EXIT_FAIL;
  247. }
  248. rd.user_addr = (__u64)(unsigned long)buffer;
  249. rd.size = page_size;
  250. rd.flags = IORING_MEM_REGION_TYPE_USER;
  251. mr.region_uptr = (__u64)(unsigned long)&rd;
  252. mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
  253. ret = test_try_register_region(&mr, true);
  254. if (ret == -EINVAL)
  255. return T_EXIT_SKIP;
  256. if (ret) {
  257. fprintf(stderr, "region: register normal fail %i\n", ret);
  258. return T_EXIT_FAIL;
  259. }
  260. ret = test_try_register_region(&mr, false);
  261. if (ret != -EINVAL) {
  262. fprintf(stderr, "region: register with !R_DISABLED fail %i\n", ret);
  263. return T_EXIT_FAIL;
  264. }
  265. rd.size = page_size * 4;
  266. ret = test_try_register_region(&mr, true);
  267. if (ret) {
  268. fprintf(stderr, "test_try_register_region() 16KB fail %i\n", ret);
  269. return T_EXIT_FAIL;
  270. }
  271. rd.size = page_size;
  272. rd.user_addr = 0;
  273. ret = test_try_register_region(&mr, true);
  274. if (ret != -EFAULT) {
  275. fprintf(stderr, "test_try_register_region() null uptr fail %i\n", ret);
  276. return T_EXIT_FAIL;
  277. }
  278. rd.user_addr = (__u64)(unsigned long)buffer;
  279. rd.flags = 0;
  280. ret = test_try_register_region(&mr, true);
  281. if (!ret) {
  282. fprintf(stderr, "test_try_register_region() kernel alloc with uptr fail %i\n", ret);
  283. return T_EXIT_FAIL;
  284. }
  285. rd.flags = IORING_MEM_REGION_TYPE_USER;
  286. rd.size = 0;
  287. ret = test_try_register_region(&mr, true);
  288. if (!ret) {
  289. fprintf(stderr, "test_try_register_region() 0-size fail %i\n", ret);
  290. return T_EXIT_FAIL;
  291. }
  292. rd.size = page_size;
  293. mr.region_uptr = 0;
  294. ret = test_try_register_region(&mr, true);
  295. if (!ret) {
  296. fprintf(stderr, "test_try_register_region() NULL region %i\n", ret);
  297. return T_EXIT_FAIL;
  298. }
  299. mr.region_uptr = (__u64)(unsigned long)&rd;
  300. rd.user_addr += 16;
  301. ret = test_try_register_region(&mr, true);
  302. if (!ret) {
  303. fprintf(stderr, "test_try_register_region() misaligned region %i\n", ret);
  304. return T_EXIT_FAIL;
  305. }
  306. rd.user_addr = 0x1000;
  307. ret = test_try_register_region(&mr, true);
  308. if (!ret) {
  309. fprintf(stderr, "test_try_register_region() bogus uptr %i\n", ret);
  310. return T_EXIT_FAIL;
  311. }
  312. rd.user_addr = (__u64)(unsigned long)buffer;
  313. free(buffer);
  314. buffer = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  315. if (buffer == MAP_FAILED) {
  316. fprintf(stderr, "mmap alloc failed\n");
  317. return 1;
  318. }
  319. rd.user_addr = (__u64)(unsigned long)buffer;
  320. ret = test_try_register_region(&mr, true);
  321. if (ret != -EFAULT) {
  322. fprintf(stderr, "test_try_register_region() RO uptr %i\n", ret);
  323. return T_EXIT_FAIL;
  324. }
  325. rd.flags = 0;
  326. rd.user_addr = 0;
  327. ret = test_try_register_region(&mr, true);
  328. if (ret == -EINVAL) {
  329. has_kernel_regions = false;
  330. goto out;
  331. }
  332. if (ret) {
  333. fprintf(stderr, "test_try_register_region() failed kernel alloc %i\n", ret);
  334. return T_EXIT_FAIL;
  335. }
  336. has_kernel_regions = true;
  337. rd.flags = 0;
  338. rd.user_addr = (__u64)(unsigned long)buffer;
  339. ret = test_try_register_region(&mr, true);
  340. if (!ret) {
  341. fprintf(stderr, "test_try_register_region() failed uptr w kernel alloc %i\n", ret);
  342. return T_EXIT_FAIL;
  343. }
  344. out:
  345. munmap(buffer, page_size);
  346. return 0;
  347. }
  348. struct t_region {
  349. void *ptr;
  350. bool user_mem;
  351. size_t size;
  352. };
  353. static void t_region_free(struct t_region *r)
  354. {
  355. if (r->ptr)
  356. munmap(r->ptr, r->size);
  357. }
  358. static int t_region_create_kernel(struct t_region *r,
  359. struct io_uring *ring)
  360. {
  361. struct io_uring_region_desc rd = { .size = r->size, };
  362. struct io_uring_mem_region_reg mr = {
  363. .region_uptr = (__u64)(unsigned long)&rd,
  364. .flags = IORING_MEM_REGION_REG_WAIT_ARG,
  365. };
  366. void *p;
  367. int ret;
  368. ret = io_uring_register_region(ring, &mr);
  369. if (ret)
  370. return ret;
  371. p = mmap(NULL, r->size, PROT_READ | PROT_WRITE,
  372. MAP_SHARED | MAP_POPULATE, ring->ring_fd, rd.mmap_offset);
  373. if (p == MAP_FAILED)
  374. return -EFAULT;
  375. r->ptr = p;
  376. r->user_mem = false;
  377. return 0;
  378. }
  379. static int t_region_create_user(struct t_region *r,
  380. struct io_uring *ring,
  381. bool huge)
  382. {
  383. struct io_uring_region_desc rd = {};
  384. struct io_uring_mem_region_reg mr = {};
  385. int flags = MAP_PRIVATE | MAP_ANONYMOUS;
  386. void *p;
  387. int ret;
  388. if (huge)
  389. flags |= MAP_HUGETLB | MAP_HUGE_2MB;
  390. p = mmap(NULL, r->size, PROT_READ | PROT_WRITE, flags, -1, 0);
  391. if (p == MAP_FAILED)
  392. return -ENOMEM;
  393. mr.region_uptr = (__u64)(unsigned long)&rd;
  394. mr.flags = IORING_MEM_REGION_REG_WAIT_ARG;
  395. rd.user_addr = (__u64)(unsigned long)p;
  396. rd.flags = IORING_MEM_REGION_TYPE_USER;
  397. rd.size = r->size;
  398. ret = io_uring_register_region(ring, &mr);
  399. if (ret) {
  400. munmap(p, r->size);
  401. return ret;
  402. }
  403. r->ptr = p;
  404. r->user_mem = true;
  405. return 0;
  406. }
  407. struct test_param {
  408. size_t size;
  409. bool huge_page;
  410. bool kern_buf;
  411. };
  412. static int test_region_buffer_types(void)
  413. {
  414. const size_t huge_size = 1024 * 1024 * 2;
  415. struct test_param params[] = {
  416. { .size = page_size },
  417. /* forcing vmap */
  418. { .size = page_size * 2 },
  419. { .size = page_size * 16 },
  420. /* huge page w/o vmap */
  421. { .size = huge_size, .huge_page = true },
  422. /* huge page w/ vmap */
  423. { .size = huge_size * 2, .huge_page = true },
  424. { .size = page_size, .kern_buf = true },
  425. /* likely to be a compound page */
  426. { .size = page_size * 2, .kern_buf = true },
  427. { .size = page_size * 8, .kern_buf = true },
  428. /* kernel allocation + vmap */
  429. { .size = page_size * 512, .kern_buf = true },
  430. };
  431. struct io_uring ring;
  432. int i, ret;
  433. for (i = 0; i < ARRAY_SIZE(params); i++) {
  434. struct t_region r = { .size = params[i].size, };
  435. ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
  436. if (ret) {
  437. fprintf(stderr, "ring setup failed: %d\n", ret);
  438. return ret;
  439. }
  440. if (params[i].kern_buf)
  441. ret = t_region_create_kernel(&r, &ring);
  442. else
  443. ret = t_region_create_user(&r, &ring, params[i].huge_page);
  444. if (ret) {
  445. io_uring_queue_exit(&ring);
  446. if (ret == -ENOMEM || ret == -EINVAL)
  447. continue;
  448. fprintf(stderr, "t_region_create_user failed, idx %i\n", i);
  449. return 1;
  450. }
  451. ret = io_uring_enable_rings(&ring);
  452. if (ret) {
  453. fprintf(stderr, "io_uring_enable_rings failure %i\n", ret);
  454. return ret;
  455. }
  456. ret = test_offsets(&ring, r.ptr, r.size, false);
  457. if (ret) {
  458. fprintf(stderr, "test_offsets failed, idx %i\n", i);
  459. return 1;
  460. }
  461. t_region_free(&r);
  462. io_uring_queue_exit(&ring);
  463. }
  464. return 0;
  465. }
  466. int main(int argc, char *argv[])
  467. {
  468. int ret;
  469. if (argc > 1)
  470. return 0;
  471. page_size = sysconf(_SC_PAGESIZE);
  472. if (page_size < 0) {
  473. perror("sysconf(_SC_PAGESIZE)");
  474. return 1;
  475. }
  476. ret = test_regions();
  477. if (ret == T_EXIT_SKIP) {
  478. printf("regions are not supported, skip\n");
  479. return 0;
  480. } else if (ret) {
  481. fprintf(stderr, "test_region failed\n");
  482. return 1;
  483. }
  484. ret = test_wait_arg();
  485. if (ret == T_EXIT_FAIL) {
  486. fprintf(stderr, "test_wait_arg failed\n");
  487. return 1;
  488. }
  489. ret = test_region_buffer_types();
  490. if (ret == T_EXIT_FAIL) {
  491. fprintf(stderr, "test_region_buffer_types failed\n");
  492. return 1;
  493. }
  494. return 0;
  495. }