Lines 28-35
Link Here
|
28 |
|
28 |
|
29 |
#include <sys/endian.h> |
29 |
#include <sys/endian.h> |
30 |
#include <sys/types.h> |
30 |
#include <sys/types.h> |
|
|
31 |
#include <stdbool.h> |
31 |
#include <sys/socket.h> |
32 |
#include <sys/socket.h> |
32 |
#include <sys/time.h> |
33 |
#include <sys/time.h> |
|
|
34 |
#include <sys/sysctl.h> /* sysctl */ |
33 |
|
35 |
|
34 |
#include <netinet/in.h> |
36 |
#include <netinet/in.h> |
35 |
#include <netdb.h> /* getaddrinfo */ |
37 |
#include <netdb.h> /* getaddrinfo */ |
Lines 36-105
Link Here
|
36 |
|
38 |
|
37 |
#include <signal.h> |
39 |
#include <signal.h> |
38 |
#include <stdio.h> |
40 |
#include <stdio.h> |
|
|
41 |
#include <inttypes.h> |
39 |
#include <stdlib.h> |
42 |
#include <stdlib.h> |
40 |
#include <string.h> |
43 |
#include <string.h> |
41 |
#include <unistd.h> /* close */ |
44 |
#include <unistd.h> /* close */ |
|
|
45 |
#include <sys/cpuset.h> |
46 |
#include <pthread.h> |
47 |
#include <pthread_np.h> |
48 |
#include <fcntl.h> |
49 |
#include <time.h> /* clock_getres() */ |
42 |
|
50 |
|
|
|
51 |
/* sysctl wrapper to return the number of active CPUs |
52 |
function from netmap/pkt-gen.c */ |
53 |
static int |
54 |
system_ncpus(void) |
55 |
{ |
56 |
int ncpus; |
57 |
int mib[2] = { CTL_HW, HW_NCPU }; |
58 |
size_t len = sizeof(mib); |
59 |
sysctl(mib, 2, &ncpus, &len, NULL, 0); |
60 |
return (ncpus); |
61 |
} |
62 |
|
63 |
/* set the thread affinity |
64 |
function from netmap/pkt-gen.c */ |
65 |
static int |
66 |
setaffinity(pthread_t me, int i) |
67 |
{ |
68 |
cpuset_t cpumask; |
69 |
|
70 |
if (i == -1) |
71 |
return 0; |
72 |
|
73 |
/* Set thread affinity affinity.*/ |
74 |
CPU_ZERO(&cpumask); |
75 |
CPU_SET(i, &cpumask); |
76 |
if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { |
77 |
perror("Unable to set affinity"); |
78 |
return 1; |
79 |
} |
80 |
return 0; |
81 |
} |
82 |
|
83 |
static int round_to(int n, int l) |
84 |
{ |
85 |
return ((n + l - 1)/l)*l; |
86 |
} |
87 |
|
43 |
static void |
88 |
static void |
44 |
usage(void) |
89 |
usage(void) |
45 |
{ |
90 |
{ |
46 |
|
91 |
|
47 |
fprintf(stderr, "netblast [ip] [port] [payloadsize] [duration]\n"); |
92 |
fprintf(stderr, "netblast [ip] [port] [payloadsize] [duration] [nthreads]\n"); |
48 |
exit(-1); |
93 |
exit(-1); |
49 |
} |
94 |
} |
50 |
|
95 |
|
51 |
static int global_stop_flag; |
96 |
static bool global_stop_flag=true; |
52 |
|
97 |
|
53 |
static void |
98 |
static void |
54 |
signal_handler(int signum __unused) |
99 |
signal_handler(int signum __unused) |
55 |
{ |
100 |
{ |
56 |
|
101 |
|
57 |
global_stop_flag = 1; |
102 |
global_stop_flag = false; |
58 |
} |
103 |
} |
59 |
|
104 |
|
60 |
/* |
105 |
/* |
61 |
* Loop that blasts packets: begin by recording time information, resetting |
106 |
* Each socket uses multiple threads so the generator is |
62 |
* stats. Set the interval timer for when we want to wake up. Then go. |
107 |
* more efficient. A collector thread runs the stats. |
63 |
* SIGALRM will set a flag indicating it's time to stop. Note that there's |
|
|
64 |
* some overhead to the signal and timer setup, so the smaller the duration, |
65 |
* the higher the relative overhead. |
66 |
*/ |
108 |
*/ |
67 |
static int |
109 |
struct td_desc { |
68 |
blast_loop(int s, long duration, u_char *packet, u_int packet_len) |
110 |
pthread_t td_id; |
|
|
111 |
uint64_t counter; /* tx counter */ |
112 |
uint64_t send_errors; /* tx send errors */ |
113 |
uint64_t send_calls; /* tx send calls */ |
114 |
char *address, *port; |
115 |
int family; |
116 |
u_char *packet; |
117 |
u_int packet_len; |
118 |
}; |
119 |
|
120 |
static void * |
121 |
blast(void *data) |
69 |
{ |
122 |
{ |
70 |
struct timespec starttime, tmptime; |
123 |
struct td_desc *t = data; |
71 |
struct itimerval it; |
124 |
t->counter=0; |
72 |
u_int32_t counter; |
125 |
t->send_errors=0; |
73 |
int send_errors, send_calls; |
126 |
t->send_calls=0; |
74 |
|
127 |
|
75 |
if (signal(SIGALRM, signal_handler) == SIG_ERR) { |
128 |
struct addrinfo hints = {0}, *res = NULL , *res0 = NULL; |
76 |
perror("signal"); |
|
|
77 |
return (-1); |
78 |
} |
79 |
|
129 |
|
80 |
if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) { |
130 |
hints.ai_family = PF_UNSPEC; |
81 |
perror("clock_getres"); |
131 |
hints.ai_socktype = SOCK_DGRAM; |
82 |
return (-1); |
|
|
83 |
} |
84 |
|
132 |
|
85 |
if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) { |
133 |
int error = getaddrinfo(t->address,t->port, &hints, &res0); |
86 |
perror("clock_gettime"); |
134 |
if (error) { |
87 |
return (-1); |
135 |
perror(gai_strerror(error)); |
|
|
136 |
return NULL; |
137 |
/*NOTREACHED*/ |
88 |
} |
138 |
} |
89 |
|
139 |
|
90 |
it.it_interval.tv_sec = 0; |
140 |
int s = -1; /* socket */ |
91 |
it.it_interval.tv_usec = 0; |
141 |
int n = 1; /* dummy value for setsockopt */ |
92 |
it.it_value.tv_sec = duration; |
142 |
const char *cause = NULL; |
93 |
it.it_value.tv_usec = 0; |
|
|
94 |
|
143 |
|
95 |
if (setitimer(ITIMER_REAL, &it, NULL) < 0) { |
144 |
for (res = res0; res; res = res->ai_next) { |
96 |
perror("setitimer"); |
145 |
s = socket(res->ai_family, res->ai_socktype, 0); |
97 |
return (-1); |
146 |
if (s < 0) { |
|
|
147 |
cause = "socket"; |
148 |
continue; |
149 |
} |
150 |
|
151 |
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &n, sizeof n) < 0) { |
152 |
cause = "SO_REUSEPORT"; |
153 |
close(s); |
154 |
s = -1; |
155 |
continue; |
156 |
} |
157 |
|
158 |
if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { |
159 |
cause = "connect"; |
160 |
close(s); |
161 |
s = -1; |
162 |
continue; |
163 |
} |
164 |
|
165 |
break; /* okay we got one */ |
98 |
} |
166 |
} |
|
|
167 |
if (s < 0) { |
168 |
perror(cause); |
169 |
return NULL; |
170 |
/*NOTREACHED*/ |
171 |
} |
99 |
|
172 |
|
100 |
send_errors = send_calls = 0; |
173 |
/* Store address family for Ethernet bandwitdh estimation */ |
101 |
counter = 0; |
174 |
t->family=res->ai_family; |
102 |
while (global_stop_flag == 0) { |
175 |
|
|
|
176 |
while (global_stop_flag) { |
103 |
/* |
177 |
/* |
104 |
* We maintain and, if there's room, send a counter. Note |
178 |
* We maintain and, if there's room, send a counter. Note |
105 |
* that even if the error is purely local, we still increment |
179 |
* that even if the error is purely local, we still increment |
Lines 110-141
Link Here
|
110 |
* operation, causing the current sequence number also to be |
184 |
* operation, causing the current sequence number also to be |
111 |
* skipped. |
185 |
* skipped. |
112 |
*/ |
186 |
*/ |
113 |
if (packet_len >= 4) { |
187 |
if (t->packet_len >= 4) { |
114 |
be32enc(packet, counter); |
188 |
be32enc(t->packet, t->counter); |
115 |
counter++; |
189 |
t->counter++; |
116 |
} |
190 |
} |
117 |
if (send(s, packet, packet_len, 0) < 0) |
191 |
if (send(s, t->packet, t->packet_len, 0) < 0) |
118 |
send_errors++; |
192 |
t->send_errors++; |
119 |
send_calls++; |
193 |
t->send_calls++; |
120 |
} |
194 |
} |
|
|
195 |
return NULL; |
196 |
} |
121 |
|
197 |
|
|
|
198 |
static struct td_desc ** |
199 |
make_threads(char *address, char *port, u_char *packet, u_int packet_len, int nthreads) |
200 |
{ |
201 |
int i; |
202 |
int lb = round_to(nthreads * sizeof (struct td_desc *), 64); |
203 |
int td_len = round_to(sizeof(struct td_desc), 64); // cache align |
204 |
char *m = calloc(1, lb + td_len * nthreads); |
205 |
struct td_desc **tp; |
206 |
|
207 |
/* pointers plus the structs */ |
208 |
if (m == NULL) { |
209 |
perror("no room for pointers!"); |
210 |
exit(1); |
211 |
} |
212 |
tp = (struct td_desc **)m; |
213 |
m += lb; /* skip the pointers */ |
214 |
int ncpu = system_ncpus(); |
215 |
for (i = 0; i < nthreads; i++, m += td_len) { |
216 |
tp[i] = (struct td_desc *)m; |
217 |
tp[i]->address = address; |
218 |
tp[i]->port = port; |
219 |
tp[i]->packet = packet; |
220 |
tp[i]->packet_len = packet_len; |
221 |
if (pthread_create(&tp[i]->td_id, NULL, blast, tp[i])) { |
222 |
perror("unable to create thread"); |
223 |
exit(1); |
224 |
} |
225 |
if (setaffinity(tp[i]->td_id, i % ncpu)) { |
226 |
perror("unable to set thread affinity"); |
227 |
} |
228 |
} |
229 |
return tp; |
230 |
} |
231 |
|
232 |
static void |
233 |
main_thread(struct td_desc **tp, long duration, struct timespec starttime, struct timespec tmptime, long payloadsize, int nthreads) |
234 |
{ |
235 |
uint64_t send_errors=0, send_calls=0; |
122 |
if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) { |
236 |
if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) { |
123 |
perror("clock_gettime"); |
237 |
perror("clock_gettime"); |
124 |
return (-1); |
|
|
125 |
} |
238 |
} |
126 |
|
239 |
|
|
|
240 |
for (int i = 0; i < nthreads; i++) { |
241 |
/* Wait for thread end */ |
242 |
pthread_join( tp[i]->td_id, NULL); |
243 |
send_calls+=tp[i]->send_calls; |
244 |
send_errors+=tp[i]->send_errors; |
245 |
} |
246 |
|
127 |
printf("\n"); |
247 |
printf("\n"); |
128 |
printf("start: %zd.%09lu\n", starttime.tv_sec, |
248 |
printf("start: %zd.%09lu\n", starttime.tv_sec, |
129 |
starttime.tv_nsec); |
249 |
starttime.tv_nsec); |
130 |
printf("finish: %zd.%09lu\n", tmptime.tv_sec, |
250 |
printf("finish: %zd.%09lu\n", tmptime.tv_sec, |
131 |
tmptime.tv_nsec); |
251 |
tmptime.tv_nsec); |
132 |
printf("send calls: %d\n", send_calls); |
252 |
printf("send calls: %" PRIu64 "\n", send_calls); |
133 |
printf("send errors: %d\n", send_errors); |
253 |
printf("send errors: %" PRIu64 "\n", send_errors); |
134 |
printf("approx send rate: %ld\n", (send_calls - send_errors) / |
254 |
printf("send success: %" PRIu64 "\n", send_calls - send_errors); |
|
|
255 |
printf("approx send rate: %" PRIu64 "\n", (send_calls - send_errors) / |
135 |
duration); |
256 |
duration); |
136 |
printf("approx error rate: %d\n", (send_errors / send_calls)); |
257 |
printf("approx error rate: %" PRIu64 "\n", (send_errors / send_calls)); |
137 |
|
258 |
printf("approx Ethernet throughput: "); |
138 |
return (0); |
259 |
if (tp[0]->family == AF_INET) |
|
|
260 |
printf("%" PRIu64 " Mib/s\n", ((send_calls - send_errors) / duration ) * |
261 |
(payloadsize + 8 + 20 + 14 ) * 8 / 1000 / 1000); |
262 |
else if (tp[0]->family == AF_INET6) |
263 |
printf("%" PRIu64 " Mib/s\n", ((send_calls - send_errors) / duration ) * |
264 |
(payloadsize + 8 + 40 + 14 ) * 8 / 1000 / 1000); |
265 |
else printf("CAN 'T DETERMINE family type %i\n",tp[0]->family); |
266 |
printf("approx payload throughput: %" PRIu64 " Mib/s\n", ((send_calls - send_errors) / |
267 |
duration ) * payloadsize * 8 / 1000 / 1000); |
139 |
} |
268 |
} |
140 |
|
269 |
|
141 |
int |
270 |
int |
Lines 142-159
Link Here
|
142 |
main(int argc, char *argv[]) |
271 |
main(int argc, char *argv[]) |
143 |
{ |
272 |
{ |
144 |
long payloadsize, duration; |
273 |
long payloadsize, duration; |
145 |
struct addrinfo hints, *res, *res0; |
274 |
char *dummy; |
146 |
char *dummy, *packet; |
275 |
u_char *packet; |
147 |
int port, s, error; |
276 |
int port, nthreads = 1; |
148 |
const char *cause = NULL; |
277 |
struct td_desc **tp; |
|
|
278 |
struct timespec starttime, tmptime; |
279 |
struct itimerval it; |
149 |
|
280 |
|
150 |
if (argc != 5) |
281 |
|
|
|
282 |
|
283 |
if (argc < 5) |
151 |
usage(); |
284 |
usage(); |
152 |
|
285 |
|
153 |
memset(&hints, 0, sizeof(hints)); |
|
|
154 |
hints.ai_family = PF_UNSPEC; |
155 |
hints.ai_socktype = SOCK_DGRAM; |
156 |
|
157 |
port = strtoul(argv[2], &dummy, 10); |
286 |
port = strtoul(argv[2], &dummy, 10); |
158 |
if (port < 1 || port > 65535 || *dummy != '\0') { |
287 |
if (port < 1 || port > 65535 || *dummy != '\0') { |
159 |
fprintf(stderr, "Invalid port number: %s\n", argv[2]); |
288 |
fprintf(stderr, "Invalid port number: %s\n", argv[2]); |
Lines 177-182
Link Here
|
177 |
/*NOTREACHED*/ |
306 |
/*NOTREACHED*/ |
178 |
} |
307 |
} |
179 |
|
308 |
|
|
|
309 |
if (argc > 5) |
310 |
nthreads = strtoul(argv[5], &dummy, 10); |
311 |
if (nthreads < 1) |
312 |
usage(); |
313 |
int ncpu = system_ncpus(); |
314 |
if (nthreads > ncpu) { |
315 |
printf("WARNING: %d threads but only %d core(s) available\n", nthreads, ncpu); |
316 |
} |
317 |
|
180 |
packet = malloc(payloadsize); |
318 |
packet = malloc(payloadsize); |
181 |
if (packet == NULL) { |
319 |
if (packet == NULL) { |
182 |
perror("malloc"); |
320 |
perror("malloc"); |
Lines 185-221
Link Here
|
185 |
} |
323 |
} |
186 |
|
324 |
|
187 |
bzero(packet, payloadsize); |
325 |
bzero(packet, payloadsize); |
188 |
error = getaddrinfo(argv[1],argv[2], &hints, &res0); |
326 |
|
189 |
if (error) { |
327 |
printf("netblast %d threads sending on UDP port %d during %lu seconds\n", |
190 |
perror(gai_strerror(error)); |
328 |
nthreads, (u_short)port, duration); |
|
|
329 |
|
330 |
/* |
331 |
* Begin by recording time information stats. |
332 |
* Set the interval timer for when we want to wake up. |
333 |
* SIGALRM will set a flag indicating it's time to stop. Note that there's |
334 |
* some overhead to the signal and timer setup, so the smaller the duration, |
335 |
* the higher the relative overhead. |
336 |
*/ |
337 |
|
338 |
if (signal(SIGALRM, signal_handler) == SIG_ERR) { |
339 |
perror("signal"); |
191 |
return (-1); |
340 |
return (-1); |
192 |
/*NOTREACHED*/ |
|
|
193 |
} |
341 |
} |
194 |
s = -1; |
|
|
195 |
for (res = res0; res; res = res->ai_next) { |
196 |
s = socket(res->ai_family, res->ai_socktype, 0); |
197 |
if (s < 0) { |
198 |
cause = "socket"; |
199 |
continue; |
200 |
} |
201 |
|
342 |
|
202 |
if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { |
343 |
if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) { |
203 |
cause = "connect"; |
344 |
perror("clock_getres"); |
204 |
close(s); |
345 |
return (-1); |
205 |
s = -1; |
346 |
} |
206 |
continue; |
|
|
207 |
} |
208 |
|
347 |
|
209 |
break; /* okay we got one */ |
348 |
if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) { |
|
|
349 |
perror("clock_gettime"); |
350 |
return (-1); |
210 |
} |
351 |
} |
211 |
if (s < 0) { |
352 |
|
212 |
perror(cause); |
353 |
it.it_interval.tv_sec = 0; |
|
|
354 |
it.it_interval.tv_usec = 0; |
355 |
it.it_value.tv_sec = duration; |
356 |
it.it_value.tv_usec = 0; |
357 |
|
358 |
if (setitimer(ITIMER_REAL, &it, NULL) < 0) { |
359 |
perror("setitimer"); |
213 |
return (-1); |
360 |
return (-1); |
214 |
/*NOTREACHED*/ |
|
|
215 |
} |
361 |
} |
216 |
|
362 |
|
217 |
freeaddrinfo(res0); |
363 |
tp = make_threads(argv[1],argv[2], packet, payloadsize, nthreads); |
|
|
364 |
main_thread(tp, duration, starttime, tmptime, payloadsize, nthreads); |
218 |
|
365 |
|
219 |
return (blast_loop(s, duration, packet, payloadsize)); |
|
|
220 |
|
221 |
} |
366 |
} |