Init work linux kernel

Init work linux kernel

Выполнение функции в контексте прерывания должно происходить максимально быстро. Поэтому если требуется драйверу в момент прерывания выполнить какой-то большой объем работы, то имеет смысл поместить функцию в очередь задач (workqueue), для её отложенного выполнения. После помещения функции в очередь (schedule_work), она будет выполнена в ближайшее время в контексте ядра.

Пример модуля с вызовом отложенной функции. Структура создаётся статически на этапе компиляции.

/* * stat_queue.c - статическая workqueue */ #include <linux/module.h> #include <linux/workqueue.h> #define DRIVER_AUTHOR "noname author " #define DRIVER_DESC "static queue" /* функция, которую требуется вызвать в отложенном режиме */ static void my_function(struct work_struct *work) < printk("call function from queue\n"); >/* создание статической структуры на этапе компиляции * первый аргумент - имя для структуры struct work_struct, которая будет создана * второй аргумент - имя функции которую нужно отложенно выполнить */ static DECLARE_WORK(tst_queue_struct, my_function); /* функция, которая вызывается при загрузке модуля */ static int __init init_stat_queue(void) < printk(KERN_ALERT "Hello, static queue\n"); /* помещение функции в очередь задач ядра */ schedule_work(&tst_queue_struct); return 0; >static void __exit cleanup_stat_queue(void) < printk(KERN_ALERT "Goodbye, static queue\n"); >module_init(init_stat_queue); module_exit(cleanup_stat_queue); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC);

Makefile для сборки модуля:

obj-m += stat_queue.o all: make -C /usr/src/linux-headers-`uname -r` M=$(PWD) modules clean: rm -rf *.o *.ko

Структуру struct work_struct можно создавать динамически.

Пример отложенного выполнения функции (workqueue) с динамическим созданием структуры:

/* * dynam_queue.c - динамически отложенное действие */ #include <linux/module.h> #include <linux/workqueue.h> #include <linux/slab.h> #define DRIVER_AUTHOR "noname author " #define DRIVER_DESC "dynamic queue" /* функция, которую требуется вызвать в отложенном режиме */ static void my_function(struct work_struct *work) < printk("call function from queue\n"); >/* функция, которая вызывается при загрузке модуля */ static int __init init_dynam_queue(void) < printk(KERN_ALERT "Hello, dynamic queue\n"); /* указатель на структуру work_struct */ struct work_struct *tst_queue_struct; /* динамически выделяется область памяти под структуру */ tst_queue_struct = kmalloc( sizeof(struct work_struct), GFP_KERNEL ); /* динамически инициализируем структуру work_struct. * первый аргумент - имя для структуры struct work_struct для инициализации * второй аргумент - имя функции, которую нужно отложенно выполнить */ INIT_WORK(tst_queue_struct, my_function); /* помещение функции в очередь задач ядра */ schedule_work(tst_queue_struct); return 0; >static void __exit cleanup_dynam_queue(void) < printk(KERN_ALERT "Goodbye, dynamic queue\n"); >module_init(init_dynam_queue); module_exit(cleanup_dynam_queue); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC);

Если требуется передать какие-либо данные в отложенную функцию, то нужно создать свою структуру, где обязательным членом должно быть поле struct work_struct.
А затем уже в отложенной функции через макрос container_of получить доступ к
своим данным.

Читайте также:  Linux исполняемый файл формат

Пример использования отложенной функции (workqueue) с передачей параметров

/* * dynam_queue_2.c - динамически отложенное действие, * с передачей параметров в отложенную функцию * */ #include <linux/module.h> #include <linux/workqueue.h> #include <linux/slab.h> #define DRIVER_AUTHOR "noname author " #define DRIVER_DESC "dynamic queue with param" struct queue_data_struct < struct work_struct queue_work; /* необходимая структура */ int data; /* данные для передачи в функцию */ >; /* функция, которую требуется вызвать в отложенном режиме */ static void my_function(struct work_struct *work)< printk("call function from queue\n"); struct queue_data_struct *tst_queue_struct = container_of(work, struct queue_data_struct, queue_work); printk( "function param: data=%d\n", tst_queue_struct->data ); kfree(tst_queue_struct); > /* функция, которая вызывается при загрузке модуля */ static int __init init_dynam_queue(void)< printk(KERN_ALERT "Hello, dynamic queue\n"); /* указатель на структуру work_struct */ struct queue_data_struct *tst_queue_struct; /* динамически выделяется область памяти под структуру */ tst_queue_struct = kmalloc( sizeof(struct queue_data_struct), GFP_KERNEL ); /* динамически инициализируем структуру work_struct. * первый аргумент - имя для структуры struct work_struct для инициализации * второй аргумент - имя функции которую нужно отложенно выполнить */ INIT_WORK(&(tst_queue_struct->queue_work), my_function); /* данные для передачи в отложенную функцию */ tst_queue_struct->data = 99; /* помещение функции в очередь задач ядра */ schedule_work(&(tst_queue_struct->queue_work)); return 0; > static void __exit cleanup_dynam_queue(void) < printk(KERN_ALERT "Goodbye, dynamic queue\n"); >module_init(init_dynam_queue); module_exit(cleanup_dynam_queue); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC);

Источник

Init work linux kernel

Linux Init_Work creates a work queue
1. Add a job using the work queue shared by the system
1. Declare or write a work handler
void my_func();
2. Create a work structure variable and assign the address of the handset and the entry address of the parameter to this work structure variable
DECLARE_WORK (MY_WORK, MY_FUNC, & DATA); -> Create a structural variable named MY_WORK and assign the function portal address and parameter address to it;

 If you don't want to create and initialize the working structure variables when you compile, you can also create: Init_Work () when you run: Structwork_struct my_work; -> Create a structural variable called my_Work, can only use init_work () INIT_WORK (& MY_WORK, MY_FUNC, & DATA); -> Initialize the MY_WORK that has been created, is actually adding the entry address of the processing function to this structural variable, and the address of the DATA is usually completed in the driver. 

3. Add the work structure variable to the system sharing work queue
schedule_work; -> Remove from the queue after the completion of the queue

2. Create your own work queue to add work
1. Declare the work handler and a pointer to the work queue
void my_func();
struct workqueue_struct *p_queue;
2. Create your own work queue and work structure variable (usually completed in the Open function)
p_queue = create_workQueue («my_queue»); -> Create a work queue named my_queue and assign the entry address of the work queue to the declared pointer
struct work_struct my_work;
INIT_WORK(&my_work, my_func, &data);
3. Add the work to the work queue created by yourself waiting to be executed
queue_work (p_queue, & my_work); -> The role is similar to Schedule_Work (), and the difference is the work queue to add the work to the P_Queue pointer instead of the system shared.
4. Delete your work queue
destroy_workQueue (p_queue); -> Generally deleted in a close function

Читайте также:  Syslog windows to linux

About the problem of work queues (personal understanding)
First, the work queue is a single-thread, the event made, has no significant impact on other parts of the system.
The work queue added by the program is the system sharing work queue, which is used by this work queue (to verify the LED flashing mode in the failed Aqua project as an example)
1. Separate a work handler function:
static void gpio_led_flash_work(struct work_struct *work)
2. Create a work structure variable in the program total data structure
struct work_struct flash_work;
3. Initialize a created work body variable:
INIT_WORK(&led_dat->flash_work, gpio_led_flash_work);
4. Add the work structure variable to the system sharing work queue in the place where you need to call to the work processing function:
schedule_work(&led_dat->flash_work);

Источник

Linux Kernel Interview questions

There are many cases where an asynchronous process execution context is needed and the work queue (wq) API is the most commonly used mechanism for such cases. Some of the cases as bottom half for interrupt handler, some time a task scheduled for specific time etc.,

When such an asynchronous execution context is needed, a work item describing which function to execute is put on a queue. An independent thread serves as the asynchronous execution context. The queue is called work queue and the thread is called worker. While there are work items on the work queue the worker executes the functions associated with the work items one after the other. When there is no work item left on the work queue the worker becomes idle.

Note: All the work items are schedulable, run in process context and can sleep.

Читайте также:  Установка golang kali linux

How work queues are implemented based on version (3.11.0)—> (Need to update the line numbers once again after releasing 3.11)

1) Initilization of workqueue happens in the kernel/workqueue.c

_init init_workqueues(void) >
early_initcall(init_workqueues);

2) Initilizes the notifiers to take care of cpu up and down

cpu_notifier(workqueue_cpu_up_callback, CPU_PRI_WORKQUEUE_UP);
hotcpu_notifier(workqueue_cpu_down_callback, CPU_PRI_WORKQUEUE_DOWN);

3)

4955 /* initialize CPU pools */
4956 for_each_possible_cpu(cpu) <
.
.
4960 for_each_cpu_worker_pool(pool, cpu) 4961 BUG_ON(init_worker_pool(pool));
.
.
4974 /* create the initial worker */
4975 for_each_online_cpu(cpu) <
Initializes 5 work queues as described below.System-wide workqueues which are always present

system_wq is the one used by schedule[_delayed]_work[_on](). Multi-CPU multi-threaded. There are users which expect relatively short queue flush time. Don’t queue works which can run for too long.

system_long_wq is similar to system_wq but may host long running works. Queue flushing might take relatively long.

system_unbound_wq is unbound workqueue. Workers are not bound to any specific CPU, not concurrency managed, and all queued works are executed immediately as long as max_active limit is not reached and resources are available.

struct workqueue_struct *system_wq __read_mostly;
struct workqueue_struct *system_highpri_wq __read_mostly;
struct workqueue_struct *system_long_wq __read_mostly;
struct workqueue_struct *system_unbound_wq __read_mostly;
struct workqueue_struct *system_freezable_wq __read_mostly;

rescuer_thread — the rescuer thread function

Workqueue rescuer thread function. There’s one rescuer for each workqueue which has WQ_MEM_RECLAIM set. Regular work processing on a pool may block trying to create a new worker which uses GFP_KERNEL allocation which has slight chance of developing into deadlock if some works currently on the same queue need to be processed to satisfy the GFP_KERNEL allocation. This is the problem rescuer solves. When such condition is possible, the pool summons rescuers of all workqueues which have works queued on the pool and let them process those works so that forward progress can be guaranteed. This should happen rarely.

How to use workqueues

destroy_work_on_stack
delayed_work_timer_fn
queue_delayed_work_on
mod_delayed_work_on
drain_workqueue

__alloc_workqueue_key
destroy_workqueue
workqueue_set_max_active
workqueue_congested
work_busy
work_on_cpu

Initialization macros.

INIT_DELAYED_WORK( work, func );
INIT_DELAYED_WORK_DEFERRABLE( work, func );

INIT_WORK_ONSTACK
__INIT_DELAYED_WORK_ONSTACK

Difference between TASKLETS and Work Queues

Tasklets provide a low-latency mechanism that is simple and straightforward, while work queues provide a flexible API that permits queuing of multiple work items. Each defers work from the interrupt context, but only tasklets run atomically in a run-to-complete fashion, where work queues permit handlers to sleep, if necessary. Either method is useful for work deferral, so the method selected is based on your particular needs.

Источник

Оцените статью
Adblock
detector