一、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.
- Initializing Completion in Linux
- Re-Initializing Completion in Linux
- Waiting for completion (The code is waiting and sleeping for something to finish)
- Waking Up Task (Sending a signal to sleeping part)
- 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 0 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");