| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright Amazon.com Inc. or its affiliates. */
- #define _GNU_SOURCE
- #include <sched.h>
- #include <fcntl.h>
- #include <netinet/in.h>
- #include <sys/socket.h>
- #include <sys/sysinfo.h>
- #include "kselftest_harness.h"
- FIXTURE(so_incoming_cpu)
- {
- int *servers;
- union {
- struct sockaddr addr;
- struct sockaddr_in in_addr;
- };
- socklen_t addrlen;
- };
- enum when_to_set {
- BEFORE_REUSEPORT,
- BEFORE_LISTEN,
- AFTER_LISTEN,
- AFTER_ALL_LISTEN,
- };
- FIXTURE_VARIANT(so_incoming_cpu)
- {
- int when_to_set;
- };
- FIXTURE_VARIANT_ADD(so_incoming_cpu, before_reuseport)
- {
- .when_to_set = BEFORE_REUSEPORT,
- };
- FIXTURE_VARIANT_ADD(so_incoming_cpu, before_listen)
- {
- .when_to_set = BEFORE_LISTEN,
- };
- FIXTURE_VARIANT_ADD(so_incoming_cpu, after_listen)
- {
- .when_to_set = AFTER_LISTEN,
- };
- FIXTURE_VARIANT_ADD(so_incoming_cpu, after_all_listen)
- {
- .when_to_set = AFTER_ALL_LISTEN,
- };
- static void write_sysctl(struct __test_metadata *_metadata,
- char *filename, char *string)
- {
- int fd, len, ret;
- fd = open(filename, O_WRONLY);
- ASSERT_NE(fd, -1);
- len = strlen(string);
- ret = write(fd, string, len);
- ASSERT_EQ(ret, len);
- }
- static void setup_netns(struct __test_metadata *_metadata)
- {
- ASSERT_EQ(unshare(CLONE_NEWNET), 0);
- ASSERT_EQ(system("ip link set lo up"), 0);
- write_sysctl(_metadata, "/proc/sys/net/ipv4/ip_local_port_range", "10000 60001");
- write_sysctl(_metadata, "/proc/sys/net/ipv4/tcp_tw_reuse", "0");
- }
- #define NR_PORT (60001 - 10000 - 1)
- #define NR_CLIENT_PER_SERVER_DEFAULT 32
- static int nr_client_per_server, nr_server, nr_client;
- FIXTURE_SETUP(so_incoming_cpu)
- {
- setup_netns(_metadata);
- nr_server = get_nprocs();
- ASSERT_LE(2, nr_server);
- if (NR_CLIENT_PER_SERVER_DEFAULT * nr_server < NR_PORT)
- nr_client_per_server = NR_CLIENT_PER_SERVER_DEFAULT;
- else
- nr_client_per_server = NR_PORT / nr_server;
- nr_client = nr_client_per_server * nr_server;
- self->servers = malloc(sizeof(int) * nr_server);
- ASSERT_NE(self->servers, NULL);
- self->in_addr.sin_family = AF_INET;
- self->in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- self->in_addr.sin_port = htons(0);
- self->addrlen = sizeof(struct sockaddr_in);
- }
- FIXTURE_TEARDOWN(so_incoming_cpu)
- {
- int i;
- for (i = 0; i < nr_server; i++)
- close(self->servers[i]);
- free(self->servers);
- }
- void set_so_incoming_cpu(struct __test_metadata *_metadata, int fd, int cpu)
- {
- int ret;
- ret = setsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, sizeof(int));
- ASSERT_EQ(ret, 0);
- }
- int create_server(struct __test_metadata *_metadata,
- FIXTURE_DATA(so_incoming_cpu) *self,
- const FIXTURE_VARIANT(so_incoming_cpu) *variant,
- int cpu)
- {
- int fd, ret;
- fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
- ASSERT_NE(fd, -1);
- if (variant->when_to_set == BEFORE_REUSEPORT)
- set_so_incoming_cpu(_metadata, fd, cpu);
- ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int));
- ASSERT_EQ(ret, 0);
- ret = bind(fd, &self->addr, self->addrlen);
- ASSERT_EQ(ret, 0);
- if (variant->when_to_set == BEFORE_LISTEN)
- set_so_incoming_cpu(_metadata, fd, cpu);
- /* We don't use nr_client_per_server here not to block
- * this test at connect() if SO_INCOMING_CPU is broken.
- */
- ret = listen(fd, nr_client);
- ASSERT_EQ(ret, 0);
- if (variant->when_to_set == AFTER_LISTEN)
- set_so_incoming_cpu(_metadata, fd, cpu);
- return fd;
- }
- void create_servers(struct __test_metadata *_metadata,
- FIXTURE_DATA(so_incoming_cpu) *self,
- const FIXTURE_VARIANT(so_incoming_cpu) *variant)
- {
- int i, ret;
- for (i = 0; i < nr_server; i++) {
- self->servers[i] = create_server(_metadata, self, variant, i);
- if (i == 0) {
- ret = getsockname(self->servers[i], &self->addr, &self->addrlen);
- ASSERT_EQ(ret, 0);
- }
- }
- if (variant->when_to_set == AFTER_ALL_LISTEN) {
- for (i = 0; i < nr_server; i++)
- set_so_incoming_cpu(_metadata, self->servers[i], i);
- }
- }
- void create_clients(struct __test_metadata *_metadata,
- FIXTURE_DATA(so_incoming_cpu) *self)
- {
- cpu_set_t cpu_set;
- int i, j, fd, ret;
- for (i = 0; i < nr_server; i++) {
- CPU_ZERO(&cpu_set);
- CPU_SET(i, &cpu_set);
- ASSERT_EQ(CPU_COUNT(&cpu_set), 1);
- ASSERT_NE(CPU_ISSET(i, &cpu_set), 0);
- /* Make sure SYN will be processed on the i-th CPU
- * and finally distributed to the i-th listener.
- */
- ret = sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
- ASSERT_EQ(ret, 0);
- for (j = 0; j < nr_client_per_server; j++) {
- fd = socket(AF_INET, SOCK_STREAM, 0);
- ASSERT_NE(fd, -1);
- ret = connect(fd, &self->addr, self->addrlen);
- ASSERT_EQ(ret, 0);
- close(fd);
- }
- }
- }
- void verify_incoming_cpu(struct __test_metadata *_metadata,
- FIXTURE_DATA(so_incoming_cpu) *self)
- {
- int i, j, fd, cpu, ret, total = 0;
- socklen_t len = sizeof(int);
- for (i = 0; i < nr_server; i++) {
- for (j = 0; j < nr_client_per_server; j++) {
- /* If we see -EAGAIN here, SO_INCOMING_CPU is broken */
- fd = accept(self->servers[i], &self->addr, &self->addrlen);
- ASSERT_NE(fd, -1);
- ret = getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, &len);
- ASSERT_EQ(ret, 0);
- ASSERT_EQ(cpu, i);
- close(fd);
- total++;
- }
- }
- ASSERT_EQ(total, nr_client);
- TH_LOG("SO_INCOMING_CPU is very likely to be "
- "working correctly with %d sockets.", total);
- }
- TEST_F(so_incoming_cpu, test1)
- {
- create_servers(_metadata, self, variant);
- create_clients(_metadata, self);
- verify_incoming_cpu(_metadata, self);
- }
- TEST_F(so_incoming_cpu, test2)
- {
- int server;
- create_servers(_metadata, self, variant);
- /* No CPU specified */
- server = create_server(_metadata, self, variant, -1);
- close(server);
- create_clients(_metadata, self);
- verify_incoming_cpu(_metadata, self);
- }
- TEST_F(so_incoming_cpu, test3)
- {
- int server, client;
- create_servers(_metadata, self, variant);
- /* No CPU specified */
- server = create_server(_metadata, self, variant, -1);
- create_clients(_metadata, self);
- /* Never receive any requests */
- client = accept(server, &self->addr, &self->addrlen);
- ASSERT_EQ(client, -1);
- verify_incoming_cpu(_metadata, self);
- }
- TEST_HARNESS_MAIN
|