diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-05-05 02:35:00 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-05-05 02:35:00 -0400 |
commit | 1fb48f8e54e5ed4d3d8599ba7e83f1f60530c81c (patch) | |
tree | 6b8c1ccdd461e211f72c674d183f5129f5fe4a5b /tools | |
parent | 778843f934e362ed4ed734520f60a44a78a074b4 (diff) | |
parent | 04974df8049fc4240d22759a91e035082ccd18b4 (diff) |
Merge tag 'v4.6-rc6' into x86/asm, to refresh the tree
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/objtool/Documentation/stack-validation.txt | 38 | ||||
-rw-r--r-- | tools/objtool/builtin-check.c | 97 | ||||
-rw-r--r-- | tools/perf/util/intel-pt.c | 2 | ||||
-rw-r--r-- | tools/testing/selftests/net/.gitignore | 1 | ||||
-rw-r--r-- | tools/testing/selftests/net/Makefile | 2 | ||||
-rw-r--r-- | tools/testing/selftests/net/reuseport_dualstack.c | 208 |
6 files changed, 312 insertions, 36 deletions
diff --git a/tools/objtool/Documentation/stack-validation.txt b/tools/objtool/Documentation/stack-validation.txt index 5a95896105bc..55a60d331f47 100644 --- a/tools/objtool/Documentation/stack-validation.txt +++ b/tools/objtool/Documentation/stack-validation.txt | |||
@@ -299,18 +299,38 @@ they mean, and suggestions for how to fix them. | |||
299 | Errors in .c files | 299 | Errors in .c files |
300 | ------------------ | 300 | ------------------ |
301 | 301 | ||
302 | If you're getting an objtool error in a compiled .c file, chances are | 302 | 1. c_file.o: warning: objtool: funcA() falls through to next function funcB() |
303 | the file uses an asm() statement which has a "call" instruction. An | ||
304 | asm() statement with a call instruction must declare the use of the | ||
305 | stack pointer in its output operand. For example, on x86_64: | ||
306 | 303 | ||
307 | register void *__sp asm("rsp"); | 304 | This means that funcA() doesn't end with a return instruction or an |
308 | asm volatile("call func" : "+r" (__sp)); | 305 | unconditional jump, and that objtool has determined that the function |
306 | can fall through into the next function. There could be different | ||
307 | reasons for this: | ||
309 | 308 | ||
310 | Otherwise the stack frame may not get created before the call. | 309 | 1) funcA()'s last instruction is a call to a "noreturn" function like |
310 | panic(). In this case the noreturn function needs to be added to | ||
311 | objtool's hard-coded global_noreturns array. Feel free to bug the | ||
312 | objtool maintainer, or you can submit a patch. | ||
311 | 313 | ||
312 | Another possible cause for errors in C code is if the Makefile removes | 314 | 2) funcA() uses the unreachable() annotation in a section of code |
313 | -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. | 315 | that is actually reachable. |
316 | |||
317 | 3) If funcA() calls an inline function, the object code for funcA() | ||
318 | might be corrupt due to a gcc bug. For more details, see: | ||
319 | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646 | ||
320 | |||
321 | 2. If you're getting any other objtool error in a compiled .c file, it | ||
322 | may be because the file uses an asm() statement which has a "call" | ||
323 | instruction. An asm() statement with a call instruction must declare | ||
324 | the use of the stack pointer in its output operand. For example, on | ||
325 | x86_64: | ||
326 | |||
327 | register void *__sp asm("rsp"); | ||
328 | asm volatile("call func" : "+r" (__sp)); | ||
329 | |||
330 | Otherwise the stack frame may not get created before the call. | ||
331 | |||
332 | 3. Another possible cause for errors in C code is if the Makefile removes | ||
333 | -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. | ||
314 | 334 | ||
315 | Also see the above section for .S file errors for more information what | 335 | Also see the above section for .S file errors for more information what |
316 | the individual error messages mean. | 336 | the individual error messages mean. |
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 7515cb2e879a..e8a1e69eb92c 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c | |||
@@ -54,6 +54,7 @@ struct instruction { | |||
54 | struct symbol *call_dest; | 54 | struct symbol *call_dest; |
55 | struct instruction *jump_dest; | 55 | struct instruction *jump_dest; |
56 | struct list_head alts; | 56 | struct list_head alts; |
57 | struct symbol *func; | ||
57 | }; | 58 | }; |
58 | 59 | ||
59 | struct alternative { | 60 | struct alternative { |
@@ -66,6 +67,7 @@ struct objtool_file { | |||
66 | struct list_head insn_list; | 67 | struct list_head insn_list; |
67 | DECLARE_HASHTABLE(insn_hash, 16); | 68 | DECLARE_HASHTABLE(insn_hash, 16); |
68 | struct section *rodata, *whitelist; | 69 | struct section *rodata, *whitelist; |
70 | bool ignore_unreachables, c_file; | ||
69 | }; | 71 | }; |
70 | 72 | ||
71 | const char *objname; | 73 | const char *objname; |
@@ -228,7 +230,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, | |||
228 | } | 230 | } |
229 | } | 231 | } |
230 | 232 | ||
231 | if (insn->type == INSN_JUMP_DYNAMIC) | 233 | if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts)) |
232 | /* sibling call */ | 234 | /* sibling call */ |
233 | return 0; | 235 | return 0; |
234 | } | 236 | } |
@@ -248,6 +250,7 @@ static int dead_end_function(struct objtool_file *file, struct symbol *func) | |||
248 | static int decode_instructions(struct objtool_file *file) | 250 | static int decode_instructions(struct objtool_file *file) |
249 | { | 251 | { |
250 | struct section *sec; | 252 | struct section *sec; |
253 | struct symbol *func; | ||
251 | unsigned long offset; | 254 | unsigned long offset; |
252 | struct instruction *insn; | 255 | struct instruction *insn; |
253 | int ret; | 256 | int ret; |
@@ -281,6 +284,21 @@ static int decode_instructions(struct objtool_file *file) | |||
281 | hash_add(file->insn_hash, &insn->hash, insn->offset); | 284 | hash_add(file->insn_hash, &insn->hash, insn->offset); |
282 | list_add_tail(&insn->list, &file->insn_list); | 285 | list_add_tail(&insn->list, &file->insn_list); |
283 | } | 286 | } |
287 | |||
288 | list_for_each_entry(func, &sec->symbol_list, list) { | ||
289 | if (func->type != STT_FUNC) | ||
290 | continue; | ||
291 | |||
292 | if (!find_insn(file, sec, func->offset)) { | ||
293 | WARN("%s(): can't find starting instruction", | ||
294 | func->name); | ||
295 | return -1; | ||
296 | } | ||
297 | |||
298 | func_for_each_insn(file, func, insn) | ||
299 | if (!insn->func) | ||
300 | insn->func = func; | ||
301 | } | ||
284 | } | 302 | } |
285 | 303 | ||
286 | return 0; | 304 | return 0; |
@@ -664,13 +682,40 @@ static int add_func_switch_tables(struct objtool_file *file, | |||
664 | text_rela->addend); | 682 | text_rela->addend); |
665 | 683 | ||
666 | /* | 684 | /* |
667 | * TODO: Document where this is needed, or get rid of it. | ||
668 | * | ||
669 | * rare case: jmpq *[addr](%rip) | 685 | * rare case: jmpq *[addr](%rip) |
686 | * | ||
687 | * This check is for a rare gcc quirk, currently only seen in | ||
688 | * three driver functions in the kernel, only with certain | ||
689 | * obscure non-distro configs. | ||
690 | * | ||
691 | * As part of an optimization, gcc makes a copy of an existing | ||
692 | * switch jump table, modifies it, and then hard-codes the jump | ||
693 | * (albeit with an indirect jump) to use a single entry in the | ||
694 | * table. The rest of the jump table and some of its jump | ||
695 | * targets remain as dead code. | ||
696 | * | ||
697 | * In such a case we can just crudely ignore all unreachable | ||
698 | * instruction warnings for the entire object file. Ideally we | ||
699 | * would just ignore them for the function, but that would | ||
700 | * require redesigning the code quite a bit. And honestly | ||
701 | * that's just not worth doing: unreachable instruction | ||
702 | * warnings are of questionable value anyway, and this is such | ||
703 | * a rare issue. | ||
704 | * | ||
705 | * kbuild reports: | ||
706 | * - https://lkml.kernel.org/r/201603231906.LWcVUpxm%25fengguang.wu@intel.com | ||
707 | * - https://lkml.kernel.org/r/201603271114.K9i45biy%25fengguang.wu@intel.com | ||
708 | * - https://lkml.kernel.org/r/201603291058.zuJ6ben1%25fengguang.wu@intel.com | ||
709 | * | ||
710 | * gcc bug: | ||
711 | * - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70604 | ||
670 | */ | 712 | */ |
671 | if (!rodata_rela) | 713 | if (!rodata_rela) { |
672 | rodata_rela = find_rela_by_dest(file->rodata, | 714 | rodata_rela = find_rela_by_dest(file->rodata, |
673 | text_rela->addend + 4); | 715 | text_rela->addend + 4); |
716 | if (rodata_rela) | ||
717 | file->ignore_unreachables = true; | ||
718 | } | ||
674 | 719 | ||
675 | if (!rodata_rela) | 720 | if (!rodata_rela) |
676 | continue; | 721 | continue; |
@@ -732,9 +777,6 @@ static int decode_sections(struct objtool_file *file) | |||
732 | { | 777 | { |
733 | int ret; | 778 | int ret; |
734 | 779 | ||
735 | file->whitelist = find_section_by_name(file->elf, "__func_stack_frame_non_standard"); | ||
736 | file->rodata = find_section_by_name(file->elf, ".rodata"); | ||
737 | |||
738 | ret = decode_instructions(file); | 780 | ret = decode_instructions(file); |
739 | if (ret) | 781 | if (ret) |
740 | return ret; | 782 | return ret; |
@@ -799,6 +841,7 @@ static int validate_branch(struct objtool_file *file, | |||
799 | struct alternative *alt; | 841 | struct alternative *alt; |
800 | struct instruction *insn; | 842 | struct instruction *insn; |
801 | struct section *sec; | 843 | struct section *sec; |
844 | struct symbol *func = NULL; | ||
802 | unsigned char state; | 845 | unsigned char state; |
803 | int ret; | 846 | int ret; |
804 | 847 | ||
@@ -813,6 +856,16 @@ static int validate_branch(struct objtool_file *file, | |||
813 | } | 856 | } |
814 | 857 | ||
815 | while (1) { | 858 | while (1) { |
859 | if (file->c_file && insn->func) { | ||
860 | if (func && func != insn->func) { | ||
861 | WARN("%s() falls through to next function %s()", | ||
862 | func->name, insn->func->name); | ||
863 | return 1; | ||
864 | } | ||
865 | |||
866 | func = insn->func; | ||
867 | } | ||
868 | |||
816 | if (insn->visited) { | 869 | if (insn->visited) { |
817 | if (frame_state(insn->state) != frame_state(state)) { | 870 | if (frame_state(insn->state) != frame_state(state)) { |
818 | WARN_FUNC("frame pointer state mismatch", | 871 | WARN_FUNC("frame pointer state mismatch", |
@@ -823,13 +876,6 @@ static int validate_branch(struct objtool_file *file, | |||
823 | return 0; | 876 | return 0; |
824 | } | 877 | } |
825 | 878 | ||
826 | /* | ||
827 | * Catch a rare case where a noreturn function falls through to | ||
828 | * the next function. | ||
829 | */ | ||
830 | if (is_fentry_call(insn) && (state & STATE_FENTRY)) | ||
831 | return 0; | ||
832 | |||
833 | insn->visited = true; | 879 | insn->visited = true; |
834 | insn->state = state; | 880 | insn->state = state; |
835 | 881 | ||
@@ -1035,12 +1081,8 @@ static int validate_functions(struct objtool_file *file) | |||
1035 | continue; | 1081 | continue; |
1036 | 1082 | ||
1037 | insn = find_insn(file, sec, func->offset); | 1083 | insn = find_insn(file, sec, func->offset); |
1038 | if (!insn) { | 1084 | if (!insn) |
1039 | WARN("%s(): can't find starting instruction", | ||
1040 | func->name); | ||
1041 | warnings++; | ||
1042 | continue; | 1085 | continue; |
1043 | } | ||
1044 | 1086 | ||
1045 | ret = validate_branch(file, insn, 0); | 1087 | ret = validate_branch(file, insn, 0); |
1046 | warnings += ret; | 1088 | warnings += ret; |
@@ -1056,13 +1098,14 @@ static int validate_functions(struct objtool_file *file) | |||
1056 | if (insn->visited) | 1098 | if (insn->visited) |
1057 | continue; | 1099 | continue; |
1058 | 1100 | ||
1059 | if (!ignore_unreachable_insn(func, insn) && | ||
1060 | !warnings) { | ||
1061 | WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset); | ||
1062 | warnings++; | ||
1063 | } | ||
1064 | |||
1065 | insn->visited = true; | 1101 | insn->visited = true; |
1102 | |||
1103 | if (file->ignore_unreachables || warnings || | ||
1104 | ignore_unreachable_insn(func, insn)) | ||
1105 | continue; | ||
1106 | |||
1107 | WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset); | ||
1108 | warnings++; | ||
1066 | } | 1109 | } |
1067 | } | 1110 | } |
1068 | } | 1111 | } |
@@ -1133,6 +1176,10 @@ int cmd_check(int argc, const char **argv) | |||
1133 | 1176 | ||
1134 | INIT_LIST_HEAD(&file.insn_list); | 1177 | INIT_LIST_HEAD(&file.insn_list); |
1135 | hash_init(file.insn_hash); | 1178 | hash_init(file.insn_hash); |
1179 | file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard"); | ||
1180 | file.rodata = find_section_by_name(file.elf, ".rodata"); | ||
1181 | file.ignore_unreachables = false; | ||
1182 | file.c_file = find_section_by_name(file.elf, ".comment"); | ||
1136 | 1183 | ||
1137 | ret = decode_sections(&file); | 1184 | ret = decode_sections(&file); |
1138 | if (ret < 0) | 1185 | if (ret < 0) |
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 407f11b97c8d..617578440989 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c | |||
@@ -1130,7 +1130,7 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) | |||
1130 | pr_err("Intel Processor Trace: failed to deliver transaction event, error %d\n", | 1130 | pr_err("Intel Processor Trace: failed to deliver transaction event, error %d\n", |
1131 | ret); | 1131 | ret); |
1132 | 1132 | ||
1133 | if (pt->synth_opts.callchain) | 1133 | if (pt->synth_opts.last_branch) |
1134 | intel_pt_reset_last_branch_rb(ptq); | 1134 | intel_pt_reset_last_branch_rb(ptq); |
1135 | 1135 | ||
1136 | return ret; | 1136 | return ret; |
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 69bb3fc38fb2..0840684deb7d 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore | |||
@@ -3,3 +3,4 @@ psock_fanout | |||
3 | psock_tpacket | 3 | psock_tpacket |
4 | reuseport_bpf | 4 | reuseport_bpf |
5 | reuseport_bpf_cpu | 5 | reuseport_bpf_cpu |
6 | reuseport_dualstack | ||
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index c658792d47b4..0e5340742620 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile | |||
@@ -4,7 +4,7 @@ CFLAGS = -Wall -O2 -g | |||
4 | 4 | ||
5 | CFLAGS += -I../../../../usr/include/ | 5 | CFLAGS += -I../../../../usr/include/ |
6 | 6 | ||
7 | NET_PROGS = socket psock_fanout psock_tpacket reuseport_bpf reuseport_bpf_cpu | 7 | NET_PROGS = socket psock_fanout psock_tpacket reuseport_bpf reuseport_bpf_cpu reuseport_dualstack |
8 | 8 | ||
9 | all: $(NET_PROGS) | 9 | all: $(NET_PROGS) |
10 | %: %.c | 10 | %: %.c |
diff --git a/tools/testing/selftests/net/reuseport_dualstack.c b/tools/testing/selftests/net/reuseport_dualstack.c new file mode 100644 index 000000000000..90958aaaafb9 --- /dev/null +++ b/tools/testing/selftests/net/reuseport_dualstack.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * It is possible to use SO_REUSEPORT to open multiple sockets bound to | ||
3 | * equivalent local addresses using AF_INET and AF_INET6 at the same time. If | ||
4 | * the AF_INET6 socket has IPV6_V6ONLY set, it's clear which socket should | ||
5 | * receive a given incoming packet. However, when it is not set, incoming v4 | ||
6 | * packets should prefer the AF_INET socket(s). This behavior was defined with | ||
7 | * the original SO_REUSEPORT implementation, but broke with | ||
8 | * e32ea7e74727 ("soreuseport: fast reuseport UDP socket selection") | ||
9 | * This test creates these mixed AF_INET/AF_INET6 sockets and asserts the | ||
10 | * AF_INET preference for v4 packets. | ||
11 | */ | ||
12 | |||
13 | #define _GNU_SOURCE | ||
14 | |||
15 | #include <arpa/inet.h> | ||
16 | #include <errno.h> | ||
17 | #include <error.h> | ||
18 | #include <linux/in.h> | ||
19 | #include <linux/unistd.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <string.h> | ||
23 | #include <sys/epoll.h> | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/socket.h> | ||
26 | #include <unistd.h> | ||
27 | |||
28 | static const int PORT = 8888; | ||
29 | |||
30 | static void build_rcv_fd(int family, int proto, int *rcv_fds, int count) | ||
31 | { | ||
32 | struct sockaddr_storage addr; | ||
33 | struct sockaddr_in *addr4; | ||
34 | struct sockaddr_in6 *addr6; | ||
35 | int opt, i; | ||
36 | |||
37 | switch (family) { | ||
38 | case AF_INET: | ||
39 | addr4 = (struct sockaddr_in *)&addr; | ||
40 | addr4->sin_family = AF_INET; | ||
41 | addr4->sin_addr.s_addr = htonl(INADDR_ANY); | ||
42 | addr4->sin_port = htons(PORT); | ||
43 | break; | ||
44 | case AF_INET6: | ||
45 | addr6 = (struct sockaddr_in6 *)&addr; | ||
46 | addr6->sin6_family = AF_INET6; | ||
47 | addr6->sin6_addr = in6addr_any; | ||
48 | addr6->sin6_port = htons(PORT); | ||
49 | break; | ||
50 | default: | ||
51 | error(1, 0, "Unsupported family %d", family); | ||
52 | } | ||
53 | |||
54 | for (i = 0; i < count; ++i) { | ||
55 | rcv_fds[i] = socket(family, proto, 0); | ||
56 | if (rcv_fds[i] < 0) | ||
57 | error(1, errno, "failed to create receive socket"); | ||
58 | |||
59 | opt = 1; | ||
60 | if (setsockopt(rcv_fds[i], SOL_SOCKET, SO_REUSEPORT, &opt, | ||
61 | sizeof(opt))) | ||
62 | error(1, errno, "failed to set SO_REUSEPORT"); | ||
63 | |||
64 | if (bind(rcv_fds[i], (struct sockaddr *)&addr, sizeof(addr))) | ||
65 | error(1, errno, "failed to bind receive socket"); | ||
66 | |||
67 | if (proto == SOCK_STREAM && listen(rcv_fds[i], 10)) | ||
68 | error(1, errno, "failed to listen on receive port"); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | static void send_from_v4(int proto) | ||
73 | { | ||
74 | struct sockaddr_in saddr, daddr; | ||
75 | int fd; | ||
76 | |||
77 | saddr.sin_family = AF_INET; | ||
78 | saddr.sin_addr.s_addr = htonl(INADDR_ANY); | ||
79 | saddr.sin_port = 0; | ||
80 | |||
81 | daddr.sin_family = AF_INET; | ||
82 | daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||
83 | daddr.sin_port = htons(PORT); | ||
84 | |||
85 | fd = socket(AF_INET, proto, 0); | ||
86 | if (fd < 0) | ||
87 | error(1, errno, "failed to create send socket"); | ||
88 | |||
89 | if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr))) | ||
90 | error(1, errno, "failed to bind send socket"); | ||
91 | |||
92 | if (connect(fd, (struct sockaddr *)&daddr, sizeof(daddr))) | ||
93 | error(1, errno, "failed to connect send socket"); | ||
94 | |||
95 | if (send(fd, "a", 1, 0) < 0) | ||
96 | error(1, errno, "failed to send message"); | ||
97 | |||
98 | close(fd); | ||
99 | } | ||
100 | |||
101 | static int receive_once(int epfd, int proto) | ||
102 | { | ||
103 | struct epoll_event ev; | ||
104 | int i, fd; | ||
105 | char buf[8]; | ||
106 | |||
107 | i = epoll_wait(epfd, &ev, 1, -1); | ||
108 | if (i < 0) | ||
109 | error(1, errno, "epoll_wait failed"); | ||
110 | |||
111 | if (proto == SOCK_STREAM) { | ||
112 | fd = accept(ev.data.fd, NULL, NULL); | ||
113 | if (fd < 0) | ||
114 | error(1, errno, "failed to accept"); | ||
115 | i = recv(fd, buf, sizeof(buf), 0); | ||
116 | close(fd); | ||
117 | } else { | ||
118 | i = recv(ev.data.fd, buf, sizeof(buf), 0); | ||
119 | } | ||
120 | |||
121 | if (i < 0) | ||
122 | error(1, errno, "failed to recv"); | ||
123 | |||
124 | return ev.data.fd; | ||
125 | } | ||
126 | |||
127 | static void test(int *rcv_fds, int count, int proto) | ||
128 | { | ||
129 | struct epoll_event ev; | ||
130 | int epfd, i, test_fd; | ||
131 | uint16_t test_family; | ||
132 | socklen_t len; | ||
133 | |||
134 | epfd = epoll_create(1); | ||
135 | if (epfd < 0) | ||
136 | error(1, errno, "failed to create epoll"); | ||
137 | |||
138 | ev.events = EPOLLIN; | ||
139 | for (i = 0; i < count; ++i) { | ||
140 | ev.data.fd = rcv_fds[i]; | ||
141 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fds[i], &ev)) | ||
142 | error(1, errno, "failed to register sock epoll"); | ||
143 | } | ||
144 | |||
145 | send_from_v4(proto); | ||
146 | |||
147 | test_fd = receive_once(epfd, proto); | ||
148 | if (getsockopt(test_fd, SOL_SOCKET, SO_DOMAIN, &test_family, &len)) | ||
149 | error(1, errno, "failed to read socket domain"); | ||
150 | if (test_family != AF_INET) | ||
151 | error(1, 0, "expected to receive on v4 socket but got v6 (%d)", | ||
152 | test_family); | ||
153 | |||
154 | close(epfd); | ||
155 | } | ||
156 | |||
157 | int main(void) | ||
158 | { | ||
159 | int rcv_fds[32], i; | ||
160 | |||
161 | fprintf(stderr, "---- UDP IPv4 created before IPv6 ----\n"); | ||
162 | build_rcv_fd(AF_INET, SOCK_DGRAM, rcv_fds, 5); | ||
163 | build_rcv_fd(AF_INET6, SOCK_DGRAM, &(rcv_fds[5]), 5); | ||
164 | test(rcv_fds, 10, SOCK_DGRAM); | ||
165 | for (i = 0; i < 10; ++i) | ||
166 | close(rcv_fds[i]); | ||
167 | |||
168 | fprintf(stderr, "---- UDP IPv6 created before IPv4 ----\n"); | ||
169 | build_rcv_fd(AF_INET6, SOCK_DGRAM, rcv_fds, 5); | ||
170 | build_rcv_fd(AF_INET, SOCK_DGRAM, &(rcv_fds[5]), 5); | ||
171 | test(rcv_fds, 10, SOCK_DGRAM); | ||
172 | for (i = 0; i < 10; ++i) | ||
173 | close(rcv_fds[i]); | ||
174 | |||
175 | /* NOTE: UDP socket lookups traverse a different code path when there | ||
176 | * are > 10 sockets in a group. | ||
177 | */ | ||
178 | fprintf(stderr, "---- UDP IPv4 created before IPv6 (large) ----\n"); | ||
179 | build_rcv_fd(AF_INET, SOCK_DGRAM, rcv_fds, 16); | ||
180 | build_rcv_fd(AF_INET6, SOCK_DGRAM, &(rcv_fds[16]), 16); | ||
181 | test(rcv_fds, 32, SOCK_DGRAM); | ||
182 | for (i = 0; i < 32; ++i) | ||
183 | close(rcv_fds[i]); | ||
184 | |||
185 | fprintf(stderr, "---- UDP IPv6 created before IPv4 (large) ----\n"); | ||
186 | build_rcv_fd(AF_INET6, SOCK_DGRAM, rcv_fds, 16); | ||
187 | build_rcv_fd(AF_INET, SOCK_DGRAM, &(rcv_fds[16]), 16); | ||
188 | test(rcv_fds, 32, SOCK_DGRAM); | ||
189 | for (i = 0; i < 32; ++i) | ||
190 | close(rcv_fds[i]); | ||
191 | |||
192 | fprintf(stderr, "---- TCP IPv4 created before IPv6 ----\n"); | ||
193 | build_rcv_fd(AF_INET, SOCK_STREAM, rcv_fds, 5); | ||
194 | build_rcv_fd(AF_INET6, SOCK_STREAM, &(rcv_fds[5]), 5); | ||
195 | test(rcv_fds, 10, SOCK_STREAM); | ||
196 | for (i = 0; i < 10; ++i) | ||
197 | close(rcv_fds[i]); | ||
198 | |||
199 | fprintf(stderr, "---- TCP IPv6 created before IPv4 ----\n"); | ||
200 | build_rcv_fd(AF_INET6, SOCK_STREAM, rcv_fds, 5); | ||
201 | build_rcv_fd(AF_INET, SOCK_STREAM, &(rcv_fds[5]), 5); | ||
202 | test(rcv_fds, 10, SOCK_STREAM); | ||
203 | for (i = 0; i < 10; ++i) | ||
204 | close(rcv_fds[i]); | ||
205 | |||
206 | fprintf(stderr, "SUCCESS\n"); | ||
207 | return 0; | ||
208 | } | ||