Linux completion

一、completion适用场景

Completion, the name itself says. When we want to notify or wake up some thread or something when we finished some work, then we can use completion. We’ll take one situation. We want to wait for one thread for something to run. Until that time that thread has to sleep. Once that process finished then we need to wake up that thread that is sleeping. We can do this by using completion without race conditions.

These completions are a synchronization mechanism which is a good method in the above situation mentioned rather than using improper locks/semaphores and busy-loops.

completions是一个类似信号量的东西,用于内核线程间的同步。

There are 5 important steps in Completions.

  1. Initializing Completion in Linux
  2. Re-Initializing Completion in Linux
  3. Waiting for completion (The code is waiting and sleeping for something to finish)
  4. Waking Up Task (Sending a signal to sleeping part)
  5. Check the status

二、completion核心数结构

struct completion { 
    unsigned int done; 
    wait_queue_head_t wait; 
};

三、completion API

1. 初始化completion:

         静态:DECLARE_COMPLETION(data_read_done);

   动态:init_completion (struct completion * x);

struct completion data_read_done;
init_completion(&data_read_done);

2.Re-Initializing Completion

        reinit_completion (struct completion * x);

This function should be used to reinitialize a completion structure so it can be reused. This is especially important after complete_all is used. This simply resets the ->done field to 0 (“not done”), without touching the waitqueue. Callers of this function must make sure that there are no racy wait_for_completion() calls going on in parallel.

3.Waiting for completion

        void wait_for_completion (struct completion * x);

Note that wait_for_completion() is calling spin_lock_irq()/spin_unlock_irq(), so it can only be called safely when you know that interrupts are enabled. Calling it from IRQs-off atomic contexts will result in hard-to-detect spurious enabling of interrupts.

4.wait_for_completion_timeout

  unsigned long wait_for_completion_timeout (struct completion * x, unsigned long timeout);—— It is not interruptible.

It returns if timed out, and positive (at least 1, or the number of jiffies left till timeout) if completed.

5.wait_for_completion_interruptible

        int wait_for_completion_interruptible (struct completion * x); —— It returns -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1, or a number of jiffies left till timeout) if completed.

6.wait_for_completion_killable

        int wait_for_completion_killable (struct completion * x); —— This waits to be signaled for completion of a specific task. It returns -ERESTARTSYS if interrupted, 0 if completed.

7.wait_for_completion_killable_timeout

        long wait_for_completion_killable_timeout (struct completion * x, unsigned long timeout); —— It returns -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1, or a number of jiffies left till timeout) if completed.

8.try_wait_for_completion

        bool try_wait_for_completion (struct completion * x); —— This function will not put the thread on the wait queue but rather returns false if it would need to en queue (block) the thread, else it consumes one posted completion and returns true.

This try_wait_for_completion() is safe to be called in IRQ or atomic context. —— 能快速完成并退出

9.complete

        void complete (struct completion * x); —— This will wake up a single thread waiting on this completion. Threads will be awakened in the same order in which they were queued.

10.complete_all

        bool completion_done (struct completion * x);

11.completion_done

        bool completion_done (struct completion * x);

It returns 0 if there are waiters (wait_for_completion in progress) 1 if there are no waiters.

This completion_done() is safe to be called in IRQ or atomic context.

四、Driver Source Code – Completion in Linux

/***************************************************************************//**
*  \file       driver.c
*
*  \details    Simple linux driver (Completion Static method)
*
*  \author     EmbeTronicX
*
*  \Tested with Linux raspberrypi 5.10.27-v7l-embetronicx-custom+
*
*******************************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>                 //kmalloc()
#include <linux/uaccess.h>              //copy_to/from_user()
 
#include <linux/kthread.h>
#include <linux/completion.h>           // Required for the completion
 
 
uint32_t read_count = 0;
static struct task_struct *wait_thread;
 
DECLARE_COMPLETION(data_read_done);
 
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
int completion_flag = 0;
 
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
 
/*************** Driver Functions **********************/
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, 
                                char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp, 
                                const char *buf, size_t len, loff_t * off);
/******************************************************/

//File operation structure
static struct file_operations fops =
{
        .owner          = THIS_MODULE,
        .read           = etx_read,
        .write          = etx_write,
        .open           = etx_open,
        .release        = etx_release,
};

/*
** Waitqueue thread
*/ 
static int wait_function(void *unused)
{
        
        while(1) {
                pr_info("Waiting For Event...\n");
                wait_for_completion (&data_read_done);
                if(completion_flag == 2) {
                        pr_info("Event Came From Exit Function\n");
                        return 0;
                }
                pr_info("Event Came From Read Function - %d\n", ++read_count);
                completion_flag = 0;
        }
        do_exit(0);
        return 0;
}

/*
** This function will be called when we open the Device file
*/ 
static int etx_open(struct inode *inode, struct file *file)
{
        pr_info("Device File Opened...!!!\n");
        return 0;
}

/*
** This function will be called when we close the Device file
*/ 
static int etx_release(struct inode *inode, struct file *file)
{
        pr_info("Device File Closed...!!!\n");
        return 0;
}

/*
** This function will be called when we read the Device file
*/
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
        pr_info("Read Function\n");
        completion_flag = 1;
        if(!completion_done (&data_read_done)) {
            complete (&data_read_done);
        }
        return 0;
}

/*
** This function will be called when we write the Device file
*/
static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
        pr_info("Write function\n");
        return len;
}

/*
** Module Init function
*/
static int __init etx_driver_init(void)
{
        /*Allocating Major number*/
        if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
                pr_err("Cannot allocate major number\n");
                return -1;
        }
        pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
 
        /*Creating cdev structure*/
        cdev_init(&etx_cdev,&fops);
        etx_cdev.owner = THIS_MODULE;
        etx_cdev.ops = &fops;
 
        /*Adding character device to the system*/
        if((cdev_add(&etx_cdev,dev,1)) < 0){
            pr_err("Cannot add the device to the system\n");
            goto r_class;
        }
 
        /*Creating struct class*/
        if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
            pr_err("Cannot create the struct class\n");
            goto r_class;
        }
 
        /*Creating device*/
        if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
            pr_err("Cannot create the Device 1\n");
            goto r_device;
        }
 
        //Create the kernel thread with name 'mythread'
        wait_thread = kthread_create(wait_function, NULL, "WaitThread");
        if (wait_thread) {
                pr_info("Thread Created successfully\n");
                wake_up_process(wait_thread);
        } else
                pr_err("Thread creation failed\n");
 
        pr_info("Device Driver Insert...Done!!!\n");
        return 0;
 
r_device:
        class_destroy(dev_class);
r_class:
        unregister_chrdev_region(dev,1);
        return -1;
}

/*
** Module exit function
*/ 
static void __exit etx_driver_exit(void)
{
        completion_flag = 2;
        if(!completion_done (&data_read_done)) {
            complete (&data_read_done);
        }
        device_destroy(dev_class,dev);
        class_destroy(dev_class);
        cdev_del(&etx_cdev);
        unregister_chrdev_region(dev, 1);
        pr_info("Device Driver Remove...Done!!!\n");
}
 
module_init(etx_driver_init);
module_exit(etx_driver_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("A simple device driver - Completion (Static Method)");
MODULE_VERSION("1.23");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

denglin12315

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值