Wind River Support Network

HomeDefectsLIN6-8313
Fixed

LIN6-8313 : CLONE - setns() takes 5ms

Created: Sep 3, 2014    Updated: Dec 3, 2018
Resolved Date: Sep 10, 2014
Found In Version: 6.0
Fix Version: 6.0.0.12
Severity: Standard
Applicable for: Wind River Linux 6
Component/s: Kernel

Description

Userspace setns() function takes too long by design.

I measured the speed of the setns() function, using clock_gettime(CLOCK_MONOTONIC) to get the time before and after a loop that calls setns() repeatedly. Averaged over 10,000 calls, one call to setns() takes 5 milliseconds.

The kernel sequence that I think is eating the time is:

void switch_task_namespaces(struct task_struct *p, struct nsproxy *new)
{
        struct nsproxy *ns;
 
        might_sleep();
 
        ns = p->nsproxy;
 
        rcu_assign_pointer(p->nsproxy, new);
 
        if (ns && atomic_dec_and_test(&ns->count)) {
                /*
                 * wait for others to get what they want from this nsproxy.
                 *
                 * cannot release this nsproxy via the call_rcu() since
                 * put_mnt_ns() will want to sleep
                 */
                synchronize_rcu();
                free_nsproxy(ns);
        }
}
 
Specifically, the call to synchronize_rcu(), which runs a synchronization protocol that is intended to be optimized for multiple CPUs. From the documentation for the Read-Copy-Update synchronization protocol, the call will wait for one “grace period”, which seems to account for the 5 milliseconds.

On a single-CPU build, there is no other CPU to synchronize with, so setns() probably takes less time. I currently do not have a single-CPU VM to test this on.

Clearly, we need to cut the number of calls to setns() to an absolute minimum.

Steps to Reproduce

Export wrl5 SDK, build this code:

user@build-host:~/test$ $CC setns_test.c -lrt
user@build-host:~/test$ cat setns_test.c
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

static int diff(struct timespec start, struct timespec end)
{
        struct timespec temp;
        if ((end.tv_nsec-start.tv_nsec)<0) {
                temp.tv_sec = end.tv_sec-start.tv_sec-1;
                temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
        } else {
                temp.tv_sec = end.tv_sec-start.tv_sec;
                temp.tv_nsec = end.tv_nsec-start.tv_nsec;
        }
        printf("diff: %ld,%ld\n", temp.tv_sec, temp.tv_nsec);
        return 0;
}

int main(int argc, char *argv[])
{
        int fd, err, i;
        struct timespec time1, time2;

        fd = open("/proc/self/ns/net", O_RDONLY);
        if (fd == -1) {
                perror("open");
                return -1;
        }

        clock_gettime(CLOCK_MONOTONIC, &time1);
        for (i=0; i<10000; i++) {
                err = setns(fd, 0);
                if (err) {
                        perror("setns");
                        return -1;
                }
        }
        clock_gettime(CLOCK_MONOTONIC, &time2);

        close(fd);
        diff(time1, time2);
        return 0;
}


Copy it to a MATXM and run it:

root@localhost:~# time /tmp/a.out
diff: 50,89477037

real    0m50.096s
user    0m0.000s
sys     0m0.099s

Other Downloads


Live chat
Online