Add FreeBSD support for -A (set CPU affinity).

Mostly this change consists of adding FreeBSD-specific code to handle
this feature.  The concepts and system calls are very similar to what's
already done for Linux.  One difference is that on FreeBSD, the CPU
affinity mask is saved before -A processing and restored afterwards.
This causes a slight change to the function signatures for
iperf_setaffinity() and iperf_clearaffinity() (these functions
however are not documented as a part of the libiperf3 API).

Slightly improve some of the documentation for the -A command line
option, to hopefully stave off some of the questions about this
feature.

Mostly based on a submitted patch.

Issue:          128 (better error message for CPU affinity failure)
Submitted by:   Susant Sahani <ssahani@redhat.com>
This commit is contained in:
Bruce A. Mah 2014-01-13 10:58:47 -08:00
parent 55a453c2a1
commit deefb95fea
9 changed files with 80 additions and 18 deletions

2
README
View File

@ -44,7 +44,7 @@ New options:
-O, --omit N omit the first n seconds (to ignore slowstart)
-T, --title str prefix every output line with this string
-F, --file name xmit/recv the specified file
-A, --affinity n/n,m set CPU affinity
-A, --affinity n/n,m set CPU affinity (Linux and FreeBSD only)
-k, --blockcount #[KMG] number of blocks (packets) to transmit (instead
of -t or -n)
-L, --flowlabel set IPv6 flow label (Linux only)

View File

@ -1,3 +1,6 @@
== iperf 3.?.? ????-??-?? ==
* Setting CPU affinity now works on FreeBSD.
== iperf 3.0.1 2014-01-10 ==
* Added the following new flags
-D, --daemon run server as a daemon

View File

@ -15,6 +15,12 @@
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#if defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/cpuset.h>
#endif
#include "timer.h"
#include "queue.h"
#include "cjson.h"
@ -161,6 +167,9 @@ struct iperf_test
int duration; /* total duration of test (-t flag) */
char *diskfile_name; /* -F option */
int affinity, server_affinity; /* -A option */
#if (__FreeBSD__)
cpuset_t cpumask;
#endif
char *title; /* -T option */
char *congestion; /* -C option */

View File

@ -36,10 +36,15 @@ server-side: read from the network and write to the file, instead
of throwing the data away
.TP
.BR -A ", " --affinity " \fIn/n,m\fR"
Set the CPU affinity, if possible (linux only).
On both the client and server you can set the local affinity;
in addition, on the client side you can override the server's
affinity for just that one test, using the n,m form.
Set the CPU affinity, if possible (Linux and FreeBSD only).
On both the client and server you can set the local affinity by using
the \fIn\fR form of this argument (where \fIn\fR is a CPU number).
In addition, on the client side you can override the server's
affinity for just that one test, using the \fIn,m\fR form of
argument.
Note that when using this feature, a process will only be bound
to a single CPU (as opposed to a set containing potentialy multiple
CPUs).
.TP
.BR -V ", " --verbose " "
give more detailed output

View File

@ -35,6 +35,11 @@
#include <setjmp.h>
#include <stdarg.h>
#if defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/cpuset.h>
#endif
#include "net.h"
#include "iperf.h"
#include "iperf_api.h"
@ -531,7 +536,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{"zerocopy", no_argument, NULL, 'Z'},
{"omit", required_argument, NULL, 'O'},
{"file", required_argument, NULL, 'F'},
#if defined(linux) || defined(__FreeBSD__)
{"affinity", required_argument, NULL, 'A'},
#endif
{"title", required_argument, NULL, 'T'},
#if defined(linux)
{"linux-congestion", required_argument, NULL, 'C'},
@ -713,6 +720,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
test->diskfile_name = optarg;
break;
case 'A':
#if defined(linux) || defined(__FreeBSD__)
test->affinity = atoi(optarg);
if (test->affinity < 0 || test->affinity > 1024) {
i_errno = IEAFFINITY;
@ -727,6 +735,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
}
client_flag = 1;
}
#else
i_errno = IEUNIMP;
return -1;
#endif
break;
case 'T':
test->title = malloc(strlen(optarg) + 4);
@ -1457,6 +1469,9 @@ iperf_defaults(struct iperf_test *testp)
testp->diskfile_name = (char*) 0;
testp->affinity = -1;
testp->server_affinity = -1;
#if defined(__FreeBSD__)
CPU_ZERO(&testp->cpumask);
#endif
testp->title = NULL;
testp->congestion = NULL;
testp->server_port = PORT;
@ -1614,6 +1629,9 @@ iperf_reset_test(struct iperf_test *test)
test->omit = OMIT;
test->duration = DURATION;
test->server_affinity = -1;
#if defined(__FreeBSD__)
CPU_ZERO(&test->cpumask);
#endif
test->state = 0;
if(test->title) {
@ -2399,10 +2417,10 @@ iperf_json_finish(struct iperf_test *test)
}
/* CPU affinity stuff - linux only. */
/* CPU affinity stuff - Linux and FreeBSD only. */
int
iperf_setaffinity(int affinity)
iperf_setaffinity(struct iperf_test *test, int affinity)
{
#ifdef linux
cpu_set_t cpu_set;
@ -2414,14 +2432,32 @@ iperf_setaffinity(int affinity)
return -1;
}
return 0;
#else /*linux*/
#elif (__FreeBSD__)
cpuset_t cpumask;
if(cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
sizeof(cpuset_t), &test->cpumask) != 0) {
i_errno = IEAFFINITY;
return -1;
}
CPU_ZERO(&cpumask);
CPU_SET(affinity, &cpumask);
if(cpuset_setaffinity(CPU_LEVEL_WHICH,CPU_WHICH_PID, -1,
sizeof(cpuset_t), &cpumask) != 0) {
i_errno = IEAFFINITY;
return -1;
}
return 0;
#else /* Linux or FreeBSD */
i_errno = IEAFFINITY;
return -1;
#endif /*linux*/
#endif /* Linux or FreeBSD*/
}
int
iperf_clearaffinity(void)
iperf_clearaffinity(struct iperf_test *test)
{
#ifdef linux
cpu_set_t cpu_set;
@ -2435,10 +2471,17 @@ iperf_clearaffinity(void)
return -1;
}
return 0;
#else /*linux*/
#elif (__FreeBSD__)
if(cpuset_setaffinity(CPU_LEVEL_WHICH,CPU_WHICH_PID, -1,
sizeof(cpuset_t), &test->cpumask) != 0) {
i_errno = IEAFFINITY;
return -1;
}
return 0;
#else /* Linux or FreeBSD */
i_errno = IEAFFINITY;
return -1;
#endif /*linux*/
#endif /* Linux or FreeBSD */
}
int

View File

@ -213,8 +213,8 @@ int iperf_json_start(struct iperf_test *);
int iperf_json_finish(struct iperf_test *);
/* CPU affinity routines */
int iperf_setaffinity(int affinity);
int iperf_clearaffinity(void);
int iperf_setaffinity(struct iperf_test *, int affinity);
int iperf_clearaffinity(struct iperf_test *);
/* Custom printf routine. */
int iprintf(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3)));

View File

@ -342,7 +342,7 @@ iperf_run_client(struct iperf_test * test)
iperf_got_sigend(test);
if (test->affinity != -1)
if (iperf_setaffinity(test->affinity) != 0)
if (iperf_setaffinity(test, test->affinity) != 0)
return -1;
if (test->json_output)

View File

@ -136,7 +136,7 @@ iperf_accept(struct iperf_test *test)
if (iperf_exchange_parameters(test) < 0)
return -1;
if (test->server_affinity != -1)
if (iperf_setaffinity(test->server_affinity) != 0)
if (iperf_setaffinity(test, test->server_affinity) != 0)
return -1;
if (test->on_connect)
test->on_connect(test);
@ -436,7 +436,7 @@ iperf_run_server(struct iperf_test *test)
iperf_got_sigend(test);
if (test->affinity != -1)
if (iperf_setaffinity(test->affinity) != 0)
if (iperf_setaffinity(test, test->affinity) != 0)
return -1;
if (test->json_output)
@ -603,7 +603,7 @@ iperf_run_server(struct iperf_test *test)
}
if (test->server_affinity != -1)
if (iperf_clearaffinity() != 0)
if (iperf_clearaffinity(test) != 0)
return -1;
return 0;

View File

@ -73,7 +73,9 @@ const char usage_longstr[] = "Usage: iperf [-s|-c host] [options]\n"
" -f, --format [kmgKMG] format to report: Kbits, Mbits, KBytes, MBytes\n"
" -i, --interval # seconds between periodic bandwidth reports\n"
" -F, --file name xmit/recv the specified file\n"
#if defined(linux) || defined(__FreeBSD__)
" -A, --affinity n/n,m set CPU affinity\n"
#endif
" -V, --verbose more detailed output\n"
" -J, --json output in JSON format\n"
" -v, --version show version information and quit\n"