Completion (kernel-4.7) __linux in Linux device drivers

Source: Internet
Author: User
Tags semaphore thread stop
Completion Definition

Defined directory: Include/linux/completion.h

*
 * struct completion-structure used to maintain state for a ' completion ' * * This is the
 opaque structure Used to maintain the state for a "completion".
 * Completions currently use a FIFO to queue threads this have to wait for
 * "Completion" event.
 *
 * ALSO:  complete (), Wait_for_completion () (and Friends _timeout,
 * _interruptible, _interruptible_ Timeout, and _killable), Init_completion (),
 * reinit_completion (), and Macros Declare_completion (),
 * Declare_completion_onstack ().
 */
struct Completion {
    unsigned int done;//Indicates whether the waiting event is complete. 0 when initialized. A 0 indicates that the waiting event has not been completed. Greater than 0 indicates that the waiting event has been completed.
    wait_queue_head_t wait;//holds the queue of processes waiting for the event to complete.
};
Completion function

Although semaphores can be used to achieve synchronization, there are often some bad results that may occur. For example, when process a assigns a temporary semaphore variable, initializes it to a closed mutex, passes its address to process B, and then calls down () on a, and process a intends to undo the semaphore once it is awakened. Subsequently, process B running on different CPUs calls up () on the same semaphore. However, the current implementation of up () and down () also allows the two functions to be concurrent on the same semaphore. Therefore, process a can wake up and undo the temporary semaphore, while process B is still running the up () function. The resulting up () may attempt to access a nonexistent data structure. This will cause an error. To prevent this error, the completion mechanism is specifically designed for synchronization. Completion Operation

Define and Initialize:

struct completion completion;
Init_completion (&completion);

Direct definition and invocation of init_completion () initialization. Init_completion () initializes the done field to the 0,wait field with a spin lock that is not locked and waits for the queue to be empty. This means that the process that calls the completion must wait for an event to complete (that is, the other process must first invoke COMPLETIOM () to wake the amount of completion).

/**
 * Init_completion-initialize A dynamically allocated completion
 * @x:  pointer to completion structure TH At are to are initialized
 *
 * This inline function would initialize a dynamically created completion
 * structure.
 *
/static inline void init_completion (struct completion *x)
{
    x->done = 0;
    Init_waitqueue_head (&x->wait);
}
Declare_completion (completion);

Define and initialize the completion completion directly, the effect is equivalent to the above definition.

/**
 * Declare_completion-declare and initialize a completion structure
 * @work:  identifier for the Comple tion Structure
 *
 * This macro declares and initializes a completion structure. Generally used
 * for static declarations. You are should use the _onstack variant for automatic
 * variables.
 * *
#define DECLARE_COMPLETION (work) \
    struct Completion work = Completion_initializer (work)

wait for the completion amount :
The Wait_for_completion () function, defined in KERNEL/SCHED/COMPLETION.C, is implemented primarily by invoking the Do_wait_for_common function, which reads as follows:

Static inline long __sched
do_wait_for_common (struct completion *x,
           Long (*action) (long), long timeout, int State)
{
    if (!x->done) {
        declare_waitqueue (wait, current);

        __add_wait_queue_tail_exclusive (&x->wait, &wait);
        Do {
            if (signal_pending_state) {
                timeout =-erestartsys;
                break;
            __set_current_state (state);
            SPIN_UNLOCK_IRQ (&x->wait.lock);
            Timeout = action (timeout);
            SPIN_LOCK_IRQ (&x->wait.lock);
        } while (!x->done && timeout);
        __remove_wait_queue (&x->wait, &wait);
        if (!x->done) return
            timeout
    }
    x->done--;
    Return timeout?: 1;
}

This function corresponds to the down () operation in the semaphore. However, in the operation of the use of its own spin lock. If done is 0, the waiting event is not completed, then the call to Declare_waitqueue () defines wait queue waiting and adds the current process to the wait queue. Then add wait to the end of the waiting queue for the completion, and enter the loop. Sets the current process to be interruptible (task_uninterruptible), releasing the spin lock and letting the current process go to sleep. Once the process is scheduled to wake up and get a spin lock and see if the waiting event is complete. If completed (greater than 0), the waiting process is removed from the waiting queue of the completion, and the spin lock is freed from the done, and the spin locks are released, returning from the wait state to continue running. Otherwise continue to sleep. If done in large 0, then the waiting event has been completed, and then the self reduction, and return directly

The Wait_for_completion_timeout () function, defined in KERNEL/SCHED/COMPLETION.C, is implemented primarily by calling the Do_wait_for_common function, and is also waiting for completion. The biggest difference from wait_for_completion () is that it returns when it waits for a time-out. This means that if the completion amount has not been awakened after a given time, it is returned directly. The greatest benefit is that the process has not had to wait for an event for a certain amount of time, so it can be awakened directly to continue executing

Wait_for_completion_interruptible () function that defines the KERNEL/SCHED/COMPLETION.C

Wait_for_completion_interruptible_timeout () function, which is defined in KERNEL/SCHED/COMPLETION.C

amount of Wake completion
Complete () function that wakes up a process that waits for the completion. The function is defined in KERNEL/SCHED/COMPLETION.C and reads as follows:

/**
 * Complete:-Signals a single thread waiting on this completion
 * @x: Holds the state of this  particular Completion
 *
 * This'll wake up a single thread waiting in this completion. Threads would be * awakened in the same order in which they were
 .
 *
 * also Complete_all (), wait_for_completion () and related routines.
 *
 * It May is assumed that this function implies a write memory barrier before
 * Changing the task state if and Only if any tasks are woken up.
 *
/void complete (struct completion *x)
{
    unsigned long flags;

    Spin_lock_irqsave (&x->wait.lock, flags);
    x->done++;
    __wake_up_locked (&x->wait, Task_normal, 1);
    Spin_unlock_irqrestore (&x->wait.lock, flags);
}

Complete_all () function, which wakes up all processes waiting for the completion amount. The function is defined in KERNEL/SCHED/COMPLETION.C and reads as follows:

/**
 * Complete_all:-Signals all threads waiting on this completion
 * @x: Holds the state of this  particular Completion
 * * This would wake up all
 threads waiting to this particular completion event.
 *
 * It May is assumed that this function implies a write memory barrier before
 * Changing the task state if and on ly if any tasks are woken up.
 *
/void Complete_all (struct completion *x)
{
    unsigned long flags;

    Spin_lock_irqsave (&x->wait.lock, flags);
    X->done + = UINT_MAX/2;
    __wake_up_locked (&x->wait, task_normal, 0);
    Spin_unlock_irqrestore (&x->wait.lock, flags);
}

Example program:

#include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/
kernel.h> #include <linux/jiffies.h> #include <linux/delay.h>//These three header files are related to the use of kernel threads; #include <linux/sched.h> #include <linux/kthread.h> #include <linux/err.h>//completion related #include <
Linux/completion.h> module_license ("GPL");
Module_author ("*************");

Module_version ("4.7.0.000");
static int sleep_time = (1*10*hz);

static int shared_res = 0;

STEP1: Define the completion quantity struct completion my_comp;

  STEP5: Implementing the thread function static int thread_process1 (void* param) {//int val = 0, ret = 0;

    while (1) {set_current_state (task_uninterruptible); if (Kthread_should_stop ()) {PRINTK ("Kernel thread '%s ' should stop;file:%s;line:%d\n", __function__, __file__, _
      _LINE__);
    Break
    //STEP3: Lock Wait_for_completion (&my_comp) on critical area;
    shared_res++;

    STEP4: Unlock complete (&AMP;MY_COMP) to critical zone;
  Mdelay (Sleep_time); } RetuRN 12;

};

  static int thread_process2 (void* param) {//int val = 0, ret = 0;

    while (1) {set_current_state (task_uninterruptible); if (Kthread_should_stop ()) {PRINTK ("Kernel thread '%s ' should stop;file:%s;line:%d\n", __function__, __file__, _
      _LINE__);
    Break
    //STEP3: Lock Wait_for_completion (&my_comp) on critical area;
    shared_res++;

    STEP4: Unlock complete (&AMP;MY_COMP) to critical zone;
  Msleep (Sleep_time);
Return 34;

};

  static int thread_process3 (void* param) {int val = 0;//, ret = 0;

    while (1) {set_current_state (task_uninterruptible); if (Kthread_should_stop ()) {PRINTK ("Kernel thread '%s ' should stop;file:%s;line:%d\n", __function__, __file__, _
      _LINE__);
    Break
    //STEP3: Lock Wait_for_completion (&my_comp) on critical area;
    val = shared_res; PRINTK ("%s:shared resource =%d;\n%s", __function__, Val, (val% 3)?
    "": "\ n"));

    STEP4: Unlock complete (&AMP;MY_COMP) to critical zone;
  Msleep (Sleep_time);
  }return 56;

};

  static int thread_process4 (void* param) {int val = 0;//, ret = 0;

    while (1) {set_current_state (task_uninterruptible); if (Kthread_should_stop ()) {PRINTK ("Kernel thread '%s ' should stop;file:%s;line:%d\n", __function__, __file__, _
      _LINE__);
    Break
    //STEP3: Lock Wait_for_completion (&my_comp) on critical area;
    val = shared_res; PRINTK ("%s:shared resource =%d;\n%s", __function__, Val, (val% 3)?
    "": "\ n"));

    STEP4: Unlock complete (&AMP;MY_COMP) to critical zone;
  Msleep (Sleep_time);
return 78;

};
static struct task_struct* my_thread1 = NULL;
static struct task_struct* my_thread2 = NULL;
static struct task_struct* my_thread3 = NULL;

static struct task_struct* my_thread4 = NULL;
 static int __init study_init (void) {int err = 0;

 PRINTK ("%s\n", __pretty_function__);
 STEP2: Initialization Completion amount init_completion (&my_comp);
 PRINTK ("Init completion ok\n");

 Complete_all (&my_comp); My_thread1 = Kthread_create (Thread_process1, NULL, "My_thread1");
   if (Is_err (My_thread1)) {ERR = Ptr_err (MY_THREAD1);
   My_thread1 = NULL;
   PRINTK (Kern_err "Unable to start kernel thread1:%d\n", ERR);
 return err;
 } my_thread2 = Kthread_create (Thread_process2, NULL, "my_thread2");
   if (Is_err (my_thread2)) {ERR = Ptr_err (my_thread2);
   My_thread2 = NULL;
   PRINTK (Kern_err "Unable to start kernel thread2:%d\n", ERR);
 return err;
 } my_thread3 = Kthread_create (THREAD_PROCESS3, NULL, "my_thread3");
   if (Is_err (my_thread3)) {ERR = Ptr_err (MY_THREAD3);
   My_thread3 = NULL;
   PRINTK (Kern_err "Unable to start kernel thread3:%d\n", ERR);
 return err;
 } my_thread4 = Kthread_create (THREAD_PROCESS4, NULL, "my_thread4");
   if (Is_err (MY_THREAD4)) {ERR = Ptr_err (MY_THREAD4);
   My_thread4 = NULL;
   PRINTK (Kern_err "Unable to start kernel thread4:%d\n", ERR);
 return err;
 } wake_up_process (MY_THREAD1);
 Wake_up_process (MY_THREAD2);
 Wake_up_process (MY_THREAD3);
 Wake_up_process (MY_THREAD4); PRINTK ("%s:alL kernel thread start;\n ", __function__);
return 0;
 } static void __exit study_exit (void) {int ret =-1;

 PRINTK ("%s\n", __pretty_function__);
 Complete_all (&my_comp);

 Complete (&AMP;MY_COMP);
   if (my_thread1) {ret = Kthread_stop (MY_THREAD1);
   My_thread1 = NULL;
 PRINTK ("Kernel thread1 stop,exit code is%d;\n", ret);
 }//complete_all (&my_comp);

 Complete (&AMP;MY_COMP);
   if (my_thread2) {ret = Kthread_stop (my_thread2);
   My_thread2 = NULL;
 PRINTK ("Kernel thread2 stop,exit code is%d;\n", ret);
 }//complete_all (&my_comp);

 Complete (&AMP;MY_COMP);
   if (my_thread3) {ret = Kthread_stop (MY_THREAD3);
   My_thread3 = NULL;
 PRINTK ("Kernel thread3 stop,exit code is%d;\n", ret);
 }//complete_all (&my_comp);

 Complete (&AMP;MY_COMP);
   if (my_thread4) {ret = Kthread_stop (MY_THREAD4);
   My_thread4 = NULL;
 PRINTK ("Kernel thread4 stop,exit code is%d;\n", ret);
} printk ("%s:all kernel thread stop;\n", __function__); } module_init (study_iNIT); Module_exit (Study_exit);

To be continued ....

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.