socket-io-cmd.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Check that CMD operations on sockets are consistent.
  5. */
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <stdint.h>
  9. #include <assert.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12. #include <linux/sockios.h>
  13. #include <sys/ioctl.h>
  14. #include "liburing.h"
  15. #include "helpers.h"
  16. #define USERDATA 0x1234
  17. #define MSG "foobarbaz"
  18. static int no_io_cmd;
  19. struct fds {
  20. int tx;
  21. int rx;
  22. };
  23. /* Create 2 sockets (tx, rx) given the socket type */
  24. static struct fds create_sockets(bool stream)
  25. {
  26. struct fds retval;
  27. int fd[2];
  28. t_create_socket_pair(fd, stream);
  29. retval.tx = fd[0];
  30. retval.rx = fd[1];
  31. return retval;
  32. }
  33. static int create_sqe_and_submit(struct io_uring *ring, int32_t fd, int op)
  34. {
  35. struct io_uring_sqe *sqe;
  36. int ret;
  37. assert(fd > 0);
  38. sqe = io_uring_get_sqe(ring);
  39. assert(sqe != NULL);
  40. io_uring_prep_cmd_sock(sqe, op, fd, 0, 0, NULL, 0);
  41. sqe->user_data = USERDATA;
  42. /* Submitting SQE */
  43. ret = io_uring_submit_and_wait(ring, 1);
  44. if (ret <= 0)
  45. return ret;
  46. return 0;
  47. }
  48. static int receive_cqe(struct io_uring *ring)
  49. {
  50. struct io_uring_cqe *cqe;
  51. int err;
  52. err = io_uring_wait_cqe(ring, &cqe);
  53. assert(err == 0);
  54. assert(cqe->user_data == USERDATA);
  55. err = cqe->res;
  56. io_uring_cqe_seen(ring, cqe);
  57. /* Return the result of the operation */
  58. return err;
  59. }
  60. static ssize_t send_data(struct fds *s, char *str)
  61. {
  62. size_t written_bytes;
  63. written_bytes = write(s->tx, str, strlen(str));
  64. assert(written_bytes == strlen(MSG));
  65. return written_bytes;
  66. }
  67. static int run_test(bool stream)
  68. {
  69. struct fds sockfds;
  70. ssize_t bytes_in, bytes_out;
  71. struct io_uring ring;
  72. size_t written_bytes;
  73. int error;
  74. /* Create three sockets */
  75. sockfds = create_sockets(stream);
  76. assert(sockfds.tx > 0);
  77. assert(sockfds.rx > 0);
  78. /* Send data sing the sockfds->send */
  79. written_bytes = send_data(&sockfds, MSG);
  80. /* Simply io_uring ring creation */
  81. error = t_create_ring(1, &ring, 0);
  82. if (error == T_SETUP_SKIP)
  83. return error;
  84. else if (error != T_SETUP_OK)
  85. return T_EXIT_FAIL;
  86. error = create_sqe_and_submit(&ring, sockfds.rx,
  87. SOCKET_URING_OP_SIOCINQ);
  88. if (error)
  89. return T_EXIT_FAIL;
  90. bytes_in = receive_cqe(&ring);
  91. if (bytes_in < 0) {
  92. if (bytes_in == -EINVAL || bytes_in == -EOPNOTSUPP) {
  93. no_io_cmd = 1;
  94. return T_EXIT_SKIP;
  95. }
  96. fprintf(stderr, "Bad return value %ld\n", (long) bytes_in);
  97. return T_EXIT_FAIL;
  98. }
  99. error = create_sqe_and_submit(&ring, sockfds.tx,
  100. SOCKET_URING_OP_SIOCOUTQ);
  101. if (error)
  102. return T_EXIT_FAIL;
  103. bytes_out = receive_cqe(&ring);
  104. if (bytes_in == -ENOTSUP || bytes_out == -ENOTSUP) {
  105. fprintf(stderr, "Skipping tests. -ENOTSUP returned\n");
  106. return T_EXIT_SKIP;
  107. }
  108. /*
  109. * Assert the number of written bytes are either in the socket buffer
  110. * or on the receive side
  111. */
  112. if (bytes_in + bytes_out != written_bytes) {
  113. fprintf(stderr, "values does not match: %zu+%zu != %zu\n",
  114. bytes_in, bytes_out, written_bytes);
  115. return T_EXIT_FAIL;
  116. }
  117. io_uring_queue_exit(&ring);
  118. return T_EXIT_PASS;
  119. }
  120. /*
  121. * Make sure that siocoutq and siocinq returns the same value
  122. * using ioctl(2) and uring commands for raw sockets
  123. */
  124. static int run_test_raw(void)
  125. {
  126. int ioctl_siocoutq, ioctl_siocinq;
  127. int uring_siocoutq, uring_siocinq;
  128. struct io_uring ring;
  129. int retry = 0, sock, error;
  130. sock = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
  131. if (sock == -1) {
  132. /* You need root to create raw socket */
  133. perror("Not able to create a raw socket");
  134. return T_EXIT_SKIP;
  135. }
  136. /* Get the same operation using uring cmd */
  137. error = t_create_ring(1, &ring, 0);
  138. if (error == T_SETUP_SKIP)
  139. return error;
  140. else if (error != T_SETUP_OK)
  141. return T_EXIT_FAIL;
  142. again:
  143. /* Simple SIOCOUTQ using ioctl */
  144. error = ioctl(sock, SIOCOUTQ, &ioctl_siocoutq);
  145. if (error < 0) {
  146. fprintf(stderr, "Failed to run ioctl(SIOCOUTQ): %d\n", error);
  147. return T_EXIT_FAIL;
  148. }
  149. error = ioctl(sock, SIOCINQ, &ioctl_siocinq);
  150. if (error < 0) {
  151. fprintf(stderr, "Failed to run ioctl(SIOCINQ): %d\n", error);
  152. return T_EXIT_FAIL;
  153. }
  154. create_sqe_and_submit(&ring, sock, SOCKET_URING_OP_SIOCOUTQ);
  155. uring_siocoutq = receive_cqe(&ring);
  156. create_sqe_and_submit(&ring, sock, SOCKET_URING_OP_SIOCINQ);
  157. uring_siocinq = receive_cqe(&ring);
  158. /* Compare that both values (ioctl and uring CMD) should be similar */
  159. if (uring_siocoutq != ioctl_siocoutq) {
  160. if (!retry) {
  161. retry = 1;
  162. goto again;
  163. }
  164. fprintf(stderr, "values does not match: %d != %d\n",
  165. uring_siocoutq, ioctl_siocoutq);
  166. return T_EXIT_FAIL;
  167. }
  168. if (uring_siocinq != ioctl_siocinq) {
  169. if (!retry) {
  170. retry = 1;
  171. goto again;
  172. }
  173. fprintf(stderr, "values does not match: %d != %d\n",
  174. uring_siocinq, ioctl_siocinq);
  175. return T_EXIT_FAIL;
  176. }
  177. return T_EXIT_PASS;
  178. }
  179. int main(int argc, char *argv[])
  180. {
  181. int err;
  182. if (argc > 1)
  183. return 0;
  184. /* Test SOCK_STREAM */
  185. err = run_test(true);
  186. if (err)
  187. return err;
  188. if (no_io_cmd)
  189. return T_EXIT_SKIP;
  190. /* Test SOCK_DGRAM */
  191. err = run_test(false);
  192. if (err)
  193. return err;
  194. /* Test raw sockets */
  195. return run_test_raw();
  196. }