Linux Audio

Check our new training course

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