[BUG] sched_setattr() SCHED_DEADLINE hangs system

From: Michael Kerrisk (man-pages)
Date: Sun May 11 2014 - 10:55:01 EST


[Dave: I wonder if there's anything trinity can add in the way of
a test here?]

Hi Peter,

This looks like another bug in sched_setattr(). Using the program
below (which you might find generally helpful for testing), I'm
able to reliably freeze up my x64 (Intel Core i7-3520M Processor)
system for up to about a minute when I run with the following
command line:

$ time sudo ./t_sched_setattr d 18446744072 18446744072 18446744073

'd' here means use SCHED_DEADLINE, then the remaining arguments
are the Runtime, Deadline, and Period, expressed in *seconds*.
(Those number by the way are just a little below 2^64.)

Aside from interpreting its command-line arguments, all that the
program does is call sched_setattr() and displays elapsed times.
(By the way, on my system I see some weird effects for time(2),
presumably VDSO effects.)

Here's sample run:

time sudo ./t_sched_setattr d 18446744072 18446744072 18446744073
Runtime = 18446744072000000000
Deadline = 18446744072000000000
Period = 18446744073000000000
About to call sched_setattr()
Successful return from sched_setattr() [6 seconds]

real 0m40.421s
user 0m3.097s
sys 0m30.804s

After unfreezing the machine is fine, while the program is running,
the machine is pretty unresponsive.

I'm on kernel 3.15-rc4.

Cheers,

Michael


=====
/*#* t_sched_setattr.c
*/
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <sys/syscall.h>
#include <inttypes.h>
#include <sched.h>
#include <time.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

struct sched_attr {
uint32_t size;

uint32_t sched_policy;
uint64_t sched_flags;

/* SCHED_NORMAL, SCHED_BATCH */
int32_t sched_nice;

/* SCHED_FIFO, SCHED_RR */
uint32_t sched_priority;

/* SCHED_DEADLINE */
uint64_t sched_runtime;
uint64_t sched_deadline;
uint64_t sched_period;
};

#ifdef __x86_64__
#define __NR_sched_setattr 314
#define __NR_sched_getattr 315
#endif

#ifdef __i386__
#define __NR_sched_setattr 351
#define __NR_sched_getattr 352
#endif

#ifdef __arm__
#define __NR_sched_setattr 380
#define __NR_sched_getattr 381
#endif

#ifndef SCHED_DEADLINE
#define SCHED_DEADLINE 6
#endif

#ifndef SCHED_FLAG_RESET_ON_FORK
#define SCHED_FLAG_RESET_ON_FORK 0x01
#endif

static int
sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
return syscall(__NR_sched_setattr, pid, attr, flags);
}

static void
usageError(char *pname)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, " Non-realtime:\n");
fprintf(stderr, " %s {o|b|i} <nice>\n", pname);
fprintf(stderr, " Realtime:\n");
fprintf(stderr, " %s {f|r} <prio>\n", pname);
fprintf(stderr, " Deadline:\n");
fprintf(stderr, " %s d <runtime> <deadline> <period>\n",
pname);
fprintf(stderr, " (runtime, deadline, and period are in "
"seconds,\n");
fprintf(stderr, " unless -m or -n specified)\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -f SCHED_FLAG_RESET_ON_FORK\n");
fprintf(stderr, " -m Deadline times are in milliseconds\n");
fprintf(stderr, " -n Deadline times are in nanoseconds\n");
fprintf(stderr, " -s size Value for size argument\n");
fprintf(stderr, " -p pid PID of target process (default "
"is self)\n");
fprintf(stderr, " -v val Value for unused bytes of 'attr' "
"buffer\n");
fprintf(stderr, " -w nsecs Sleep time (only valid when setting "
"policy for self)\n");
exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
size_t size, alloc_size;;
int opt, flags, val;
struct sched_attr *sa;
pid_t pid;
int sleepTime;
long long timeFactor;
time_t base;

/* Parse command-line arguments */

sleepTime = 0;
pid = 0;
flags = 0;
size = sizeof(struct sched_attr);
val = 0;
timeFactor = 1000 * 1000 * 1000;


while ((opt = getopt(argc, argv, "fmnp:s:v:w:")) != -1) {
switch (opt) {
case 'f': flags = SCHED_FLAG_RESET_ON_FORK; break;
case 'm': timeFactor = 1000 * 1000; break;
case 'n': timeFactor = 1; break;
case 'p': pid = atoi(optarg); break;
case 's': size = atoi(optarg); break;
case 'v': val = atoi(optarg); break;
case 'w': sleepTime = atoi(optarg); break;
default: usageError(argv[1]);
}
}

if (optind + 1 > argc)
usageError(argv[0]);

alloc_size = (size > sizeof(struct sched_attr)) ? size :
sizeof(struct sched_attr);
sa = malloc(alloc_size);
if (sa == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}

/* Initializing bytes in buffer to nonzero values allows us to test
the E2BIG error case */

memset(sa, val, alloc_size);

sa->size = size;
sa->sched_flags = flags;

switch (argv[optind][0]) {

case 'o':
sa->sched_policy = SCHED_OTHER;
sa->sched_nice = atoi(argv[optind + 1]);
break;

case 'b':
sa->sched_policy = SCHED_BATCH;
sa->sched_priority = 1;
sa->sched_nice = atoi(argv[optind + 1]);
break;

case 'i':
sa->sched_policy = SCHED_IDLE;
sa->sched_nice = atoi(argv[optind + 1]);
/* Yes, SCHED_IDLE doesn't use nice values, but this let's us
test what happen if a nonzero nice value is given */
break;

case 'f':
sa->sched_policy = SCHED_FIFO;
sa->sched_priority = atoi(argv[optind + 1]);
break;

case 'r':
sa->sched_policy = SCHED_RR;
sa->sched_priority = atoi(argv[optind + 1]);
break;

case 'd':
if (argc != optind + 4)
usageError(argv[0]);
sa->sched_policy = SCHED_DEADLINE;
sa->sched_runtime = atoll(argv[optind + 1]) * timeFactor;
sa->sched_deadline = atoll(argv[optind + 2]) * timeFactor;
sa->sched_period = atoll(argv[optind + 3]) * timeFactor;
printf("Runtime = %25" PRIu64 "\nDeadline = %25" PRIu64
"\nPeriod = %25" PRIu64 "\n",
sa->sched_runtime, sa->sched_deadline, sa->sched_period);
break;

default:
usageError(argv[0]);
}

printf("About to call sched_setattr()\n");

usleep(10000);

base = time(NULL);
if (sched_setattr(pid, sa, 0) == -1) {
perror("sched_setattr");
printf("sa->size = %" PRIu32 "\n", sa->size);
exit(EXIT_FAILURE);
}

usleep(10000); /* Without this small sleep, time() does not
seem to return an up-to-date time. Some VDSO
effect? */

printf("Successful return from sched_setattr() [%ld seconds]\n",
(long) time(NULL) - base);

/* If we set our own scheduling policy and attributes, then we
optionally sleep for a while, so we can inspect process
attributes */

if ((pid == 0 || pid == getpid()) && sleepTime > 0)
sleep(sleepTime);

exit(EXIT_SUCCESS);
}


--
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/