13

I am trying to spawn threads with SCHED_FIFO or SCHED_RR policies as root on a Linux system but my calls to pthread_create() are returning 1 (EPERM). The man page for pthread_create() says that EPERM indicates that "[t]he caller does not have appropriate permission to set the required scheduling parameters or scheduling policy." Shouldn't root be able to specify SCHED_FIFO or SCHED_RR?

I've stripped out the code that creates a thread into a small program that does only that. It looks right to me but still gets the error. What am I doing wrong?

The program:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

static void *_Thread(void *arg)
{
    (void)arg;
    printf("Thread running!\n");
    return NULL;
}

int main(void)
{
    int retVal;
    pthread_attr_t attr;
    struct sched_param schedParam;
    pthread_t thread;

    retVal = pthread_attr_init(&attr);
    if (retVal)
    {
        fprintf(stderr, "pthread_attr_init error %d\n", retVal);
        exit(1);
    }

    retVal = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    if (retVal)
    {
        fprintf(stderr, "pthread_attr_setinheritsched error %d\n", retVal);
        exit(1);
    }

    retVal = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
    if (retVal)
    {
        fprintf(stderr, "pthread_attr_setschedpolicy error %d\n", retVal);
        exit(1);
    }

    schedParam.sched_priority = 1;
    retVal = pthread_attr_setschedparam(&attr, &schedParam);
    if (retVal)
    {
        fprintf(stderr, "pthread_attr_setschedparam error %d\n", retVal);
        exit(1);
    }

    retVal = pthread_create(&thread,
                            &attr,
                            _Thread,
                            NULL);
    if (retVal)
    {
        fprintf(stderr, "pthread_create error %d\n", retVal);
        exit(1);
    }

    retVal = pthread_join(thread, NULL);
    if (retVal)
    {
        fprintf(stderr, "pthread_join error %d\n", retVal);
        exit(1);
    }

    printf("main run successfully\n");
    return 0;
}

This program has been compiled and run as root. When run, it program fails at the call to pthread_create, returning EPERM.

Changing the thread to SCHED_RR scheduling has no effect - EPERM still returned by pthread_create.

Changing the thread to SCHED_OTHER scheduling and its priority to 0 allows the program to run without error.

ChrisL
  • 133
  • 1
  • 1
  • 5
  • I believe you need `CAP_SYS_NICE` capability set for the calling user. I don't know much about how this works with `sudo` etc. Check out `man 7 capabilities`. – Nikolai Fetissov Feb 16 '12 at 15:04
  • It looks like you can set the nice level using `ulimit -e` in the same way as setting the real-time priority limit. It hasn't provided me with a "eureka!" moment yet, though. – ChrisL Feb 16 '12 at 15:42
  • The Linux systems we use at work are virtualized servers. I'm grasping at straws here but is that likely to have any effect on setting real-time priorities? – ChrisL Feb 16 '12 at 15:51
  • Look in /etc/security/limits.conf for an "rtprio" entry. That is probably the issue. Virtual servers are usually set up to be completely standalone in terms of things like this. Do you have a full local root filesystem (not inherited)? – jim mcnamara Feb 16 '12 at 16:56
  • The only reference to "rtprio" in /etc/security/limits.conf is in a comment. I found someone who has Linux (Ubuntu) running on a laptop and I got him to compile and run the program above and it worked for him so it's something to do with our Linux servers that's causing the issue. I'll check with our IT guy when he comes in tomorrow about the nature of their root filesystems. – ChrisL Feb 16 '12 at 17:15

4 Answers4

8

Surprisingly, I ran into the same problem as the OP almost 10 years later: CentOS 7.6, spawning a realtime thread, root user, ulimit -r says "unlimited", yet the thread creation fails returning EPERM. It turned out the failure is caused by the following clause in Linux kernel:

kernel/sched/core.c

          if (user) {
  #ifdef CONFIG_RT_GROUP_SCHED
                  /*
                   * Do not allow realtime tasks into groups that have
no runtime
                   * assigned.
                   */
                  if (rt_bandwidth_enabled() && rt_policy(policy) &&
                                  task_group(p)->rt_bandwidth.rt_runtime == 0 &&
                                  !task_group_is_autogroup(task_group(p))) {
                              task_rq_unlock(rq, p, &flags);
                              return -EPERM;
                  }
  #endif

Given that, and Linux cgroup documentation https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt, and CONFIG_RT_GROUP_SCHED documentation https://www.kernel.org/doc/Documentation/scheduler/sched-rt-group.txt, the problem is resolved by adding the current shell to the existing cgroup with realtime scheduling info:

cd /sys/fs/cgroup/cpu
echo $$ > tasks

Then running the executable spawning a realtime thread from the current shell the thread creation succeeds.

glagolig
  • 1,100
  • 1
  • 12
  • 28
  • 1
    Wow! I totally hit exactly this same thing. Man... just when I thought I'd seen every super obscure roadblock out there! Thank you for tracking it down! – Mike Andrews Jul 25 '21 at 23:05
  • Fascinating. We have ran into the same problem at CERN, and this fixed it. – Martin Cejp Apr 26 '22 at 20:04
8

It must be that your real-time priority soft limit is too restrictive.

Either call ulimit -r unlimited in shell before running your code. Or call setrlimit(RLIMIT_RTPRIO, ...) directly in your code.

System-wide limits are set in /etc/security/limits.conf.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Calling `ulimit -r` did indeed show that root's real-time priority limit is 0. I have changed it first using `ulimit -r unlimited` and then in my program with `setrlimit(...)` but while I could verify that the limit had changed, I still got EPERM. – ChrisL Feb 16 '12 at 15:25
  • Are you in a special envrionment, maybe like ARM? What does uname -a show? – jim mcnamara Feb 16 '12 at 16:51
  • `uname -a` gives me `Linux cumanta 2.6.32-042stab044.17 #1 SMP Fri Jan 13 12:53:58 MSK 2012 i686 i686 i386 GNU/Linux`. The machine runs CentOS 6.2. – ChrisL Feb 16 '12 at 17:16
  • 1
    I ran your code on Fedora 14 and it completes successfully. However, I have custom settings in my `/etc/security/limits.conf`: `@wheel hard rtprio unlimited` and `@wheel soft rtprio 10` and my user belongs to group `wheel`. – Maxim Egorushkin Feb 16 '12 at 19:11
  • 1
    I also ran your code on CentOS 6.2 `Linux 2.6.32-220.2.1.el6.x86_64 #1 SMP Tue Dec 13 16:21:34 EST 2011 x86_64 x86_64 x86_64 GNU/Linux` with default `/etc/security/limits.conf`. It fails under my non-privileged user and succeeds when run as `root`, as expected. – Maxim Egorushkin Feb 16 '12 at 19:15
  • It seems to be something to do with the kernel build used by CentOS 6.2 for virtualized servers. My code's been tested on a virtualized CentOS 5 server and worked fine there (once the real-time priorities were tweaked) and already worked on a standalone Ubuntu box so it seems to be something to do with that kernel. Thanks for all the help, demonstrating that it was the environment and not the code is enough for now I think! – ChrisL Feb 17 '12 at 15:29
1

Run the program as root user. Because we are messing with scheduling (its for FIFO and RR). you're messing also (by implication) with relative priorities, and that only root can do.

Running above program normally, pthread_create() returns EPERM, but as root user is works fine.

For more info visit: http://www.linuxforums.org/forum/programming-scripting/111359-pthread-error.html

Mike Andrews
  • 3,045
  • 18
  • 28
1

I've tested your code on Linux (2.6 kernel), Solaris 9 & 10. No problems. It has to do with your real-time priority settings. You can change that with:

ulimit -r unlimited

oops. Maxim already got here with the answer....

jim mcnamara
  • 16,005
  • 2
  • 34
  • 51