1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: run various file registration tests
4 *
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
13 #include "helpers.h"
14 #include "liburing.h"
15
16 static int no_update = 0;
17
close_files(int * files,int nr_files,int add)18 static void close_files(int *files, int nr_files, int add)
19 {
20 char fname[32];
21 int i;
22
23 for (i = 0; i < nr_files; i++) {
24 if (files)
25 close(files[i]);
26 if (!add)
27 sprintf(fname, ".reg.%d", i);
28 else
29 sprintf(fname, ".add.%d", i + add);
30 unlink(fname);
31 }
32 if (files)
33 free(files);
34 }
35
open_files(int nr_files,int extra,int add)36 static int *open_files(int nr_files, int extra, int add)
37 {
38 char fname[32];
39 int *files;
40 int i;
41
42 files = t_calloc(nr_files + extra, sizeof(int));
43
44 for (i = 0; i < nr_files; i++) {
45 if (!add)
46 sprintf(fname, ".reg.%d", i);
47 else
48 sprintf(fname, ".add.%d", i + add);
49 files[i] = open(fname, O_RDWR | O_CREAT, 0644);
50 if (files[i] < 0) {
51 perror("open");
52 free(files);
53 files = NULL;
54 break;
55 }
56 }
57 if (extra) {
58 for (i = nr_files; i < nr_files + extra; i++)
59 files[i] = -1;
60 }
61
62 return files;
63 }
64
test_shrink(struct io_uring * ring)65 static int test_shrink(struct io_uring *ring)
66 {
67 int ret, off, fd;
68 int *files;
69
70 files = open_files(50, 0, 0);
71 ret = io_uring_register_files(ring, files, 50);
72 if (ret) {
73 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
74 goto err;
75 }
76
77 off = 0;
78 do {
79 fd = -1;
80 ret = io_uring_register_files_update(ring, off, &fd, 1);
81 if (ret != 1) {
82 if (off == 50 && ret == -EINVAL)
83 break;
84 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
85 break;
86 }
87 off++;
88 } while (1);
89
90 ret = io_uring_unregister_files(ring);
91 if (ret) {
92 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
93 goto err;
94 }
95
96 close_files(files, 50, 0);
97 return 0;
98 err:
99 close_files(files, 50, 0);
100 return 1;
101 }
102
103
test_grow(struct io_uring * ring)104 static int test_grow(struct io_uring *ring)
105 {
106 int ret, off;
107 int *files, *fds = NULL;
108
109 files = open_files(50, 250, 0);
110 ret = io_uring_register_files(ring, files, 300);
111 if (ret) {
112 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
113 goto err;
114 }
115
116 off = 50;
117 do {
118 fds = open_files(1, 0, off);
119 ret = io_uring_register_files_update(ring, off, fds, 1);
120 if (ret != 1) {
121 if (off == 300 && ret == -EINVAL)
122 break;
123 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
124 break;
125 }
126 if (off >= 300) {
127 fprintf(stderr, "%s: Succeeded beyond end-of-list?\n", __FUNCTION__);
128 goto err;
129 }
130 off++;
131 } while (1);
132
133 ret = io_uring_unregister_files(ring);
134 if (ret) {
135 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
136 goto err;
137 }
138
139 close_files(files, 100, 0);
140 close_files(NULL, 251, 50);
141 return 0;
142 err:
143 close_files(files, 100, 0);
144 close_files(NULL, 251, 50);
145 return 1;
146 }
147
test_replace_all(struct io_uring * ring)148 static int test_replace_all(struct io_uring *ring)
149 {
150 int *files, *fds = NULL;
151 int ret, i;
152
153 files = open_files(100, 0, 0);
154 ret = io_uring_register_files(ring, files, 100);
155 if (ret) {
156 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
157 goto err;
158 }
159
160 fds = t_malloc(100 * sizeof(int));
161 for (i = 0; i < 100; i++)
162 fds[i] = -1;
163
164 ret = io_uring_register_files_update(ring, 0, fds, 100);
165 if (ret != 100) {
166 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
167 goto err;
168 }
169
170 ret = io_uring_unregister_files(ring);
171 if (ret) {
172 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
173 goto err;
174 }
175
176 close_files(files, 100, 0);
177 if (fds)
178 free(fds);
179 return 0;
180 err:
181 close_files(files, 100, 0);
182 if (fds)
183 free(fds);
184 return 1;
185 }
186
test_replace(struct io_uring * ring)187 static int test_replace(struct io_uring *ring)
188 {
189 int *files, *fds = NULL;
190 int ret;
191
192 files = open_files(100, 0, 0);
193 ret = io_uring_register_files(ring, files, 100);
194 if (ret) {
195 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
196 goto err;
197 }
198
199 fds = open_files(10, 0, 1);
200 ret = io_uring_register_files_update(ring, 90, fds, 10);
201 if (ret != 10) {
202 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
203 goto err;
204 }
205
206 ret = io_uring_unregister_files(ring);
207 if (ret) {
208 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
209 goto err;
210 }
211
212 close_files(files, 100, 0);
213 if (fds)
214 close_files(fds, 10, 1);
215 return 0;
216 err:
217 close_files(files, 100, 0);
218 if (fds)
219 close_files(fds, 10, 1);
220 return 1;
221 }
222
test_removals(struct io_uring * ring)223 static int test_removals(struct io_uring *ring)
224 {
225 int *files, *fds = NULL;
226 int ret, i;
227
228 files = open_files(100, 0, 0);
229 ret = io_uring_register_files(ring, files, 100);
230 if (ret) {
231 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
232 goto err;
233 }
234
235 fds = t_calloc(10, sizeof(int));
236 for (i = 0; i < 10; i++)
237 fds[i] = -1;
238
239 ret = io_uring_register_files_update(ring, 50, fds, 10);
240 if (ret != 10) {
241 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
242 goto err;
243 }
244
245 ret = io_uring_unregister_files(ring);
246 if (ret) {
247 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
248 goto err;
249 }
250
251 close_files(files, 100, 0);
252 if (fds)
253 free(fds);
254 return 0;
255 err:
256 close_files(files, 100, 0);
257 if (fds)
258 free(fds);
259 return 1;
260 }
261
test_additions(struct io_uring * ring)262 static int test_additions(struct io_uring *ring)
263 {
264 int *files, *fds = NULL;
265 int ret;
266
267 files = open_files(100, 100, 0);
268 ret = io_uring_register_files(ring, files, 200);
269 if (ret) {
270 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
271 goto err;
272 }
273
274 fds = open_files(2, 0, 1);
275 ret = io_uring_register_files_update(ring, 100, fds, 2);
276 if (ret != 2) {
277 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
278 goto err;
279 }
280
281 ret = io_uring_unregister_files(ring);
282 if (ret) {
283 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
284 goto err;
285 }
286
287 close_files(files, 100, 0);
288 if (fds)
289 close_files(fds, 2, 1);
290 return 0;
291 err:
292 close_files(files, 100, 0);
293 if (fds)
294 close_files(fds, 2, 1);
295 return 1;
296 }
297
test_sparse(struct io_uring * ring)298 static int test_sparse(struct io_uring *ring)
299 {
300 int *files;
301 int ret;
302
303 files = open_files(100, 100, 0);
304 ret = io_uring_register_files(ring, files, 200);
305 if (ret) {
306 if (ret == -EBADF) {
307 fprintf(stdout, "Sparse files not supported\n");
308 no_update = 1;
309 goto done;
310 }
311 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
312 goto err;
313 }
314 ret = io_uring_unregister_files(ring);
315 if (ret) {
316 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
317 goto err;
318 }
319 done:
320 close_files(files, 100, 0);
321 return 0;
322 err:
323 close_files(files, 100, 0);
324 return 1;
325 }
326
test_basic_many(struct io_uring * ring)327 static int test_basic_many(struct io_uring *ring)
328 {
329 int *files;
330 int ret;
331
332 files = open_files(768, 0, 0);
333 ret = io_uring_register_files(ring, files, 768);
334 if (ret) {
335 fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret);
336 goto err;
337 }
338 ret = io_uring_unregister_files(ring);
339 if (ret) {
340 fprintf(stderr, "%s: unregister %d\n", __FUNCTION__, ret);
341 goto err;
342 }
343 close_files(files, 768, 0);
344 return 0;
345 err:
346 close_files(files, 768, 0);
347 return 1;
348 }
349
test_basic(struct io_uring * ring,int fail)350 static int test_basic(struct io_uring *ring, int fail)
351 {
352 int *files;
353 int ret;
354
355 files = open_files(fail ? 10 : 100, 0, 0);
356 ret = io_uring_register_files(ring, files, 100);
357 if (ret) {
358 if (fail) {
359 if (ret == -EBADF || ret == -EFAULT)
360 return 0;
361 }
362 fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret);
363 goto err;
364 }
365 if (fail) {
366 fprintf(stderr, "Registration succeeded, but expected fail\n");
367 goto err;
368 }
369 ret = io_uring_unregister_files(ring);
370 if (ret) {
371 fprintf(stderr, "%s: unregister %d\n", __FUNCTION__, ret);
372 goto err;
373 }
374 close_files(files, 100, 0);
375 return 0;
376 err:
377 close_files(files, 100, 0);
378 return 1;
379 }
380
381 /*
382 * Register 0 files, but reserve space for 10. Then add one file.
383 */
test_zero(struct io_uring * ring)384 static int test_zero(struct io_uring *ring)
385 {
386 int *files, *fds = NULL;
387 int ret;
388
389 files = open_files(0, 10, 0);
390 ret = io_uring_register_files(ring, files, 10);
391 if (ret) {
392 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
393 goto err;
394 }
395
396 fds = open_files(1, 0, 1);
397 ret = io_uring_register_files_update(ring, 0, fds, 1);
398 if (ret != 1) {
399 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
400 goto err;
401 }
402
403 ret = io_uring_unregister_files(ring);
404 if (ret) {
405 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
406 goto err;
407 }
408
409 if (fds)
410 close_files(fds, 1, 1);
411 free(files);
412 return 0;
413 err:
414 if (fds)
415 close_files(fds, 1, 1);
416 free(files);
417 return 1;
418 }
419
test_fixed_read_write(struct io_uring * ring,int index)420 static int test_fixed_read_write(struct io_uring *ring, int index)
421 {
422 struct io_uring_sqe *sqe;
423 struct io_uring_cqe *cqe;
424 struct iovec iov[2];
425 int ret;
426
427 iov[0].iov_base = t_malloc(4096);
428 iov[0].iov_len = 4096;
429 memset(iov[0].iov_base, 0x5a, 4096);
430
431 iov[1].iov_base = t_malloc(4096);
432 iov[1].iov_len = 4096;
433
434 sqe = io_uring_get_sqe(ring);
435 if (!sqe) {
436 fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
437 return 1;
438 }
439 io_uring_prep_writev(sqe, index, &iov[0], 1, 0);
440 sqe->flags |= IOSQE_FIXED_FILE;
441 sqe->user_data = 1;
442
443 ret = io_uring_submit(ring);
444 if (ret != 1) {
445 fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
446 return 1;
447 }
448
449 ret = io_uring_wait_cqe(ring, &cqe);
450 if (ret < 0) {
451 fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
452 return 1;
453 }
454 if (cqe->res != 4096) {
455 fprintf(stderr, "%s: write cqe->res=%d\n", __FUNCTION__, cqe->res);
456 return 1;
457 }
458 io_uring_cqe_seen(ring, cqe);
459
460 sqe = io_uring_get_sqe(ring);
461 if (!sqe) {
462 fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
463 return 1;
464 }
465 io_uring_prep_readv(sqe, index, &iov[1], 1, 0);
466 sqe->flags |= IOSQE_FIXED_FILE;
467 sqe->user_data = 2;
468
469 ret = io_uring_submit(ring);
470 if (ret != 1) {
471 fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
472 return 1;
473 }
474
475 ret = io_uring_wait_cqe(ring, &cqe);
476 if (ret < 0) {
477 fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
478 return 1;
479 }
480 if (cqe->res != 4096) {
481 fprintf(stderr, "%s: read cqe->res=%d\n", __FUNCTION__, cqe->res);
482 return 1;
483 }
484 io_uring_cqe_seen(ring, cqe);
485
486 if (memcmp(iov[1].iov_base, iov[0].iov_base, 4096)) {
487 fprintf(stderr, "%s: data mismatch\n", __FUNCTION__);
488 return 1;
489 }
490
491 free(iov[0].iov_base);
492 free(iov[1].iov_base);
493 return 0;
494 }
495
496 /*
497 * Register 8K of sparse files, update one at a random spot, then do some
498 * file IO to verify it works.
499 */
test_huge(struct io_uring * ring)500 static int test_huge(struct io_uring *ring)
501 {
502 int *files;
503 int ret;
504
505 files = open_files(0, 8192, 0);
506 ret = io_uring_register_files(ring, files, 8192);
507 if (ret) {
508 /* huge sets not supported */
509 if (ret == -EMFILE) {
510 fprintf(stdout, "%s: No huge file set support, skipping\n", __FUNCTION__);
511 goto out;
512 }
513 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
514 goto err;
515 }
516
517 files[7193] = open(".reg.7193", O_RDWR | O_CREAT, 0644);
518 if (files[7193] < 0) {
519 fprintf(stderr, "%s: open=%d\n", __FUNCTION__, errno);
520 goto err;
521 }
522
523 ret = io_uring_register_files_update(ring, 7193, &files[7193], 1);
524 if (ret != 1) {
525 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
526 goto err;
527 }
528
529 if (test_fixed_read_write(ring, 7193))
530 goto err;
531
532 ret = io_uring_unregister_files(ring);
533 if (ret) {
534 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
535 goto err;
536 }
537
538 if (files[7193] != -1) {
539 close(files[7193]);
540 unlink(".reg.7193");
541 }
542 out:
543 free(files);
544 return 0;
545 err:
546 if (files[7193] != -1) {
547 close(files[7193]);
548 unlink(".reg.7193");
549 }
550 free(files);
551 return 1;
552 }
553
test_skip(struct io_uring * ring)554 static int test_skip(struct io_uring *ring)
555 {
556 int *files;
557 int ret;
558
559 files = open_files(100, 0, 0);
560 ret = io_uring_register_files(ring, files, 100);
561 if (ret) {
562 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
563 goto err;
564 }
565
566 files[90] = IORING_REGISTER_FILES_SKIP;
567 ret = io_uring_register_files_update(ring, 90, &files[90], 1);
568 if (ret != 1) {
569 if (ret == -EBADF) {
570 fprintf(stdout, "Skipping files not supported\n");
571 goto done;
572 }
573 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
574 goto err;
575 }
576
577 /* verify can still use file index 90 */
578 if (test_fixed_read_write(ring, 90))
579 goto err;
580
581 ret = io_uring_unregister_files(ring);
582 if (ret) {
583 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
584 goto err;
585 }
586
587 done:
588 close_files(files, 100, 0);
589 return 0;
590 err:
591 close_files(files, 100, 0);
592 return 1;
593 }
594
test_sparse_updates(void)595 static int test_sparse_updates(void)
596 {
597 struct io_uring ring;
598 int ret, i, *fds, newfd;
599
600 ret = io_uring_queue_init(8, &ring, 0);
601 if (ret) {
602 fprintf(stderr, "queue_init: %d\n", ret);
603 return ret;
604 }
605
606 fds = t_malloc(256 * sizeof(int));
607 for (i = 0; i < 256; i++)
608 fds[i] = -1;
609
610 ret = io_uring_register_files(&ring, fds, 256);
611 if (ret) {
612 fprintf(stderr, "file_register: %d\n", ret);
613 return ret;
614 }
615
616 newfd = 1;
617 for (i = 0; i < 256; i++) {
618 ret = io_uring_register_files_update(&ring, i, &newfd, 1);
619 if (ret != 1) {
620 fprintf(stderr, "file_update: %d\n", ret);
621 return ret;
622 }
623 }
624 io_uring_unregister_files(&ring);
625
626 for (i = 0; i < 256; i++)
627 fds[i] = 1;
628
629 ret = io_uring_register_files(&ring, fds, 256);
630 if (ret) {
631 fprintf(stderr, "file_register: %d\n", ret);
632 return ret;
633 }
634
635 newfd = -1;
636 for (i = 0; i < 256; i++) {
637 ret = io_uring_register_files_update(&ring, i, &newfd, 1);
638 if (ret != 1) {
639 fprintf(stderr, "file_update: %d\n", ret);
640 return ret;
641 }
642 }
643 io_uring_unregister_files(&ring);
644
645 io_uring_queue_exit(&ring);
646 return 0;
647 }
648
test_fixed_removal_ordering(void)649 static int test_fixed_removal_ordering(void)
650 {
651 char buffer[128];
652 struct io_uring ring;
653 struct io_uring_sqe *sqe;
654 struct io_uring_cqe *cqe;
655 struct __kernel_timespec ts;
656 int ret, fd, i, fds[2];
657
658 ret = io_uring_queue_init(8, &ring, 0);
659 if (ret < 0) {
660 fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
661 return ret;
662 }
663 if (pipe(fds)) {
664 perror("pipe");
665 return -1;
666 }
667 ret = io_uring_register_files(&ring, fds, 2);
668 if (ret) {
669 fprintf(stderr, "file_register: %d\n", ret);
670 return ret;
671 }
672 /* ring should have fds referenced, can close them */
673 close(fds[0]);
674 close(fds[1]);
675
676 sqe = io_uring_get_sqe(&ring);
677 if (!sqe) {
678 fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
679 return 1;
680 }
681 /* outwait file recycling delay */
682 ts.tv_sec = 3;
683 ts.tv_nsec = 0;
684 io_uring_prep_timeout(sqe, &ts, 0, 0);
685 sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
686 sqe->user_data = 1;
687
688 sqe = io_uring_get_sqe(&ring);
689 if (!sqe) {
690 printf("get sqe failed\n");
691 return -1;
692 }
693 io_uring_prep_write(sqe, 1, buffer, sizeof(buffer), 0);
694 sqe->flags |= IOSQE_FIXED_FILE;
695 sqe->user_data = 2;
696
697 ret = io_uring_submit(&ring);
698 if (ret != 2) {
699 fprintf(stderr, "%s: got %d, wanted 2\n", __FUNCTION__, ret);
700 return -1;
701 }
702
703 /* remove unused pipe end */
704 fd = -1;
705 ret = io_uring_register_files_update(&ring, 0, &fd, 1);
706 if (ret != 1) {
707 fprintf(stderr, "update off=0 failed\n");
708 return -1;
709 }
710
711 /* remove used pipe end */
712 fd = -1;
713 ret = io_uring_register_files_update(&ring, 1, &fd, 1);
714 if (ret != 1) {
715 fprintf(stderr, "update off=1 failed\n");
716 return -1;
717 }
718
719 for (i = 0; i < 2; ++i) {
720 ret = io_uring_wait_cqe(&ring, &cqe);
721 if (ret < 0) {
722 fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
723 return 1;
724 }
725 io_uring_cqe_seen(&ring, cqe);
726 }
727
728 io_uring_queue_exit(&ring);
729 return 0;
730 }
731
732
733
main(int argc,char * argv[])734 int main(int argc, char *argv[])
735 {
736 struct io_uring ring;
737 int ret;
738
739 if (argc > 1)
740 return 0;
741
742 ret = io_uring_queue_init(8, &ring, 0);
743 if (ret) {
744 printf("ring setup failed\n");
745 return 1;
746 }
747
748 ret = test_basic(&ring, 0);
749 if (ret) {
750 printf("test_basic failed\n");
751 return ret;
752 }
753
754 ret = test_basic(&ring, 1);
755 if (ret) {
756 printf("test_basic failed\n");
757 return ret;
758 }
759
760 ret = test_basic_many(&ring);
761 if (ret) {
762 printf("test_basic_many failed\n");
763 return ret;
764 }
765
766 ret = test_sparse(&ring);
767 if (ret) {
768 printf("test_sparse failed\n");
769 return ret;
770 }
771
772 if (no_update)
773 return 0;
774
775 ret = test_additions(&ring);
776 if (ret) {
777 printf("test_additions failed\n");
778 return ret;
779 }
780
781 ret = test_removals(&ring);
782 if (ret) {
783 printf("test_removals failed\n");
784 return ret;
785 }
786
787 ret = test_replace(&ring);
788 if (ret) {
789 printf("test_replace failed\n");
790 return ret;
791 }
792
793 ret = test_replace_all(&ring);
794 if (ret) {
795 printf("test_replace_all failed\n");
796 return ret;
797 }
798
799 ret = test_grow(&ring);
800 if (ret) {
801 printf("test_grow failed\n");
802 return ret;
803 }
804
805 ret = test_shrink(&ring);
806 if (ret) {
807 printf("test_shrink failed\n");
808 return ret;
809 }
810
811 ret = test_zero(&ring);
812 if (ret) {
813 printf("test_zero failed\n");
814 return ret;
815 }
816
817 ret = test_huge(&ring);
818 if (ret) {
819 printf("test_huge failed\n");
820 return ret;
821 }
822
823 ret = test_skip(&ring);
824 if (ret) {
825 printf("test_skip failed\n");
826 return 1;
827 }
828
829 ret = test_sparse_updates();
830 if (ret) {
831 printf("test_sparse_updates failed\n");
832 return ret;
833 }
834
835 ret = test_fixed_removal_ordering();
836 if (ret) {
837 printf("test_fixed_removal_ordering failed\n");
838 return 1;
839 }
840
841 return 0;
842 }
843