summaryrefslogtreecommitdiff
path: root/tests/bandwidth.c
blob: 279126a018b499d690f1a9bda325d34fa1c7145e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* Check bandwidth set is respected.
 * This tests uses UDP packets as they not depend on window or
 * other TCP synchronizations and also is easier to compute
 * bandwidth as header is simpler.
 */
#include "common.h"
#include "../utils.h"
#include <poll.h>
#include <pthread.h>

#define MIN_IP_UDP_HEADER 28

static unsigned current_bw = 0;
static int udp_socks[2] = { -1, -1 };

typedef struct {
	uint8_t data[1024];
} test_payload;

#define SOCK2PTR(s) ((void *)(uintptr_t)(s))
#define PTR2SOCK(p) ((int)(uintptr_t)(p))

static void *
send_proc(void *arg)
{
	int sock = PTR2SOCK(arg);
	test_payload payload;

	int n;
	for (n = 0; n < sizeof(payload.data); ++n)
		payload.data[n] = n * 123 + 456;

	uint64_t start_time = get_time_us();
	while (get_time_us() - start_time < 130 * 1000) {
		for (n = current_bw / 4; n >= 0; --n)
			send(sock, &payload, sizeof(payload), MSG_NOSIGNAL);
		usleep(1000);
	}
	return NULL;
}

static void *
recv_proc(void *arg)
{
	int sock = PTR2SOCK(arg);
	test_payload payload;

	uint64_t bytes_received = 0;
	uint64_t start_time = 0;
	for (;;) {
		ssize_t len = recv(sock, &payload, sizeof(payload), MSG_TRUNC);
		assert(len > 0);

		uint64_t curr_time = get_time_us();
		assert(bytes_received <= 0xffffffffu - len - MIN_IP_UDP_HEADER);
		if (start_time == 0)
			start_time = curr_time;
		else
			bytes_received += len + MIN_IP_UDP_HEADER;
		if (curr_time - start_time >= 100 * 1000)
			break;
	}
	return (void *) (uintptr_t) bytes_received;
}

static void
flush_socks(void)
{
	struct pollfd fds[2];
	int i;

	fds[0].fd = udp_socks[0];
	fds[0].events = POLLIN;
	fds[1].fd = udp_socks[1];
	fds[1].events = POLLIN;
	for (;;) {
		// wait any packet for 30 ms
		fds[0].revents = 0;
		fds[1].revents = 0;
		int res = poll(fds, 2, 30);
		if (res == 0)
			break;

		for (i = 0; i < 2; ++i) {
			char buf[1024];
			if (fds[i].revents)
				recv(fds[i].fd, buf, sizeof(buf), MSG_TRUNC);
		}
	}
}

static void
test_bandwidth(unsigned bw)
{
	current_bw = bw;

	pthread_t th[3];
	void *thread_res;
	unsigned bytes_received;
	unsigned expected = 1024 * 1024 * bw / 8 / 10 + 1 * (1024 + MIN_IP_UDP_HEADER);

	// launch program with given latency
	launch_latency("10 %uMbit", bw);
	create_udp_pair(udp_socks);

	// one direction
	assert(pthread_create(th, NULL, recv_proc, SOCK2PTR(udp_socks[1])) == 0);
	send_proc(SOCK2PTR(udp_socks[0]));
	assert(pthread_join(th[0], &thread_res) == 0);
	bytes_received = (uintptr_t) thread_res;
	printf("received %u/%u\n", bytes_received, expected);
	// check into -5% to +5%
	assert(bytes_received >= expected * 0.96 && bytes_received <= expected * 1.04);

	// other direction
	flush_socks();
	assert(pthread_create(th, NULL, recv_proc, SOCK2PTR(udp_socks[0])) == 0);
	send_proc(SOCK2PTR(udp_socks[1]));
	assert(pthread_join(th[0], &thread_res) == 0);
	bytes_received = (uintptr_t) thread_res;
	printf("received %u/%u\n", bytes_received, expected);
	// check into -5% to +5%
	assert(bytes_received >= expected * 0.96 && bytes_received <= expected * 1.04);

	close_udp_pair(udp_socks);
	kill_latency();
}

int main(void)
{
	printf("Testing bandwidth limitation\n");

	test_bandwidth(2);
	test_bandwidth(4);
	test_bandwidth(8);
	test_bandwidth(16);
	return 0;
}