1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright Amazon.com Inc. or its affiliates. */
6 #include <netinet/in.h>
7 #include <sys/socket.h>
8 #include <sys/sysinfo.h>
10 #include "../kselftest_harness.h"
12 #define CLIENT_PER_SERVER 32 /* More sockets, more reliable */
13 #define NR_SERVER self->nproc
14 #define NR_CLIENT (CLIENT_PER_SERVER * NR_SERVER)
16 FIXTURE(so_incoming_cpu)
22 struct sockaddr_in in_addr;
34 FIXTURE_VARIANT(so_incoming_cpu)
39 FIXTURE_VARIANT_ADD(so_incoming_cpu, before_reuseport)
41 .when_to_set = BEFORE_REUSEPORT,
44 FIXTURE_VARIANT_ADD(so_incoming_cpu, before_listen)
46 .when_to_set = BEFORE_LISTEN,
49 FIXTURE_VARIANT_ADD(so_incoming_cpu, after_listen)
51 .when_to_set = AFTER_LISTEN,
54 FIXTURE_VARIANT_ADD(so_incoming_cpu, after_all_listen)
56 .when_to_set = AFTER_ALL_LISTEN,
59 FIXTURE_SETUP(so_incoming_cpu)
61 self->nproc = get_nprocs();
62 ASSERT_LE(2, self->nproc);
64 self->servers = malloc(sizeof(int) * NR_SERVER);
65 ASSERT_NE(self->servers, NULL);
67 self->in_addr.sin_family = AF_INET;
68 self->in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
69 self->in_addr.sin_port = htons(0);
70 self->addrlen = sizeof(struct sockaddr_in);
73 FIXTURE_TEARDOWN(so_incoming_cpu)
77 for (i = 0; i < NR_SERVER; i++)
78 close(self->servers[i]);
83 void set_so_incoming_cpu(struct __test_metadata *_metadata, int fd, int cpu)
87 ret = setsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, sizeof(int));
91 int create_server(struct __test_metadata *_metadata,
92 FIXTURE_DATA(so_incoming_cpu) *self,
93 const FIXTURE_VARIANT(so_incoming_cpu) *variant,
98 fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
101 if (variant->when_to_set == BEFORE_REUSEPORT)
102 set_so_incoming_cpu(_metadata, fd, cpu);
104 ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int));
107 ret = bind(fd, &self->addr, self->addrlen);
110 if (variant->when_to_set == BEFORE_LISTEN)
111 set_so_incoming_cpu(_metadata, fd, cpu);
113 /* We don't use CLIENT_PER_SERVER here not to block
114 * this test at connect() if SO_INCOMING_CPU is broken.
116 ret = listen(fd, NR_CLIENT);
119 if (variant->when_to_set == AFTER_LISTEN)
120 set_so_incoming_cpu(_metadata, fd, cpu);
125 void create_servers(struct __test_metadata *_metadata,
126 FIXTURE_DATA(so_incoming_cpu) *self,
127 const FIXTURE_VARIANT(so_incoming_cpu) *variant)
131 for (i = 0; i < NR_SERVER; i++) {
132 self->servers[i] = create_server(_metadata, self, variant, i);
135 ret = getsockname(self->servers[i], &self->addr, &self->addrlen);
140 if (variant->when_to_set == AFTER_ALL_LISTEN) {
141 for (i = 0; i < NR_SERVER; i++)
142 set_so_incoming_cpu(_metadata, self->servers[i], i);
146 void create_clients(struct __test_metadata *_metadata,
147 FIXTURE_DATA(so_incoming_cpu) *self)
152 for (i = 0; i < NR_SERVER; i++) {
155 CPU_SET(i, &cpu_set);
156 ASSERT_EQ(CPU_COUNT(&cpu_set), 1);
157 ASSERT_NE(CPU_ISSET(i, &cpu_set), 0);
159 /* Make sure SYN will be processed on the i-th CPU
160 * and finally distributed to the i-th listener.
162 sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
165 for (j = 0; j < CLIENT_PER_SERVER; j++) {
166 fd = socket(AF_INET, SOCK_STREAM, 0);
169 ret = connect(fd, &self->addr, self->addrlen);
177 void verify_incoming_cpu(struct __test_metadata *_metadata,
178 FIXTURE_DATA(so_incoming_cpu) *self)
180 int i, j, fd, cpu, ret, total = 0;
181 socklen_t len = sizeof(int);
183 for (i = 0; i < NR_SERVER; i++) {
184 for (j = 0; j < CLIENT_PER_SERVER; j++) {
185 /* If we see -EAGAIN here, SO_INCOMING_CPU is broken */
186 fd = accept(self->servers[i], &self->addr, &self->addrlen);
189 ret = getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, &len);
198 ASSERT_EQ(total, NR_CLIENT);
199 TH_LOG("SO_INCOMING_CPU is very likely to be "
200 "working correctly with %d sockets.", total);
203 TEST_F(so_incoming_cpu, test1)
205 create_servers(_metadata, self, variant);
206 create_clients(_metadata, self);
207 verify_incoming_cpu(_metadata, self);
210 TEST_F(so_incoming_cpu, test2)
214 create_servers(_metadata, self, variant);
216 /* No CPU specified */
217 server = create_server(_metadata, self, variant, -1);
220 create_clients(_metadata, self);
221 verify_incoming_cpu(_metadata, self);
224 TEST_F(so_incoming_cpu, test3)
228 create_servers(_metadata, self, variant);
230 /* No CPU specified */
231 server = create_server(_metadata, self, variant, -1);
233 create_clients(_metadata, self);
235 /* Never receive any requests */
236 client = accept(server, &self->addr, &self->addrlen);
237 ASSERT_EQ(client, -1);
239 verify_incoming_cpu(_metadata, self);