1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright Amazon.com Inc. or its affiliates. */ 3 #define _GNU_SOURCE 4 #include <sched.h> 5 6 #include <fcntl.h> 7 8 #include <netinet/in.h> 9 #include <sys/socket.h> 10 #include <sys/sysinfo.h> 11 12 #include "kselftest_harness.h" 13 14 FIXTURE(so_incoming_cpu) 15 { 16 int *servers; 17 union { 18 struct sockaddr addr; 19 struct sockaddr_in in_addr; 20 }; 21 socklen_t addrlen; 22 }; 23 24 enum when_to_set { 25 BEFORE_REUSEPORT, 26 BEFORE_LISTEN, 27 AFTER_LISTEN, 28 AFTER_ALL_LISTEN, 29 }; 30 31 FIXTURE_VARIANT(so_incoming_cpu) 32 { 33 int when_to_set; 34 }; 35 36 FIXTURE_VARIANT_ADD(so_incoming_cpu, before_reuseport) 37 { 38 .when_to_set = BEFORE_REUSEPORT, 39 }; 40 41 FIXTURE_VARIANT_ADD(so_incoming_cpu, before_listen) 42 { 43 .when_to_set = BEFORE_LISTEN, 44 }; 45 46 FIXTURE_VARIANT_ADD(so_incoming_cpu, after_listen) 47 { 48 .when_to_set = AFTER_LISTEN, 49 }; 50 51 FIXTURE_VARIANT_ADD(so_incoming_cpu, after_all_listen) 52 { 53 .when_to_set = AFTER_ALL_LISTEN, 54 }; 55 56 static void write_sysctl(struct __test_metadata *_metadata, 57 char *filename, char *string) 58 { 59 int fd, len, ret; 60 61 fd = open(filename, O_WRONLY); 62 ASSERT_NE(fd, -1); 63 64 len = strlen(string); 65 ret = write(fd, string, len); 66 ASSERT_EQ(ret, len); 67 } 68 69 static void setup_netns(struct __test_metadata *_metadata) 70 { 71 ASSERT_EQ(unshare(CLONE_NEWNET), 0); 72 ASSERT_EQ(system("ip link set lo up"), 0); 73 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"); 76 } 77 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; 81 82 FIXTURE_SETUP(so_incoming_cpu) 83 { 84 nr_server = get_nprocs(); 85 if (nr_server < 2) 86 SKIP(return, "requires at least two CPUs"); 87 88 setup_netns(_metadata); 89 90 if (NR_CLIENT_PER_SERVER_DEFAULT * nr_server < NR_PORT) 91 nr_client_per_server = NR_CLIENT_PER_SERVER_DEFAULT; 92 else 93 nr_client_per_server = NR_PORT / nr_server; 94 95 nr_client = nr_client_per_server * nr_server; 96 97 self->servers = malloc(sizeof(int) * nr_server); 98 ASSERT_NE(self->servers, NULL); 99 100 self->in_addr.sin_family = AF_INET; 101 self->in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 102 self->in_addr.sin_port = htons(0); 103 self->addrlen = sizeof(struct sockaddr_in); 104 } 105 106 FIXTURE_TEARDOWN(so_incoming_cpu) 107 { 108 int i; 109 110 for (i = 0; i < nr_server; i++) 111 close(self->servers[i]); 112 113 free(self->servers); 114 } 115 116 void set_so_incoming_cpu(struct __test_metadata *_metadata, int fd, int cpu) 117 { 118 int ret; 119 120 ret = setsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, sizeof(int)); 121 ASSERT_EQ(ret, 0); 122 } 123 124 int create_server(struct __test_metadata *_metadata, 125 FIXTURE_DATA(so_incoming_cpu) *self, 126 const FIXTURE_VARIANT(so_incoming_cpu) *variant, 127 int cpu) 128 { 129 int fd, ret; 130 131 fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); 132 ASSERT_NE(fd, -1); 133 134 if (variant->when_to_set == BEFORE_REUSEPORT) 135 set_so_incoming_cpu(_metadata, fd, cpu); 136 137 ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)); 138 ASSERT_EQ(ret, 0); 139 140 ret = bind(fd, &self->addr, self->addrlen); 141 ASSERT_EQ(ret, 0); 142 143 if (variant->when_to_set == BEFORE_LISTEN) 144 set_so_incoming_cpu(_metadata, fd, cpu); 145 146 /* We don't use nr_client_per_server here not to block 147 * this test at connect() if SO_INCOMING_CPU is broken. 148 */ 149 ret = listen(fd, nr_client); 150 ASSERT_EQ(ret, 0); 151 152 if (variant->when_to_set == AFTER_LISTEN) 153 set_so_incoming_cpu(_metadata, fd, cpu); 154 155 return fd; 156 } 157 158 void create_servers(struct __test_metadata *_metadata, 159 FIXTURE_DATA(so_incoming_cpu) *self, 160 const FIXTURE_VARIANT(so_incoming_cpu) *variant) 161 { 162 int i, ret; 163 164 for (i = 0; i < nr_server; i++) { 165 self->servers[i] = create_server(_metadata, self, variant, i); 166 167 if (i == 0) { 168 ret = getsockname(self->servers[i], &self->addr, &self->addrlen); 169 ASSERT_EQ(ret, 0); 170 } 171 } 172 173 if (variant->when_to_set == AFTER_ALL_LISTEN) { 174 for (i = 0; i < nr_server; i++) 175 set_so_incoming_cpu(_metadata, self->servers[i], i); 176 } 177 } 178 179 void create_clients(struct __test_metadata *_metadata, 180 FIXTURE_DATA(so_incoming_cpu) *self) 181 { 182 cpu_set_t cpu_set; 183 int i, j, fd, ret; 184 185 for (i = 0; i < nr_server; i++) { 186 CPU_ZERO(&cpu_set); 187 188 CPU_SET(i, &cpu_set); 189 ASSERT_EQ(CPU_COUNT(&cpu_set), 1); 190 ASSERT_NE(CPU_ISSET(i, &cpu_set), 0); 191 192 /* Make sure SYN will be processed on the i-th CPU 193 * and finally distributed to the i-th listener. 194 */ 195 ret = sched_setaffinity(0, sizeof(cpu_set), &cpu_set); 196 ASSERT_EQ(ret, 0); 197 198 for (j = 0; j < nr_client_per_server; j++) { 199 fd = socket(AF_INET, SOCK_STREAM, 0); 200 ASSERT_NE(fd, -1); 201 202 ret = connect(fd, &self->addr, self->addrlen); 203 ASSERT_EQ(ret, 0); 204 205 close(fd); 206 } 207 } 208 } 209 210 void verify_incoming_cpu(struct __test_metadata *_metadata, 211 FIXTURE_DATA(so_incoming_cpu) *self) 212 { 213 int i, j, fd, cpu, ret, total = 0; 214 socklen_t len = sizeof(int); 215 216 for (i = 0; i < nr_server; i++) { 217 for (j = 0; j < nr_client_per_server; j++) { 218 /* If we see -EAGAIN here, SO_INCOMING_CPU is broken */ 219 fd = accept(self->servers[i], &self->addr, &self->addrlen); 220 ASSERT_NE(fd, -1); 221 222 ret = getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, &len); 223 ASSERT_EQ(ret, 0); 224 ASSERT_EQ(cpu, i); 225 226 close(fd); 227 total++; 228 } 229 } 230 231 ASSERT_EQ(total, nr_client); 232 TH_LOG("SO_INCOMING_CPU is very likely to be " 233 "working correctly with %d sockets.", total); 234 } 235 236 TEST_F(so_incoming_cpu, test1) 237 { 238 create_servers(_metadata, self, variant); 239 create_clients(_metadata, self); 240 verify_incoming_cpu(_metadata, self); 241 } 242 243 TEST_F(so_incoming_cpu, test2) 244 { 245 int server; 246 247 create_servers(_metadata, self, variant); 248 249 /* No CPU specified */ 250 server = create_server(_metadata, self, variant, -1); 251 close(server); 252 253 create_clients(_metadata, self); 254 verify_incoming_cpu(_metadata, self); 255 } 256 257 TEST_F(so_incoming_cpu, test3) 258 { 259 int server, client; 260 261 create_servers(_metadata, self, variant); 262 263 /* No CPU specified */ 264 server = create_server(_metadata, self, variant, -1); 265 266 create_clients(_metadata, self); 267 268 /* Never receive any requests */ 269 client = accept(server, &self->addr, &self->addrlen); 270 ASSERT_EQ(client, -1); 271 272 verify_incoming_cpu(_metadata, self); 273 } 274 275 TEST_HARNESS_MAIN 276