Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * This application is Copyright 2012 Red Hat, Inc.
  3 *	Doug Ledford <dledford@redhat.com>
  4 *
  5 * mq_perf_tests is free software: you can redistribute it and/or modify
  6 * it under the terms of the GNU General Public License as published by
  7 * the Free Software Foundation, version 3.
  8 *
  9 * mq_perf_tests is distributed in the hope that it will be useful,
 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 * GNU General Public License for more details.
 13 *
 14 * For the full text of the license, see <http://www.gnu.org/licenses/>.
 15 *
 16 * mq_perf_tests.c
 17 *   Tests various types of message queue workloads, concentrating on those
 18 *   situations that invole large message sizes, large message queue depths,
 19 *   or both, and reports back useful metrics about kernel message queue
 20 *   performance.
 21 *
 22 */
 23#define _GNU_SOURCE
 24#include <stdio.h>
 25#include <stdlib.h>
 26#include <unistd.h>
 27#include <fcntl.h>
 28#include <string.h>
 29#include <limits.h>
 30#include <errno.h>
 31#include <signal.h>
 32#include <pthread.h>
 33#include <sched.h>
 34#include <sys/types.h>
 35#include <sys/time.h>
 36#include <sys/resource.h>
 37#include <sys/stat.h>
 38#include <sys/param.h>
 39#include <mqueue.h>
 40#include <popt.h>
 41#include <error.h>
 42
 43#include "../kselftest.h"
 44
 45static char *usage =
 46"Usage:\n"
 47"  %s [-c #[,#..] -f] path\n"
 48"\n"
 49"	-c #	Skip most tests and go straight to a high queue depth test\n"
 50"		and then run that test continuously (useful for running at\n"
 51"		the same time as some other workload to see how much the\n"
 52"		cache thrashing caused by adding messages to a very deep\n"
 53"		queue impacts the performance of other programs).  The number\n"
 54"		indicates which CPU core we should bind the process to during\n"
 55"		the run.  If you have more than one physical CPU, then you\n"
 56"		will need one copy per physical CPU package, and you should\n"
 57"		specify the CPU cores to pin ourself to via a comma separated\n"
 58"		list of CPU values.\n"
 59"	-f	Only usable with continuous mode.  Pin ourself to the CPUs\n"
 60"		as requested, then instead of looping doing a high mq\n"
 61"		workload, just busy loop.  This will allow us to lock up a\n"
 62"		single CPU just like we normally would, but without actually\n"
 63"		thrashing the CPU cache.  This is to make it easier to get\n"
 64"		comparable numbers from some other workload running on the\n"
 65"		other CPUs.  One set of numbers with # CPUs locked up running\n"
 66"		an mq workload, and another set of numbers with those same\n"
 67"		CPUs locked away from the test workload, but not doing\n"
 68"		anything to trash the cache like the mq workload might.\n"
 69"	path	Path name of the message queue to create\n"
 70"\n"
 71"	Note: this program must be run as root in order to enable all tests\n"
 72"\n";
 73
 74char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max";
 75char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max";
 76
 77#define MAX_CPUS 64
 78char *cpu_option_string;
 79int cpus_to_pin[MAX_CPUS];
 80int num_cpus_to_pin;
 81pthread_t cpu_threads[MAX_CPUS];
 82pthread_t main_thread;
 83cpu_set_t *cpu_set;
 84int cpu_set_size;
 85int cpus_online;
 86
 87#define MSG_SIZE 16
 88#define TEST1_LOOPS 10000000
 89#define TEST2_LOOPS 100000
 90int continuous_mode;
 91int continuous_mode_fake;
 92
 93struct rlimit saved_limits, cur_limits;
 94int saved_max_msgs, saved_max_msgsize;
 95int cur_max_msgs, cur_max_msgsize;
 96FILE *max_msgs, *max_msgsize;
 97int cur_nice;
 98char *queue_path = "/mq_perf_tests";
 99mqd_t queue = -1;
100struct mq_attr result;
101int mq_prio_max;
102
103const struct poptOption options[] = {
104	{
105		.longName = "continuous",
106		.shortName = 'c',
107		.argInfo = POPT_ARG_STRING,
108		.arg = &cpu_option_string,
109		.val = 'c',
110		.descrip = "Run continuous tests at a high queue depth in "
111			"order to test the effects of cache thrashing on "
112			"other tasks on the system.  This test is intended "
113			"to be run on one core of each physical CPU while "
114			"some other CPU intensive task is run on all the other "
115			"cores of that same physical CPU and the other task "
116			"is timed.  It is assumed that the process of adding "
117			"messages to the message queue in a tight loop will "
118			"impact that other task to some degree.  Once the "
119			"tests are performed in this way, you should then "
120			"re-run the tests using fake mode in order to check "
121			"the difference in time required to perform the CPU "
122			"intensive task",
123		.argDescrip = "cpu[,cpu]",
124	},
125	{
126		.longName = "fake",
127		.shortName = 'f',
128		.argInfo = POPT_ARG_NONE,
129		.arg = &continuous_mode_fake,
130		.val = 0,
131		.descrip = "Tie up the CPUs that we would normally tie up in"
132			"continuous mode, but don't actually do any mq stuff, "
133			"just keep the CPU busy so it can't be used to process "
134			"system level tasks as this would free up resources on "
135			"the other CPU cores and skew the comparison between "
136			"the no-mqueue work and mqueue work tests",
137		.argDescrip = NULL,
138	},
139	{
140		.longName = "path",
141		.shortName = 'p',
142		.argInfo = POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT,
143		.arg = &queue_path,
144		.val = 'p',
145		.descrip = "The name of the path to use in the mqueue "
146			"filesystem for our tests",
147		.argDescrip = "pathname",
148	},
149	POPT_AUTOHELP
150	POPT_TABLEEND
151};
152
153static inline void __set(FILE *stream, int value, char *err_msg);
154void shutdown(int exit_val, char *err_cause, int line_no);
155void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context);
156void sig_action(int signum, siginfo_t *info, void *context);
157static inline int get(FILE *stream);
158static inline void set(FILE *stream, int value);
159static inline int try_set(FILE *stream, int value);
160static inline void getr(int type, struct rlimit *rlim);
161static inline void setr(int type, struct rlimit *rlim);
162static inline void open_queue(struct mq_attr *attr);
163void increase_limits(void);
164
165static inline void __set(FILE *stream, int value, char *err_msg)
166{
167	rewind(stream);
168	if (fprintf(stream, "%d", value) < 0)
169		perror(err_msg);
170}
171
172
173void shutdown(int exit_val, char *err_cause, int line_no)
174{
175	static int in_shutdown = 0;
176	int errno_at_shutdown = errno;
177	int i;
178
179	/* In case we get called by multiple threads or from an sighandler */
180	if (in_shutdown++)
181		return;
182
183	/* Free the cpu_set allocated using CPU_ALLOC in main function */
184	CPU_FREE(cpu_set);
185
186	for (i = 0; i < num_cpus_to_pin; i++)
187		if (cpu_threads[i]) {
188			pthread_kill(cpu_threads[i], SIGUSR1);
189			pthread_join(cpu_threads[i], NULL);
190		}
191
192	if (queue != -1)
193		if (mq_close(queue))
194			perror("mq_close() during shutdown");
195	if (queue_path)
196		/*
197		 * Be silent if this fails, if we cleaned up already it's
198		 * expected to fail
199		 */
200		mq_unlink(queue_path);
201	if (saved_max_msgs)
202		__set(max_msgs, saved_max_msgs,
203		      "failed to restore saved_max_msgs");
204	if (saved_max_msgsize)
205		__set(max_msgsize, saved_max_msgsize,
206		      "failed to restore saved_max_msgsize");
207	if (exit_val)
208		error(exit_val, errno_at_shutdown, "%s at %d",
209		      err_cause, line_no);
210	exit(0);
211}
212
213void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context)
214{
215	if (pthread_self() != main_thread)
216		pthread_exit(0);
217	else {
218		fprintf(stderr, "Caught signal %d in SIGUSR1 handler, "
219				"exiting\n", signum);
220		shutdown(0, "", 0);
221		fprintf(stderr, "\n\nReturned from shutdown?!?!\n\n");
222		exit(0);
223	}
224}
225
226void sig_action(int signum, siginfo_t *info, void *context)
227{
228	if (pthread_self() != main_thread)
229		pthread_kill(main_thread, signum);
230	else {
231		fprintf(stderr, "Caught signal %d, exiting\n", signum);
232		shutdown(0, "", 0);
233		fprintf(stderr, "\n\nReturned from shutdown?!?!\n\n");
234		exit(0);
235	}
236}
237
238static inline int get(FILE *stream)
239{
240	int value;
241	rewind(stream);
242	if (fscanf(stream, "%d", &value) != 1)
243		shutdown(4, "Error reading /proc entry", __LINE__);
244	return value;
245}
246
247static inline void set(FILE *stream, int value)
248{
249	int new_value;
250
251	rewind(stream);
252	if (fprintf(stream, "%d", value) < 0)
253		return shutdown(5, "Failed writing to /proc file", __LINE__);
254	new_value = get(stream);
255	if (new_value != value)
256		return shutdown(5, "We didn't get what we wrote to /proc back",
257				__LINE__);
258}
259
260static inline int try_set(FILE *stream, int value)
261{
262	int new_value;
263
264	rewind(stream);
265	fprintf(stream, "%d", value);
266	new_value = get(stream);
267	return new_value == value;
268}
269
270static inline void getr(int type, struct rlimit *rlim)
271{
272	if (getrlimit(type, rlim))
273		shutdown(6, "getrlimit()", __LINE__);
274}
275
276static inline void setr(int type, struct rlimit *rlim)
277{
278	if (setrlimit(type, rlim))
279		shutdown(7, "setrlimit()", __LINE__);
280}
281
282/**
283 * open_queue - open the global queue for testing
284 * @attr - An attr struct specifying the desired queue traits
285 * @result - An attr struct that lists the actual traits the queue has
286 *
287 * This open is not allowed to fail, failure will result in an orderly
288 * shutdown of the program.  The global queue_path is used to set what
289 * queue to open, the queue descriptor is saved in the global queue
290 * variable.
291 */
292static inline void open_queue(struct mq_attr *attr)
293{
294	int flags = O_RDWR | O_EXCL | O_CREAT | O_NONBLOCK;
295	int perms = DEFFILEMODE;
296
297	queue = mq_open(queue_path, flags, perms, attr);
298	if (queue == -1)
299		shutdown(1, "mq_open()", __LINE__);
300	if (mq_getattr(queue, &result))
301		shutdown(1, "mq_getattr()", __LINE__);
302	printf("\n\tQueue %s created:\n", queue_path);
303	printf("\t\tmq_flags:\t\t\t%s\n", result.mq_flags & O_NONBLOCK ?
304	       "O_NONBLOCK" : "(null)");
305	printf("\t\tmq_maxmsg:\t\t\t%lu\n", result.mq_maxmsg);
306	printf("\t\tmq_msgsize:\t\t\t%lu\n", result.mq_msgsize);
307	printf("\t\tmq_curmsgs:\t\t\t%lu\n", result.mq_curmsgs);
308}
309
310void *fake_cont_thread(void *arg)
311{
312	int i;
313
314	for (i = 0; i < num_cpus_to_pin; i++)
315		if (cpu_threads[i] == pthread_self())
316			break;
317	printf("\tStarted fake continuous mode thread %d on CPU %d\n", i,
318	       cpus_to_pin[i]);
319	while (1)
320		;
321}
322
323void *cont_thread(void *arg)
324{
325	char buff[MSG_SIZE];
326	int i;
327	unsigned int priority;
328
329	for (i = 0; i < num_cpus_to_pin; i++)
330		if (cpu_threads[i] == pthread_self())
331			break;
332	printf("\tStarted continuous mode thread %d on CPU %d\n", i,
333	       cpus_to_pin[i]);
334	while (1) {
335		while (mq_send(queue, buff, sizeof(buff), 0) == 0)
336			;
337		mq_receive(queue, buff, sizeof(buff), &priority);
338	}
339}
340
341#define drain_queue() \
342	while (mq_receive(queue, buff, MSG_SIZE, &prio_in) == MSG_SIZE)
343
344#define do_untimed_send() \
345	do { \
346		if (mq_send(queue, buff, MSG_SIZE, prio_out)) \
347			shutdown(3, "Test send failure", __LINE__); \
348	} while (0)
349
350#define do_send_recv() \
351	do { \
352		clock_gettime(clock, &start); \
353		if (mq_send(queue, buff, MSG_SIZE, prio_out)) \
354			shutdown(3, "Test send failure", __LINE__); \
355		clock_gettime(clock, &middle); \
356		if (mq_receive(queue, buff, MSG_SIZE, &prio_in) != MSG_SIZE) \
357			shutdown(3, "Test receive failure", __LINE__); \
358		clock_gettime(clock, &end); \
359		nsec = ((middle.tv_sec - start.tv_sec) * 1000000000) + \
360			(middle.tv_nsec - start.tv_nsec); \
361		send_total.tv_nsec += nsec; \
362		if (send_total.tv_nsec >= 1000000000) { \
363			send_total.tv_sec++; \
364			send_total.tv_nsec -= 1000000000; \
365		} \
366		nsec = ((end.tv_sec - middle.tv_sec) * 1000000000) + \
367			(end.tv_nsec - middle.tv_nsec); \
368		recv_total.tv_nsec += nsec; \
369		if (recv_total.tv_nsec >= 1000000000) { \
370			recv_total.tv_sec++; \
371			recv_total.tv_nsec -= 1000000000; \
372		} \
373	} while (0)
374
375struct test {
376	char *desc;
377	void (*func)(int *);
378};
379
380void const_prio(int *prio)
381{
382	return;
383}
384
385void inc_prio(int *prio)
386{
387	if (++*prio == mq_prio_max)
388		*prio = 0;
389}
390
391void dec_prio(int *prio)
392{
393	if (--*prio < 0)
394		*prio = mq_prio_max - 1;
395}
396
397void random_prio(int *prio)
398{
399	*prio = random() % mq_prio_max;
400}
401
402struct test test2[] = {
403	{"\n\tTest #2a: Time send/recv message, queue full, constant prio\n",
404		const_prio},
405	{"\n\tTest #2b: Time send/recv message, queue full, increasing prio\n",
406		inc_prio},
407	{"\n\tTest #2c: Time send/recv message, queue full, decreasing prio\n",
408		dec_prio},
409	{"\n\tTest #2d: Time send/recv message, queue full, random prio\n",
410		random_prio},
411	{NULL, NULL}
412};
413
414/**
415 * Tests to perform (all done with MSG_SIZE messages):
416 *
417 * 1) Time to add/remove message with 0 messages on queue
418 * 1a) with constant prio
419 * 2) Time to add/remove message when queue close to capacity:
420 * 2a) with constant prio
421 * 2b) with increasing prio
422 * 2c) with decreasing prio
423 * 2d) with random prio
424 * 3) Test limits of priorities honored (double check _SC_MQ_PRIO_MAX)
425 */
426void *perf_test_thread(void *arg)
427{
428	char buff[MSG_SIZE];
429	int prio_out;
430	unsigned int prio_in;
431	int i;
432	clockid_t clock;
433	pthread_t *t;
434	struct timespec res, start, middle, end, send_total, recv_total;
435	unsigned long long nsec;
436	struct test *cur_test;
437
438	t = &cpu_threads[0];
439	printf("\n\tStarted mqueue performance test thread on CPU %d\n",
440	       cpus_to_pin[0]);
441	mq_prio_max = sysconf(_SC_MQ_PRIO_MAX);
442	if (mq_prio_max == -1)
443		shutdown(2, "sysconf(_SC_MQ_PRIO_MAX)", __LINE__);
444	if (pthread_getcpuclockid(cpu_threads[0], &clock) != 0)
445		shutdown(2, "pthread_getcpuclockid", __LINE__);
446
447	if (clock_getres(clock, &res))
448		shutdown(2, "clock_getres()", __LINE__);
449
450	printf("\t\tMax priorities:\t\t\t%d\n", mq_prio_max);
451	printf("\t\tClock resolution:\t\t%lu nsec%s\n", res.tv_nsec,
452	       res.tv_nsec > 1 ? "s" : "");
453
454
455
456	printf("\n\tTest #1: Time send/recv message, queue empty\n");
457	printf("\t\t(%d iterations)\n", TEST1_LOOPS);
458	prio_out = 0;
459	send_total.tv_sec = 0;
460	send_total.tv_nsec = 0;
461	recv_total.tv_sec = 0;
462	recv_total.tv_nsec = 0;
463	for (i = 0; i < TEST1_LOOPS; i++)
464		do_send_recv();
465	printf("\t\tSend msg:\t\t\t%ld.%lus total time\n",
466	       send_total.tv_sec, send_total.tv_nsec);
467	nsec = ((unsigned long long)send_total.tv_sec * 1000000000 +
468		 send_total.tv_nsec) / TEST1_LOOPS;
469	printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec);
470	printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n",
471	       recv_total.tv_sec, recv_total.tv_nsec);
472	nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 +
473		recv_total.tv_nsec) / TEST1_LOOPS;
474	printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec);
475
476
477	for (cur_test = test2; cur_test->desc != NULL; cur_test++) {
478		printf("%s:\n", cur_test->desc);
479		printf("\t\t(%d iterations)\n", TEST2_LOOPS);
480		prio_out = 0;
481		send_total.tv_sec = 0;
482		send_total.tv_nsec = 0;
483		recv_total.tv_sec = 0;
484		recv_total.tv_nsec = 0;
485		printf("\t\tFilling queue...");
486		fflush(stdout);
487		clock_gettime(clock, &start);
488		for (i = 0; i < result.mq_maxmsg - 1; i++) {
489			do_untimed_send();
490			cur_test->func(&prio_out);
491		}
492		clock_gettime(clock, &end);
493		nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) *
494			1000000000) + (end.tv_nsec - start.tv_nsec);
495		printf("done.\t\t%lld.%llds\n", nsec / 1000000000,
496		       nsec % 1000000000);
497		printf("\t\tTesting...");
498		fflush(stdout);
499		for (i = 0; i < TEST2_LOOPS; i++) {
500			do_send_recv();
501			cur_test->func(&prio_out);
502		}
503		printf("done.\n");
504		printf("\t\tSend msg:\t\t\t%ld.%lus total time\n",
505		       send_total.tv_sec, send_total.tv_nsec);
506		nsec = ((unsigned long long)send_total.tv_sec * 1000000000 +
507			 send_total.tv_nsec) / TEST2_LOOPS;
508		printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec);
509		printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n",
510		       recv_total.tv_sec, recv_total.tv_nsec);
511		nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 +
512			recv_total.tv_nsec) / TEST2_LOOPS;
513		printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec);
514		printf("\t\tDraining queue...");
515		fflush(stdout);
516		clock_gettime(clock, &start);
517		drain_queue();
518		clock_gettime(clock, &end);
519		nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) *
520			1000000000) + (end.tv_nsec - start.tv_nsec);
521		printf("done.\t\t%lld.%llds\n", nsec / 1000000000,
522		       nsec % 1000000000);
523	}
524	return 0;
525}
526
527void increase_limits(void)
528{
529	cur_limits.rlim_cur = RLIM_INFINITY;
530	cur_limits.rlim_max = RLIM_INFINITY;
531	setr(RLIMIT_MSGQUEUE, &cur_limits);
532	while (try_set(max_msgs, cur_max_msgs += 10))
533		;
534	cur_max_msgs = get(max_msgs);
535	while (try_set(max_msgsize, cur_max_msgsize += 1024))
536		;
537	cur_max_msgsize = get(max_msgsize);
538	if (setpriority(PRIO_PROCESS, 0, -20) != 0)
539		shutdown(2, "setpriority()", __LINE__);
540	cur_nice = -20;
541}
542
543int main(int argc, char *argv[])
544{
545	struct mq_attr attr;
546	char *option, *next_option;
547	int i, cpu, rc;
548	struct sigaction sa;
549	poptContext popt_context;
550	void *retval;
551
552	main_thread = pthread_self();
553	num_cpus_to_pin = 0;
554
555	if (sysconf(_SC_NPROCESSORS_ONLN) == -1) {
556		perror("sysconf(_SC_NPROCESSORS_ONLN)");
557		exit(1);
558	}
559
560	if (getuid() != 0)
561		ksft_exit_skip("Not running as root, but almost all tests "
562			"require root in order to modify\nsystem settings.  "
563			"Exiting.\n");
564
565	cpus_online = MIN(MAX_CPUS, sysconf(_SC_NPROCESSORS_ONLN));
566	cpu_set = CPU_ALLOC(cpus_online);
567	if (cpu_set == NULL) {
568		perror("CPU_ALLOC()");
569		exit(1);
570	}
571	cpu_set_size = CPU_ALLOC_SIZE(cpus_online);
572	CPU_ZERO_S(cpu_set_size, cpu_set);
573
574	popt_context = poptGetContext(NULL, argc, (const char **)argv,
575				      options, 0);
576
577	while ((rc = poptGetNextOpt(popt_context)) > 0) {
578		switch (rc) {
579		case 'c':
580			continuous_mode = 1;
581			option = cpu_option_string;
582			do {
583				next_option = strchr(option, ',');
584				if (next_option)
585					*next_option = '\0';
586				cpu = atoi(option);
587				if (cpu >= cpus_online)
588					fprintf(stderr, "CPU %d exceeds "
589						"cpus online, ignoring.\n",
590						cpu);
591				else
592					cpus_to_pin[num_cpus_to_pin++] = cpu;
593				if (next_option)
594					option = ++next_option;
595			} while (next_option && num_cpus_to_pin < MAX_CPUS);
596			/* Double check that they didn't give us the same CPU
597			 * more than once */
598			for (cpu = 0; cpu < num_cpus_to_pin; cpu++) {
599				if (CPU_ISSET_S(cpus_to_pin[cpu], cpu_set_size,
600						cpu_set)) {
601					fprintf(stderr, "Any given CPU may "
602						"only be given once.\n");
603					goto err_code;
604				} else
605					CPU_SET_S(cpus_to_pin[cpu],
606						  cpu_set_size, cpu_set);
607			}
608			break;
609		case 'p':
610			/*
611			 * Although we can create a msg queue with a
612			 * non-absolute path name, unlink will fail.  So,
613			 * if the name doesn't start with a /, add one
614			 * when we save it.
615			 */
616			option = queue_path;
617			if (*option != '/') {
618				queue_path = malloc(strlen(option) + 2);
619				if (!queue_path) {
620					perror("malloc()");
621					goto err_code;
622				}
623				queue_path[0] = '/';
624				queue_path[1] = 0;
625				strcat(queue_path, option);
626				free(option);
627			}
628			break;
629		}
630	}
631
632	if (continuous_mode && num_cpus_to_pin == 0) {
633		fprintf(stderr, "Must pass at least one CPU to continuous "
634			"mode.\n");
635		poptPrintUsage(popt_context, stderr, 0);
636		goto err_code;
637	} else if (!continuous_mode) {
638		num_cpus_to_pin = 1;
639		cpus_to_pin[0] = cpus_online - 1;
640	}
641
642	max_msgs = fopen(MAX_MSGS, "r+");
643	max_msgsize = fopen(MAX_MSGSIZE, "r+");
644	if (!max_msgs)
645		shutdown(2, "Failed to open msg_max", __LINE__);
646	if (!max_msgsize)
647		shutdown(2, "Failed to open msgsize_max", __LINE__);
648
649	/* Load up the current system values for everything we can */
650	getr(RLIMIT_MSGQUEUE, &saved_limits);
651	cur_limits = saved_limits;
652	saved_max_msgs = cur_max_msgs = get(max_msgs);
653	saved_max_msgsize = cur_max_msgsize = get(max_msgsize);
654	errno = 0;
655	cur_nice = getpriority(PRIO_PROCESS, 0);
656	if (errno)
657		shutdown(2, "getpriority()", __LINE__);
658
659	/* Tell the user our initial state */
660	printf("\nInitial system state:\n");
661	printf("\tUsing queue path:\t\t\t%s\n", queue_path);
662	printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n",
663		(long) saved_limits.rlim_cur);
664	printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n",
665		(long) saved_limits.rlim_max);
666	printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize);
667	printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs);
668	printf("\tNice value:\t\t\t\t%d\n", cur_nice);
669	printf("\n");
670
671	increase_limits();
672
673	printf("Adjusted system state for testing:\n");
674	if (cur_limits.rlim_cur == RLIM_INFINITY) {
675		printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n");
676		printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n");
677	} else {
678		printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n",
679		       (long) cur_limits.rlim_cur);
680		printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n",
681		       (long) cur_limits.rlim_max);
682	}
683	printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize);
684	printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs);
685	printf("\tNice value:\t\t\t\t%d\n", cur_nice);
686	printf("\tContinuous mode:\t\t\t(%s)\n", continuous_mode ?
687	       (continuous_mode_fake ? "fake mode" : "enabled") :
688	       "disabled");
689	printf("\tCPUs to pin:\t\t\t\t%d", cpus_to_pin[0]);
690	for (cpu = 1; cpu < num_cpus_to_pin; cpu++)
691			printf(",%d", cpus_to_pin[cpu]);
692	printf("\n");
693
694	sa.sa_sigaction = sig_action_SIGUSR1;
695	sigemptyset(&sa.sa_mask);
696	sigaddset(&sa.sa_mask, SIGHUP);
697	sigaddset(&sa.sa_mask, SIGINT);
698	sigaddset(&sa.sa_mask, SIGQUIT);
699	sigaddset(&sa.sa_mask, SIGTERM);
700	sa.sa_flags = SA_SIGINFO;
701	if (sigaction(SIGUSR1, &sa, NULL) == -1)
702		shutdown(1, "sigaction(SIGUSR1)", __LINE__);
703	sa.sa_sigaction = sig_action;
704	if (sigaction(SIGHUP, &sa, NULL) == -1)
705		shutdown(1, "sigaction(SIGHUP)", __LINE__);
706	if (sigaction(SIGINT, &sa, NULL) == -1)
707		shutdown(1, "sigaction(SIGINT)", __LINE__);
708	if (sigaction(SIGQUIT, &sa, NULL) == -1)
709		shutdown(1, "sigaction(SIGQUIT)", __LINE__);
710	if (sigaction(SIGTERM, &sa, NULL) == -1)
711		shutdown(1, "sigaction(SIGTERM)", __LINE__);
712
713	if (!continuous_mode_fake) {
714		attr.mq_flags = O_NONBLOCK;
715		attr.mq_maxmsg = cur_max_msgs;
716		attr.mq_msgsize = MSG_SIZE;
717		open_queue(&attr);
718	}
719	for (i = 0; i < num_cpus_to_pin; i++) {
720		pthread_attr_t thread_attr;
721		void *thread_func;
722
723		if (continuous_mode_fake)
724			thread_func = &fake_cont_thread;
725		else if (continuous_mode)
726			thread_func = &cont_thread;
727		else
728			thread_func = &perf_test_thread;
729
730		CPU_ZERO_S(cpu_set_size, cpu_set);
731		CPU_SET_S(cpus_to_pin[i], cpu_set_size, cpu_set);
732		pthread_attr_init(&thread_attr);
733		pthread_attr_setaffinity_np(&thread_attr, cpu_set_size,
734					    cpu_set);
735		if (pthread_create(&cpu_threads[i], &thread_attr, thread_func,
736				   NULL))
737			shutdown(1, "pthread_create()", __LINE__);
738		pthread_attr_destroy(&thread_attr);
739	}
740
741	if (!continuous_mode) {
742		pthread_join(cpu_threads[0], &retval);
743		shutdown((long)retval, "perf_test_thread()", __LINE__);
744	} else {
745		while (1)
746			sleep(1);
747	}
748	shutdown(0, "", 0);
749
750err_code:
751	CPU_FREE(cpu_set);
752	exit(1);
753
754}