157257Sdufault/*
257257Sdufault * Copyright (c) 1996 - 2000
357257Sdufault *	HD Associates, Inc.  All rights reserved.
457257Sdufault *
557257Sdufault * Redistribution and use in source and binary forms, with or without
657257Sdufault * modification, are permitted provided that the following conditions
757257Sdufault * are met:
857257Sdufault * 1. Redistributions of source code must retain the above copyright
957257Sdufault *    notice, this list of conditions and the following disclaimer.
1057257Sdufault * 2. Redistributions in binary form must reproduce the above copyright
1157257Sdufault *    notice, this list of conditions and the following disclaimer in the
1257257Sdufault *    documentation and/or other materials provided with the distribution.
1357257Sdufault * 3. All advertising materials mentioning features or use of this software
1457257Sdufault *    must display the following acknowledgement:
1557257Sdufault *	This product includes software developed by HD Associates, Inc
1657257Sdufault * 4. Neither the name of the author nor the names of any co-contributors
1757257Sdufault *    may be used to endorse or promote products derived from this software
1857257Sdufault *    without specific prior written permission.
1957257Sdufault *
2057257Sdufault * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES AND CONTRIBUTORS ``AS IS'' AND
2157257Sdufault * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2257257Sdufault * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2357257Sdufault * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
2457257Sdufault * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2557257Sdufault * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2657257Sdufault * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2757257Sdufault * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2857257Sdufault * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2957257Sdufault * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3057257Sdufault * SUCH DAMAGE.
3157257Sdufault *
3257257Sdufault * $FreeBSD$
3357257Sdufault */
3457257Sdufault#include <sys/types.h>
3557257Sdufault#include <sys/mman.h>
3657257Sdufault#include <sys/time.h>
37289954Sngie#include <err.h>
38289954Sngie#include <errno.h>
39289954Sngie#include <fcntl.h>
4057257Sdufault#include <sched.h>
4157257Sdufault#include <signal.h>
42289954Sngie#include <stdlib.h>
43289954Sngie#include <stdio.h>
44289954Sngie#include <unistd.h>
4557257Sdufault
4657257Sdufaultvolatile int ticked;
4757257Sdufault#define CAN_USE_ALARMS
4857257Sdufault
4957257Sdufault#ifdef CAN_USE_ALARMS
5057257Sdufaultvoid tick(int arg)
5157257Sdufault{
5257257Sdufault	ticked = 1;
5357257Sdufault}
5457257Sdufault#endif
5557257Sdufault
5657257Sdufault/* Fifo: Verify that fifo and round-robin scheduling seem to work.
5757257Sdufault *
5857257Sdufault * This tests:
5957257Sdufault * 1. That sched_rr_get_interval seems to work;
6057257Sdufault * 2. That FIFO scheduling doesn't seeem to be round-robin;
6157257Sdufault * 3. That round-robin scheduling seems to work.
6257257Sdufault *
6357257Sdufault */
6457257Sdufaultstatic pid_t child;
6557257Sdufaultstatic void tidyup(void)
6657257Sdufault{
6757257Sdufault	if (child)
6857257Sdufault		kill(child, SIGHUP);
6957257Sdufault}
7057257Sdufault
7157257Sdufaultstatic double
7257257Sdufaulttvsub(const struct timeval *a, const struct timeval *b)
7357257Sdufault{
7457257Sdufault	long sdiff;
7557257Sdufault	long udiff;
7657257Sdufault
7757257Sdufault	sdiff = a->tv_sec - b->tv_sec;
7857257Sdufault	udiff = a->tv_usec - b->tv_usec;
7957257Sdufault
8057257Sdufault	return (double)(sdiff * 1000000 + udiff) / 1e6;
8157257Sdufault}
8257257Sdufault
8357257Sdufaultint fifo(int argc, char *argv[])
8457257Sdufault{
8557257Sdufault	int e = 0;
8657257Sdufault	volatile long *p, pid;
8757257Sdufault	int i;
8857257Sdufault	struct sched_param fifo_param;
8957257Sdufault	struct timespec interval;
9057257Sdufault#define MAX_RANAT 32
9157257Sdufault	struct timeval ranat[MAX_RANAT];
9257257Sdufault
9357257Sdufault#ifdef CAN_USE_ALARMS
9457257Sdufault	static struct itimerval itimerval;
9557257Sdufault#endif
9657257Sdufault
9757257Sdufault	/* What is the round robin interval?
9857257Sdufault	 */
9957257Sdufault
10057257Sdufault	if (sched_rr_get_interval(0, &interval) == -1) {
10157257Sdufault		perror("sched_rr_get_interval");
10257257Sdufault		exit(errno);
10357257Sdufault	}
10457257Sdufault
10557257Sdufault#ifdef CAN_USE_ALARMS
10657257Sdufault	signal(SIGALRM, tick);
10757257Sdufault#endif
10857257Sdufault
10957257Sdufault	fifo_param.sched_priority = 1;
11057257Sdufault
11157257Sdufault	p = (long *)mmap(0, sizeof(*p),
112289954Sngie	PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
11357257Sdufault
11457257Sdufault	if (p == (long *)-1)
11557257Sdufault		err(errno, "mmap");
11657257Sdufault
11757257Sdufault	*p = 0;
11857257Sdufault
11957257Sdufault	if (sched_setscheduler(0, SCHED_FIFO, &fifo_param) == -1)
12057257Sdufault	{
12157257Sdufault		perror("sched_setscheduler");
12257257Sdufault		return -1;
12357257Sdufault	}
12457257Sdufault
12557257Sdufault	pid = getpid();
12657257Sdufault
12757257Sdufault	if ((child = fork()) == 0)
12857257Sdufault	{
12957257Sdufault		/* Child process.  Just keep setting the pointer to our
13057257Sdufault		 * PID.  The parent will kill us when it wants to.
13157257Sdufault		 */
13257257Sdufault
13357257Sdufault		pid = getpid();
13457257Sdufault		while (1)
13557257Sdufault			*p = pid;
13657257Sdufault	}
13757257Sdufault	else
13857257Sdufault	{
13957257Sdufault		atexit(tidyup);
14057257Sdufault		*p = pid;
14157257Sdufault
14257257Sdufault
14357257Sdufault		ticked = 0;
14457257Sdufault
14557257Sdufault#ifdef CAN_USE_ALARMS
14657257Sdufault		/* Set an alarm for 250 times the round-robin interval.
14757257Sdufault		 * Then we will verify that a similar priority process
14857257Sdufault		 * will not run when we are using the FIFO scheduler.
14957257Sdufault		 */
15057257Sdufault		itimerval.it_value.tv_usec = interval.tv_nsec / (1000 / 250);
15157257Sdufault
15257257Sdufault		itimerval.it_value.tv_sec = itimerval.it_value.tv_usec / 1000000;
15357257Sdufault		itimerval.it_value.tv_usec %= 1000000;
15457257Sdufault
15557257Sdufault
15657257Sdufault		if (setitimer(ITIMER_REAL, &itimerval, 0) == -1) {
15757257Sdufault			perror("setitimer");
15857257Sdufault			exit(errno);
15957257Sdufault		}
16057257Sdufault#endif
16157257Sdufault
16257257Sdufault
16357257Sdufault		gettimeofday(ranat, 0);
16457257Sdufault		i = 1;
16557257Sdufault		while (!ticked && i < MAX_RANAT)
16657257Sdufault			if (*p == child) {
16757257Sdufault				gettimeofday(ranat + i, 0);
16857257Sdufault				*p = 0;
16957257Sdufault				e = -1;
17057257Sdufault				i++;
17157257Sdufault			}
17257257Sdufault
17357257Sdufault		if (e) {
17457257Sdufault			int j;
17557257Sdufault
17657257Sdufault			fprintf(stderr,
17757257Sdufault			"SCHED_FIFO had erroneous context switches:\n");
17857257Sdufault			for (j = 1; j < i; j++) {
17957257Sdufault				fprintf(stderr, "%d %g\n", j,
18057257Sdufault					tvsub(ranat + j, ranat + j - 1));
18157257Sdufault			}
18257257Sdufault			return e;
18357257Sdufault		}
18457257Sdufault
18557257Sdufault		/* Switch to the round robin scheduler and the child
18657257Sdufault		 * should run within twice the interval.
18757257Sdufault		 */
18857257Sdufault		if (sched_setscheduler(child, SCHED_RR, &fifo_param) == -1 ||
18957257Sdufault		sched_setscheduler(0, SCHED_RR, &fifo_param) == -1)
19057257Sdufault		{
19157257Sdufault			perror("sched_setscheduler");
19257257Sdufault			return -1;
19357257Sdufault		}
19457257Sdufault
19557257Sdufault		e = -1;
19657257Sdufault
19757257Sdufault		ticked = 0;
19857257Sdufault
19957257Sdufault#ifdef CAN_USE_ALARMS
20057257Sdufault
20157257Sdufault		/* Now we do want to see it run.  But only set
20257257Sdufault		 * the alarm for twice the interval:
20357257Sdufault		 */
20457257Sdufault		itimerval.it_value.tv_usec = interval.tv_nsec / 500;
20557257Sdufault
20657257Sdufault		if (setitimer(ITIMER_REAL, &itimerval, 0) == -1) {
20757257Sdufault			perror("setitimer");
20857257Sdufault			exit(errno);
20957257Sdufault		}
21057257Sdufault#endif
21157257Sdufault
21257257Sdufault		for (i = 0; !ticked; i++)
21357257Sdufault			if (*p == child) {
21457257Sdufault				e = 0;
21557257Sdufault				break;
21657257Sdufault			}
21757257Sdufault
21857257Sdufault		if (e)
21957257Sdufault			fprintf(stderr,"Child never ran when it should have.\n");
22057257Sdufault	}
22157257Sdufault
22257257Sdufault	exit(e);
22357257Sdufault}
22457257Sdufault
22557257Sdufault#ifdef STANDALONE_TESTS
22657257Sdufaultint main(int argc, char *argv[]) { return fifo(argc, argv); }
22757257Sdufault#endif
228