Wednesday, 5 June 2013

KERNEL THREAD SYNCHRONIZATION WITH SEMAPHORE

// Kernel thread synchronization with semaphore
// Theory:
//Two threads can be synchornized by semaphore, blocked threads are pushed //into semaphore queue
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/delay.h>

MODULE_LICENSE("Dual BSD/GPL");


struct kthr_data
{
    const char *name; //kthread name
    struct semaphore *sem1;
    struct semaphore *sem2;
};

static struct kthr_data dking, dqueen;
static struct semaphore kingsem, queensem;
static struct task_struct *tking, *tqueen;

/* our case:
   down dking sem1 decrements to make it (1->0)zero and runs the thread tking unlocked to locked state //king works
   up dking sem2 increments to make it (0->1) unlock because sem2 waitlist is still empty
   down dqueen sem1 decrements to make it (1->0)zero and runs the thread tking unlocked to locked state //queen works
   up dqueen sem2 increments to make it (0->1) unlock

*/
int kthread_function(void *data)
{
    struct kthr_data *pdata = (struct kthr_data*)data;
    while(1)
    {
        //down operation decrements the counter, if the count is 0 then down operation over the semaphore
        //1. blocks the calling kernel thread
        //2. Insert it into task structure to the queue of the semaphore
        //3. schedule another task
        down_interruptible(pdata->sem1); //uninterruptible sleep in sem->wait list when sem1 is zero
        printk("%s\n", pdata->name);
        mdelay(499);
        msleep(1);
        up(pdata->sem2);
        //if the semaphore wait queue is not empty then it pick a task and make it runnable else increment the counter
        //if sem->wait list is empty increment sem->count and leave if sem->count is 0 remove the first waiter structure from the sem queue
        if(kthread_should_stop())
            break;
    }
    return 0;
}

struct task_struct *ts;

static int __init kthr_init(void)
{
    printk("kthread init called");
    //semaphore is an object consists of a counter and a queue of waiting tasks

    sema_init(&kingsem, 1); //unlocked state
    sema_init(&queensem, 0); //locked state
    dking.name = "king";
    dqueen.name = "queen";
    dking.sem1 = &kingsem;
    dking.sem2 = &queensem;
    dqueen.sem1 = &queensem;
    dqueen.sem2 = &kingsem;

    tking = kthread_run(kthread_function, &dking, "king");
    tqueen = kthread_run(kthread_function, &dqueen, "queen");
    return 0;
}

void __exit kthr_exit(void)
{
    printk("tking_stop called");
    kthread_stop(tking);
    printk("tqueen_stop called");
    kthread_stop(tqueen);
}

module_init(kthr_init);
module_exit(kthr_exit);
//output:
//king queen king queen ....
//why we synchronize to know this just comment down and up lines and see there //will not be any ordered synchronized print statements
 

LINUX KERNEL THREAD SYNCHRONIZATION WITH WAITQUEUE


// Kernel thread synchronization with wait queue
/*
This code post is useful to learn the synchronization behavior of kernel thread with respect to synchronization 

Theory: 

Threads synchronization is based on event, 2 threads use completion event for synchronization and unblock the other thread

Blocked thread waiting for an event waits in a wait queue. when it receives the event it is eligible by the scheduler to run

Task in blocked state will expect some condition to be true (non zero)

wait queues in linux are defined by wait queue header which is a list_head node (Double linked list node) linked to wait_queue_t nodes which holds a function pointer to the task

wait_event_interruptible puts the task into waitqueue

wake_up_interruptible wakes the task on wakeup event


Code Explanation:

Initialization creates 2 instances of kernel thread (tone, tzero)with same kernel function (kthread function)
The kernel threads tone and tzero should alternatively display their names one and zero
Thread parameter structure is assocaited with each thread instance kthread_data
Thread parameters are
1. name
2. wait queue to synchronize the two threads
3. condition variable used with the wait queue
4. Pointer to other thread's parameter

kthread_function?

conditionally block on a wait queue using wait_event_interruptible

output the name
wait for the event in wait_queue
unblock the other thread using wake_up_interruptible

tone is the first thread with condition variable to true (1 - non-zero)
tzero thread with condition variable initialized to false (0 - zero)



*/

 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>

MODULE_LICENSE("Dual BSD/GPL");

struct kthr_data {
const char *name;
//wq<--wqh-->wq
wait_queue_head_t thrdwq; // kernel thread waits on this queue //wait_queue_head_t is the list head other nodes are wait_queue_t //nodes which holds the task (function pointer)
int condition;
struct kthr_data *datalink;
};

static struct kthr_data done, dzero;


int kthread_function(void* data)
{
struct kthr_data *pdata = (struct kthr_data*)data; // this data depends on the kernel thread running "one"/"zero"
while(1)
{
printk("wait(%s)", pdata->name);
wait_event_interruptible(pdata->thrdwq, pdata->condition);
pdata->condition = 0;
printk("%s\n",pdata->name);
mdelay(500);
msleep(1);
pdata->datalink->condition = 1;
printk("wakeup (%s)\n", pdata->name);
wake_up_interruptible(&pdata->datalink->thrdwq);//wake up the //task waiting in queue
if(kthread_should_stop())
break;
}
return 0;

}



struct task_struct *tone, *tzero;

int __init kthread_init(void)
{
printk("\nkthread init");
init_waitqueue_head(&done.thrdwq);
init_waitqueue_head(&dzero.thrdwq);
done.condition = 1;
dzero.condition = 0;
done.name = "one";
dzero.name = "zero";
done.datalink = &dzero;
dzero.datalink = &done;

tone = kthread_run(kthread_function, &done, "one");
tzero = kthread_run(kthread_function, &dzero, "zero");
return 0;
}

void __exit kthread_exit(void)
{
printk("\nkernel exit thread");
kthread_stop(tone);
kthread_stop(tzero);
}

module_init(kthread_init);

module_exit(kthread_exit);

//output: one zero one zero ...