1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright Amazon.com Inc. or its affiliates. */
8 #include <netinet/in.h>
9 #include <sys/socket.h>
10 #include <sys/sysinfo.h>
12 #include "../kselftest_harness.h"
14 FIXTURE(so_incoming_cpu
)
19 struct sockaddr_in in_addr
;
31 FIXTURE_VARIANT(so_incoming_cpu
)
36 FIXTURE_VARIANT_ADD(so_incoming_cpu
, before_reuseport
)
38 .when_to_set
= BEFORE_REUSEPORT
,
41 FIXTURE_VARIANT_ADD(so_incoming_cpu
, before_listen
)
43 .when_to_set
= BEFORE_LISTEN
,
46 FIXTURE_VARIANT_ADD(so_incoming_cpu
, after_listen
)
48 .when_to_set
= AFTER_LISTEN
,
51 FIXTURE_VARIANT_ADD(so_incoming_cpu
, after_all_listen
)
53 .when_to_set
= AFTER_ALL_LISTEN
,
56 static void write_sysctl(struct __test_metadata
*_metadata
,
57 char *filename
, char *string
)
61 fd
= open(filename
, O_WRONLY
);
65 ret
= write(fd
, string
, len
);
69 static void setup_netns(struct __test_metadata
*_metadata
)
71 ASSERT_EQ(unshare(CLONE_NEWNET
), 0);
72 ASSERT_EQ(system("ip link set lo up"), 0);
74 write_sysctl(_metadata
, "/proc/sys/net/ipv4/ip_local_port_range", "10000 60001");
75 write_sysctl(_metadata
, "/proc/sys/net/ipv4/tcp_tw_reuse", "0");
78 #define NR_PORT (60001 - 10000 - 1)
79 #define NR_CLIENT_PER_SERVER_DEFAULT 32
80 static int nr_client_per_server
, nr_server
, nr_client
;
82 FIXTURE_SETUP(so_incoming_cpu
)
84 setup_netns(_metadata
);
86 nr_server
= get_nprocs();
87 ASSERT_LE(2, nr_server
);
89 if (NR_CLIENT_PER_SERVER_DEFAULT
* nr_server
< NR_PORT
)
90 nr_client_per_server
= NR_CLIENT_PER_SERVER_DEFAULT
;
92 nr_client_per_server
= NR_PORT
/ nr_server
;
94 nr_client
= nr_client_per_server
* nr_server
;
96 self
->servers
= malloc(sizeof(int) * nr_server
);
97 ASSERT_NE(self
->servers
, NULL
);
99 self
->in_addr
.sin_family
= AF_INET
;
100 self
->in_addr
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
101 self
->in_addr
.sin_port
= htons(0);
102 self
->addrlen
= sizeof(struct sockaddr_in
);
105 FIXTURE_TEARDOWN(so_incoming_cpu
)
109 for (i
= 0; i
< nr_server
; i
++)
110 close(self
->servers
[i
]);
115 void set_so_incoming_cpu(struct __test_metadata
*_metadata
, int fd
, int cpu
)
119 ret
= setsockopt(fd
, SOL_SOCKET
, SO_INCOMING_CPU
, &cpu
, sizeof(int));
123 int create_server(struct __test_metadata
*_metadata
,
124 FIXTURE_DATA(so_incoming_cpu
) *self
,
125 const FIXTURE_VARIANT(so_incoming_cpu
) *variant
,
130 fd
= socket(AF_INET
, SOCK_STREAM
| SOCK_NONBLOCK
, 0);
133 if (variant
->when_to_set
== BEFORE_REUSEPORT
)
134 set_so_incoming_cpu(_metadata
, fd
, cpu
);
136 ret
= setsockopt(fd
, SOL_SOCKET
, SO_REUSEPORT
, &(int){1}, sizeof(int));
139 ret
= bind(fd
, &self
->addr
, self
->addrlen
);
142 if (variant
->when_to_set
== BEFORE_LISTEN
)
143 set_so_incoming_cpu(_metadata
, fd
, cpu
);
145 /* We don't use nr_client_per_server here not to block
146 * this test at connect() if SO_INCOMING_CPU is broken.
148 ret
= listen(fd
, nr_client
);
151 if (variant
->when_to_set
== AFTER_LISTEN
)
152 set_so_incoming_cpu(_metadata
, fd
, cpu
);
157 void create_servers(struct __test_metadata
*_metadata
,
158 FIXTURE_DATA(so_incoming_cpu
) *self
,
159 const FIXTURE_VARIANT(so_incoming_cpu
) *variant
)
163 for (i
= 0; i
< nr_server
; i
++) {
164 self
->servers
[i
] = create_server(_metadata
, self
, variant
, i
);
167 ret
= getsockname(self
->servers
[i
], &self
->addr
, &self
->addrlen
);
172 if (variant
->when_to_set
== AFTER_ALL_LISTEN
) {
173 for (i
= 0; i
< nr_server
; i
++)
174 set_so_incoming_cpu(_metadata
, self
->servers
[i
], i
);
178 void create_clients(struct __test_metadata
*_metadata
,
179 FIXTURE_DATA(so_incoming_cpu
) *self
)
184 for (i
= 0; i
< nr_server
; i
++) {
187 CPU_SET(i
, &cpu_set
);
188 ASSERT_EQ(CPU_COUNT(&cpu_set
), 1);
189 ASSERT_NE(CPU_ISSET(i
, &cpu_set
), 0);
191 /* Make sure SYN will be processed on the i-th CPU
192 * and finally distributed to the i-th listener.
194 ret
= sched_setaffinity(0, sizeof(cpu_set
), &cpu_set
);
197 for (j
= 0; j
< nr_client_per_server
; j
++) {
198 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
201 ret
= connect(fd
, &self
->addr
, self
->addrlen
);
209 void verify_incoming_cpu(struct __test_metadata
*_metadata
,
210 FIXTURE_DATA(so_incoming_cpu
) *self
)
212 int i
, j
, fd
, cpu
, ret
, total
= 0;
213 socklen_t len
= sizeof(int);
215 for (i
= 0; i
< nr_server
; i
++) {
216 for (j
= 0; j
< nr_client_per_server
; j
++) {
217 /* If we see -EAGAIN here, SO_INCOMING_CPU is broken */
218 fd
= accept(self
->servers
[i
], &self
->addr
, &self
->addrlen
);
221 ret
= getsockopt(fd
, SOL_SOCKET
, SO_INCOMING_CPU
, &cpu
, &len
);
230 ASSERT_EQ(total
, nr_client
);
231 TH_LOG("SO_INCOMING_CPU is very likely to be "
232 "working correctly with %d sockets.", total
);
235 TEST_F(so_incoming_cpu
, test1
)
237 create_servers(_metadata
, self
, variant
);
238 create_clients(_metadata
, self
);
239 verify_incoming_cpu(_metadata
, self
);
242 TEST_F(so_incoming_cpu
, test2
)
246 create_servers(_metadata
, self
, variant
);
248 /* No CPU specified */
249 server
= create_server(_metadata
, self
, variant
, -1);
252 create_clients(_metadata
, self
);
253 verify_incoming_cpu(_metadata
, self
);
256 TEST_F(so_incoming_cpu
, test3
)
260 create_servers(_metadata
, self
, variant
);
262 /* No CPU specified */
263 server
= create_server(_metadata
, self
, variant
, -1);
265 create_clients(_metadata
, self
);
267 /* Never receive any requests */
268 client
= accept(server
, &self
->addr
, &self
->addrlen
);
269 ASSERT_EQ(client
, -1);
271 verify_incoming_cpu(_metadata
, self
);