regbuf-clone.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: test buffer cloning between rings
  5. *
  6. */
  7. #include <errno.h>
  8. #include <stdio.h>
  9. #include <unistd.h>
  10. #include <stdlib.h>
  11. #include <sys/uio.h>
  12. #include <string.h>
  13. #include <limits.h>
  14. #include <sys/mman.h>
  15. #include <linux/mman.h>
  16. #include "liburing.h"
  17. #include "helpers.h"
  18. #define NR_VECS 64
  19. #define BUF_SIZE 8192
  20. static int no_buf_clone;
  21. static int no_buf_offset;
  22. static void fdinfo_read(struct io_uring *ring)
  23. {
  24. char fd_name[128];
  25. char *buf;
  26. int fd;
  27. buf = malloc(4096);
  28. sprintf(fd_name, "/proc/self/fdinfo/%d", ring->ring_fd);
  29. fd = open(fd_name, O_RDONLY);
  30. if (fd < 0) {
  31. perror("open");
  32. return;
  33. }
  34. do {
  35. int ret = read(fd, buf, 4096);
  36. if (ret < 0) {
  37. perror("fdinfo read");
  38. break;
  39. } else if (ret == 4096) {
  40. continue;
  41. }
  42. break;
  43. } while (1);
  44. close(fd);
  45. free(buf);
  46. }
  47. static int use_buf(struct io_uring *ring, void *addr, int index)
  48. {
  49. struct io_uring_sqe *sqe;
  50. struct io_uring_cqe *cqe;
  51. char src_buf[32];
  52. int fds[2], ret;
  53. fdinfo_read(ring);
  54. if (pipe(fds) < 0)
  55. return -errno;
  56. memset(src_buf, 0xbb, sizeof(src_buf));
  57. sqe = io_uring_get_sqe(ring);
  58. io_uring_prep_read_fixed(sqe, fds[0], addr, sizeof(src_buf), 0, index);
  59. io_uring_submit(ring);
  60. ret = write(fds[1], src_buf, sizeof(src_buf));
  61. if (ret < 0)
  62. return -errno;
  63. ret = io_uring_wait_cqe(ring, &cqe);
  64. if (ret) {
  65. fprintf(stderr, "wait_cqe: %d\n", ret);
  66. return ret;
  67. }
  68. ret = cqe->res;
  69. io_uring_cqe_seen(ring, cqe);
  70. if (ret < 0)
  71. return ret;
  72. close(fds[0]);
  73. close(fds[1]);
  74. return 0;
  75. }
  76. static int test_offsets(void)
  77. {
  78. struct iovec vecs[NR_VECS];
  79. struct io_uring src, dst;
  80. unsigned int i, offset, nr;
  81. int ret;
  82. ret = io_uring_queue_init(1, &src, 0);
  83. if (ret) {
  84. fprintf(stderr, "ring_init: %d\n", ret);
  85. return T_EXIT_FAIL;
  86. }
  87. ret = io_uring_queue_init(1, &dst, 0);
  88. if (ret) {
  89. fprintf(stderr, "ring_init: %d\n", ret);
  90. return T_EXIT_FAIL;
  91. }
  92. for (i = 0; i < NR_VECS; i++) {
  93. if (posix_memalign(&vecs[i].iov_base, 4096, BUF_SIZE))
  94. return T_EXIT_FAIL;
  95. vecs[i].iov_len = BUF_SIZE;
  96. }
  97. ret = io_uring_register_buffers(&src, vecs, NR_VECS);
  98. if (ret < 0) {
  99. if (ret == -ENOMEM)
  100. return T_EXIT_SKIP;
  101. return T_EXIT_FAIL;
  102. }
  103. /* clone half the buffers, src offset 0, but ask for too many */
  104. offset = NR_VECS / 2;
  105. nr = NR_VECS;
  106. ret = io_uring_clone_buffers_offset(&dst, &src, 0, offset, nr, 0);
  107. if (ret != -EOVERFLOW) {
  108. if (ret == -EINVAL) {
  109. no_buf_offset = 1;
  110. return T_EXIT_SKIP;
  111. }
  112. fprintf(stderr, "Offset and too big total failed: %d\n", ret);
  113. return T_EXIT_FAIL;
  114. }
  115. /* ask for too many buffers */
  116. nr = NR_VECS + 1;
  117. ret = io_uring_clone_buffers_offset(&dst, &src, 0, 0, nr, 0);
  118. if (ret != -EINVAL) {
  119. fprintf(stderr, "Too many buffers total failed: %d\n", ret);
  120. return T_EXIT_FAIL;
  121. }
  122. /* clone half the buffers into start of src offset */
  123. nr = NR_VECS / 2;
  124. ret = io_uring_clone_buffers_offset(&dst, &src, 0, nr, nr, 0);
  125. if (ret) {
  126. fprintf(stderr, "Half clone with offset failed: %d\n", ret);
  127. return T_EXIT_FAIL;
  128. }
  129. /* 'nr' offset should be 0 on the src side */
  130. ret = use_buf(&dst, vecs[nr].iov_base, 0);
  131. if (ret) {
  132. fprintf(stderr, "1 use_buf=%d\n", ret);
  133. return T_EXIT_FAIL;
  134. }
  135. ret = io_uring_unregister_buffers(&dst);
  136. if (ret) {
  137. fprintf(stderr, "Failed to unregister partial dst: %d\n", ret);
  138. return T_EXIT_FAIL;
  139. }
  140. ret = use_buf(&dst, vecs[0].iov_base, 0);
  141. if (ret != -EFAULT) {
  142. fprintf(stderr, "2 use_buf=%d\n", ret);
  143. return T_EXIT_FAIL;
  144. }
  145. /* clone half the buffers into middle of src offset */
  146. nr = NR_VECS / 2;
  147. ret = io_uring_clone_buffers_offset(&dst, &src, nr, nr, nr, 0);
  148. if (ret) {
  149. fprintf(stderr, "Half buffers and middle offset failed: %d\n", ret);
  150. return T_EXIT_FAIL;
  151. }
  152. ret = use_buf(&dst, vecs[0].iov_base, 0);
  153. if (ret != -EFAULT) {
  154. fprintf(stderr, "3 use_buf=%d\n", ret);
  155. return T_EXIT_FAIL;
  156. }
  157. ret = use_buf(&dst, vecs[nr].iov_base, nr);
  158. if (ret) {
  159. fprintf(stderr, "4 use_buf=%d\n", ret);
  160. return T_EXIT_FAIL;
  161. }
  162. ret = io_uring_unregister_buffers(&dst);
  163. if (ret) {
  164. fprintf(stderr, "Failed to unregister partial dst: %d\n", ret);
  165. return T_EXIT_FAIL;
  166. }
  167. /* clone buffers, but specify overflowing dst offset */
  168. offset = UINT_MAX - 32;
  169. nr = NR_VECS;
  170. ret = io_uring_clone_buffers_offset(&dst, &src, 0, offset, nr, 0);
  171. if (ret != -EOVERFLOW) {
  172. fprintf(stderr, "Overflow dst offset failed: %d\n", ret);
  173. return T_EXIT_FAIL;
  174. }
  175. /* clone half the buffers into middle of src offset */
  176. nr = NR_VECS / 2;
  177. ret = io_uring_clone_buffers_offset(&dst, &src, nr, nr, nr, 0);
  178. if (ret) {
  179. fprintf(stderr, "Clone half middle src offset failed: %d\n", ret);
  180. return T_EXIT_FAIL;
  181. }
  182. ret = use_buf(&dst, vecs[nr].iov_base, nr);
  183. if (ret) {
  184. fprintf(stderr, "5 use_buf=%d\n", ret);
  185. return T_EXIT_FAIL;
  186. }
  187. ret = use_buf(&dst, vecs[0].iov_base, 0);
  188. if (ret != -EFAULT) {
  189. fprintf(stderr, "5 use_buf=%d\n", ret);
  190. return T_EXIT_FAIL;
  191. }
  192. /* should get -EBUSY now, REPLACE not set */
  193. nr = NR_VECS / 2;
  194. ret = io_uring_clone_buffers_offset(&dst, &src, nr, nr, nr, 0);
  195. if (ret != -EBUSY) {
  196. fprintf(stderr, "Replace buffers failed: %d\n", ret);
  197. return T_EXIT_FAIL;
  198. }
  199. /* now replace the initial 0..n in dst (which are dummy nodes) */
  200. ret = io_uring_clone_buffers_offset(&dst, &src, 0, 0, nr, IORING_REGISTER_DST_REPLACE);
  201. if (ret) {
  202. fprintf(stderr, "Buffer replace failed: %d\n", ret);
  203. return T_EXIT_FAIL;
  204. }
  205. ret = use_buf(&dst, vecs[0].iov_base, 0);
  206. if (ret) {
  207. fprintf(stderr, "6 use_buf=%d\n", ret);
  208. return T_EXIT_FAIL;
  209. }
  210. ret = io_uring_unregister_buffers(&dst);
  211. if (ret) {
  212. fprintf(stderr, "Failed to unregister partial dst: %d\n", ret);
  213. return T_EXIT_FAIL;
  214. }
  215. ret = io_uring_register_buffers_sparse(&dst, NR_VECS);
  216. if (ret) {
  217. fprintf(stderr, "Register sparse buffers failed: %d\n", ret);
  218. return T_EXIT_FAIL;
  219. }
  220. /* dst has a full sparse table, replace first NR_VECS / 2 with bufs */
  221. nr = NR_VECS / 2;
  222. ret = io_uring_clone_buffers_offset(&dst, &src, 0, 0, nr, 0);
  223. if (ret != -EBUSY) {
  224. fprintf(stderr, "Buffer replace failed: %d\n", ret);
  225. return T_EXIT_FAIL;
  226. }
  227. ret = io_uring_clone_buffers_offset(&dst, &src, 0, 0, nr, IORING_REGISTER_DST_REPLACE);
  228. if (ret) {
  229. fprintf(stderr, "Buffer replace failed: %d\n", ret);
  230. return T_EXIT_FAIL;
  231. }
  232. ret = use_buf(&dst, vecs[0].iov_base, 0);
  233. if (ret) {
  234. fprintf(stderr, "7 use_buf=%d\n", ret);
  235. return T_EXIT_FAIL;
  236. }
  237. /* now expand existing dst table, from to NR_VECS + NR_VECS / 2 */
  238. nr = NR_VECS;
  239. offset = NR_VECS / 2;
  240. ret = io_uring_clone_buffers_offset(&dst, &src, offset, 0, nr, IORING_REGISTER_DST_REPLACE);
  241. if (ret) {
  242. fprintf(stderr, "Buffer replace failed: %d\n", ret);
  243. return T_EXIT_FAIL;
  244. }
  245. ret = use_buf(&dst, vecs[0].iov_base, 0);
  246. if (ret) {
  247. fprintf(stderr, "8 use_buf=%d\n", ret);
  248. return T_EXIT_FAIL;
  249. }
  250. offset = NR_VECS + (NR_VECS / 2) - 1;
  251. ret = use_buf(&dst, vecs[NR_VECS - 1].iov_base, offset);
  252. if (ret) {
  253. fprintf(stderr, "8b use_buf=%d\n", ret);
  254. return T_EXIT_FAIL;
  255. }
  256. ret = use_buf(&dst, vecs[NR_VECS / 2].iov_base, NR_VECS);
  257. if (ret) {
  258. fprintf(stderr, "9 use_buf=%d\n", ret);
  259. return T_EXIT_FAIL;
  260. }
  261. return T_EXIT_PASS;
  262. }
  263. static int test(int reg_src, int reg_dst)
  264. {
  265. struct iovec vecs[NR_VECS];
  266. struct io_uring src, dst;
  267. int ret, i;
  268. ret = io_uring_queue_init(1, &src, 0);
  269. if (ret) {
  270. fprintf(stderr, "ring_init: %d\n", ret);
  271. return T_EXIT_FAIL;
  272. }
  273. ret = io_uring_queue_init(1, &dst, 0);
  274. if (ret) {
  275. fprintf(stderr, "ring_init: %d\n", ret);
  276. return T_EXIT_FAIL;
  277. }
  278. if (reg_src) {
  279. ret = io_uring_register_ring_fd(&src);
  280. if (ret < 0) {
  281. if (ret == -EINVAL)
  282. return T_EXIT_SKIP;
  283. fprintf(stderr, "register ring: %d\n", ret);
  284. return T_EXIT_FAIL;
  285. }
  286. }
  287. if (reg_dst) {
  288. ret = io_uring_register_ring_fd(&dst);
  289. if (ret < 0) {
  290. if (ret == -EINVAL)
  291. return T_EXIT_SKIP;
  292. fprintf(stderr, "register ring: %d\n", ret);
  293. return T_EXIT_FAIL;
  294. }
  295. }
  296. /* test fail with no buffers in src */
  297. ret = io_uring_clone_buffers(&dst, &src);
  298. if (ret == -EINVAL) {
  299. /* no buffer copy support */
  300. no_buf_clone = true;
  301. return T_EXIT_SKIP;
  302. } else if (ret != -ENXIO) {
  303. fprintf(stderr, "empty copy: %d\n", ret);
  304. return T_EXIT_FAIL;
  305. }
  306. for (i = 0; i < NR_VECS; i++) {
  307. if (posix_memalign(&vecs[i].iov_base, 4096, BUF_SIZE))
  308. return T_EXIT_FAIL;
  309. vecs[i].iov_len = BUF_SIZE;
  310. }
  311. ret = io_uring_register_buffers(&src, vecs, NR_VECS);
  312. if (ret < 0) {
  313. if (ret == -ENOMEM)
  314. return T_EXIT_SKIP;
  315. return T_EXIT_FAIL;
  316. }
  317. ret = use_buf(&src, vecs[0].iov_base, 0);
  318. if (ret) {
  319. fprintf(stderr, "use_buf=%d\n", ret);
  320. return T_EXIT_FAIL;
  321. }
  322. ret = use_buf(&dst, vecs[0].iov_base, 0);
  323. if (ret != -EFAULT) {
  324. fprintf(stderr, "use_buf=%d\n", ret);
  325. return T_EXIT_FAIL;
  326. }
  327. /* copy should work now */
  328. ret = io_uring_clone_buffers(&dst, &src);
  329. if (ret) {
  330. fprintf(stderr, "buffer copy: %d\n", ret);
  331. return T_EXIT_FAIL;
  332. }
  333. ret = use_buf(&dst, vecs[NR_VECS / 2].iov_base, NR_VECS / 2);
  334. if (ret) {
  335. fprintf(stderr, "use_buf=%d\n", ret);
  336. return T_EXIT_FAIL;
  337. }
  338. /* try copy again, should get -EBUSY */
  339. ret = io_uring_clone_buffers(&dst, &src);
  340. if (ret != -EBUSY) {
  341. fprintf(stderr, "busy copy: %d\n", ret);
  342. return T_EXIT_FAIL;
  343. }
  344. ret = io_uring_unregister_buffers(&dst);
  345. if (ret) {
  346. fprintf(stderr, "dst unregister buffers: %d\n", ret);
  347. return T_EXIT_FAIL;
  348. }
  349. ret = use_buf(&dst, vecs[NR_VECS / 2].iov_base, NR_VECS / 2);
  350. if (ret != -EFAULT) {
  351. fprintf(stderr, "use_buf=%d\n", ret);
  352. return T_EXIT_FAIL;
  353. }
  354. ret = io_uring_unregister_buffers(&dst);
  355. if (ret != -ENXIO) {
  356. fprintf(stderr, "dst unregister empty buffers: %d\n", ret);
  357. return T_EXIT_FAIL;
  358. }
  359. ret = use_buf(&src, vecs[NR_VECS / 2].iov_base, NR_VECS / 2);
  360. if (ret) {
  361. fprintf(stderr, "use_buf=%d\n", ret);
  362. return T_EXIT_FAIL;
  363. }
  364. ret = io_uring_unregister_buffers(&src);
  365. if (ret) {
  366. fprintf(stderr, "src unregister buffers: %d\n", ret);
  367. return T_EXIT_FAIL;
  368. }
  369. ret = use_buf(&src, vecs[NR_VECS / 2].iov_base, NR_VECS / 2);
  370. if (ret != -EFAULT) {
  371. fprintf(stderr, "use_buf=%d\n", ret);
  372. return T_EXIT_FAIL;
  373. }
  374. ret = io_uring_register_buffers(&dst, vecs, NR_VECS);
  375. if (ret < 0) {
  376. fprintf(stderr, "register buffers dst; %d\n", ret);
  377. return T_EXIT_FAIL;
  378. }
  379. ret = io_uring_clone_buffers(&src, &dst);
  380. if (ret) {
  381. fprintf(stderr, "buffer copy reverse: %d\n", ret);
  382. return T_EXIT_FAIL;
  383. }
  384. ret = io_uring_unregister_buffers(&dst);
  385. if (ret) {
  386. fprintf(stderr, "dst unregister buffers: %d\n", ret);
  387. return T_EXIT_FAIL;
  388. }
  389. ret = io_uring_unregister_buffers(&dst);
  390. if (ret != -ENXIO) {
  391. fprintf(stderr, "dst unregister empty buffers: %d\n", ret);
  392. return T_EXIT_FAIL;
  393. }
  394. ret = io_uring_unregister_buffers(&src);
  395. if (ret) {
  396. fprintf(stderr, "src unregister buffers: %d\n", ret);
  397. return T_EXIT_FAIL;
  398. }
  399. io_uring_queue_exit(&src);
  400. io_uring_queue_exit(&dst);
  401. for (i = 0; i < NR_VECS; i++)
  402. free(vecs[i].iov_base);
  403. return T_EXIT_PASS;
  404. }
  405. static int test_dummy(void)
  406. {
  407. struct iovec vec = { };
  408. struct io_uring src, dst;
  409. int ret;
  410. ret = io_uring_queue_init(1, &src, 0);
  411. if (ret) {
  412. fprintf(stderr, "ring_init: %d\n", ret);
  413. return T_EXIT_FAIL;
  414. }
  415. ret = io_uring_queue_init(1, &dst, 0);
  416. if (ret) {
  417. fprintf(stderr, "ring_init: %d\n", ret);
  418. return T_EXIT_FAIL;
  419. }
  420. ret = io_uring_register_buffers(&src, &vec, 1);
  421. if (ret < 0) {
  422. fprintf(stderr, "failed to register dummy buffer: %d\n", ret);
  423. return T_EXIT_FAIL;
  424. }
  425. ret = io_uring_clone_buffers(&dst, &src);
  426. if (ret) {
  427. fprintf(stderr, "clone dummy buf: %d\n", ret);
  428. return T_EXIT_FAIL;
  429. }
  430. ret = io_uring_unregister_buffers(&src);
  431. if (ret) {
  432. fprintf(stderr, "rsc unregister buffers: %d\n", ret);
  433. return T_EXIT_FAIL;
  434. }
  435. ret = io_uring_unregister_buffers(&dst);
  436. if (ret) {
  437. fprintf(stderr, "dst unregister buffers: %d\n", ret);
  438. return T_EXIT_FAIL;
  439. }
  440. io_uring_queue_exit(&src);
  441. io_uring_queue_exit(&dst);
  442. return T_EXIT_PASS;
  443. }
  444. /*
  445. * Register sparse buffer table, then try updating that with a few huge
  446. * page entries.
  447. */
  448. static int test_merge(void)
  449. {
  450. int ret, res = T_EXIT_SKIP;
  451. struct iovec vecs[8];
  452. struct io_uring ring;
  453. __u64 tags[2];
  454. void *p1;
  455. p1 = mmap(NULL, 2*1024*1024, PROT_READ|PROT_WRITE,
  456. MAP_PRIVATE|MAP_HUGETLB | MAP_HUGE_2MB | MAP_ANONYMOUS,
  457. -1, 0);
  458. if (p1 == MAP_FAILED)
  459. return T_EXIT_SKIP;
  460. ret = io_uring_queue_init(1, &ring, 0);
  461. if (ret) {
  462. fprintf(stderr, "ring_init: %d\n", ret);
  463. return T_EXIT_FAIL;
  464. }
  465. memset(vecs, 0, sizeof(vecs));
  466. ret = io_uring_register_buffers(&ring, vecs, 8);
  467. if (ret < 0) {
  468. if (ret == -EINVAL)
  469. goto skip;
  470. fprintf(stderr, "failed to register initial buffers: %d\n", ret);
  471. return T_EXIT_FAIL;
  472. }
  473. vecs[0].iov_base = p1;
  474. vecs[0].iov_len = 4096;
  475. vecs[1].iov_base = p1 + 4096;
  476. vecs[1].iov_len = 4096;
  477. tags[0] = 1;
  478. tags[1] = 2;
  479. ret = io_uring_register_buffers_update_tag(&ring, 4, vecs, tags, 2);
  480. if (ret < 0) {
  481. if (ret == -EINVAL)
  482. goto skip;
  483. fprintf(stderr, "failed to register merge buffers: %d\n", ret);
  484. return T_EXIT_FAIL;
  485. }
  486. res = T_EXIT_PASS;
  487. skip:
  488. munmap(p1, 2*1024*1024);
  489. io_uring_queue_exit(&ring);
  490. return res;
  491. }
  492. int main(int argc, char *argv[])
  493. {
  494. int ret;
  495. if (argc > 1)
  496. return T_EXIT_SKIP;
  497. ret = test_merge();
  498. if (ret == T_EXIT_FAIL) {
  499. fprintf(stderr, "test_merge failed\n");
  500. return T_EXIT_FAIL;
  501. }
  502. ret = test(0, 0);
  503. if (ret == T_EXIT_SKIP) {
  504. return T_EXIT_SKIP;
  505. } else if (ret != T_EXIT_PASS) {
  506. fprintf(stderr, "test 0 0 failed\n");
  507. return T_EXIT_FAIL;
  508. }
  509. if (no_buf_clone)
  510. return T_EXIT_SKIP;
  511. ret = test(0, 1);
  512. if (ret == T_EXIT_SKIP) {
  513. return T_EXIT_SKIP;
  514. } else if (ret != T_EXIT_PASS) {
  515. fprintf(stderr, "test 0 1 failed\n");
  516. return T_EXIT_FAIL;
  517. }
  518. ret = test(1, 0);
  519. if (ret == T_EXIT_SKIP) {
  520. return T_EXIT_SKIP;
  521. } else if (ret != T_EXIT_PASS) {
  522. fprintf(stderr, "test 1 0 failed\n");
  523. return T_EXIT_FAIL;
  524. }
  525. ret = test(1, 1);
  526. if (ret == T_EXIT_SKIP) {
  527. return T_EXIT_SKIP;
  528. } else if (ret != T_EXIT_PASS) {
  529. fprintf(stderr, "test 1 1 failed\n");
  530. return T_EXIT_FAIL;
  531. }
  532. ret = test_dummy();
  533. if (ret == T_EXIT_SKIP) {
  534. return T_EXIT_SKIP;
  535. } else if (ret != T_EXIT_PASS) {
  536. fprintf(stderr, "test_dummy failed\n");
  537. return T_EXIT_FAIL;
  538. }
  539. ret = test_offsets();
  540. if (ret == T_EXIT_SKIP) {
  541. return T_EXIT_PASS;
  542. } else if (ret != T_EXIT_PASS) {
  543. fprintf(stderr, "test_offset failed\n");
  544. return T_EXIT_FAIL;
  545. }
  546. if (no_buf_offset)
  547. return T_EXIT_PASS;
  548. return T_EXIT_PASS;
  549. }