Forced preemption in Linux kernel

73 views Asked by At

I played with kernel preemption. Idea was to see how higher priority RT process preempts lower priority process running in kernel. In order to get into kernel, simple module was created with read() syscall callback:

#include <linux/cdev.h>

#include <linux/delay.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/sched.h>


#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO */
#include <linux/init.h>         /* Needed for the macros */


static dev_t my_dev;
static struct cdev *my_cdev;

// sysfs class structure
static struct class *mychardev_class = NULL;

// callback for read() system call
static ssize_t my_read(struct file *file, char __user *buf,size_t count,loff_t *ppos)
{
    int len = 20;
    if(*ppos > 0)
    {
        return 0;
    }
    msleep(10000);
    if (copy_to_user(buf, "hello from demo_dev ", len)) {
        return -EFAULT;
    } else {
        *ppos +=len;
        return len;
    }
}


static struct file_operations my_fops =
{
    .owner = THIS_MODULE,
    .read = my_read,
};




static int __init hello_init (void)
{

    my_dev = MKDEV(400,0);
    
    // create sysfs class
    mychardev_class = class_create(THIS_MODULE, "mychardev");
    
    register_chrdev_region(my_dev,1,"demo");

    my_cdev=cdev_alloc();
    if(!my_cdev)
    {
        printk (KERN_ERR "cdev alloc error.\n");
        return -1;      
    }
    
    my_cdev->ops = &my_fops;
    my_cdev->owner = THIS_MODULE;
    
    cdev_init(my_cdev, &my_fops);
    
    if(cdev_add(my_cdev,my_dev,1))
    {
        printk (KERN_ERR "cdev add error.\n");
        return -1;      
    }

    // create device node /dev/demo_dev
    device_create(mychardev_class, NULL, my_dev, NULL, "demo_dev");
        
    printk (KERN_INFO "demo_dev added.\n");
    return 0;

}


static void __exit hello_exit (void)
{
    
    device_destroy(mychardev_class, my_dev);
    cdev_del(my_cdev);
    
    class_unregister(mychardev_class);
    class_destroy(mychardev_class);
    
    unregister_chrdev_region(my_dev, 1);
    printk (KERN_INFO "demo_dev removed.\n");
}


module_init (hello_init);
module_exit (hello_exit);
MODULE_LICENSE("GPL");

To test it, 2 threads were created with SCHED_FIFO policy, one with higher priority 50 and one with lower priority 30, created 2 seconds later:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int off;

void *hi_prio(void *p)
{
    printf("thread1 start time=%ld\n",time(NULL)-off);
    sleep(3);
    printf("thread1 stop time=%ld\n",time(NULL)-off);
    return NULL;
}

void *low_prio(void *p)
{
    int ret;
    char buf[20];
    int fd=open("/dev/demo_dev",O_RDONLY);  // #mknod /dev/demo c 400 0
    printf("thread2 start time=%ld\n",time(NULL)-off);
    ret = read(fd,buf,20);
    printf("thread2 read:%s\n");
    printf("thread2 stop time=%ld\n",time(NULL)-off);
    return NULL;
}

int main()
{
    pthread_t t1,t2,t3;
    
    pthread_attr_t attr;
    
    struct sched_param param;
      
    pthread_attr_init(&attr);
    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
    
    param.sched_priority = 50;
    pthread_attr_setschedparam(&attr, &param);
    
    off = time(NULL);
    
    pthread_create(&t1,&attr,hi_prio,NULL);
    
    param.sched_priority = 30;
    pthread_attr_setschedparam(&attr, &param);
    
    sleep(2);
    pthread_create(&t2,&attr,low_prio,NULL);
    
    sleep(20);
    printf("main thread stop time=%ld\n",time(NULL)-off);
    puts("end test");
    return 0;
}

High priority thread slept for 3 seconds and result is as expected:

thread1 start time=0
thread2 start time=2
thread1 stop time=3
thread2 read:hello from demo_dev
thread2 stop time=13
main thread stop time=22
end test

But when i changed sleepy wait msleep(10000); with busy wait:

    mdelay(5000); // busy-wait for 5 seconds
    schedule();
    mdelay(5000); // busy-wait for 5 seconds

High priority thread woke up after 7 seconds.

thread1 start time=0
thread2 start time=2
thread1 stop time=7
thread2 read:hello from demo_dev
thread2 stop time=12
main thread stop time=22
end test

The reason I suppose is because just voluntary preemption was configured:

CONFIG_PREEMPT_VOLUNTARY=y
#CONFIG_PREEMPT is not set

And process can't be preempted in kernel code unless it explicitly calls schedule() or blocks(sleeps)? I assume same results would be with other scheduling policy?

2

There are 2 answers

0
AudioBubble On BEST ANSWER

You have only voluntary preemption enabled, CONFIG_PREEMPT is not set, that means when kernel executed syscall Another higher priority process cant interrupt it on this same CPU when it becomes runable.

1
AudioBubble On

Forced Kernel preemption

In this configuration the context switch is done on time also in the kernel, means if we run the above test we will see the high priority thread waking up after 3 seconds:

It means that in this options the system will perform more context switches per second but it is more “real time”. On embedded systems with soft real time requirements it is a best practice to use this option but in a server system that we are usually work asynchronously the first option is better – less context switches – more CPU time