About workqueues.

Performance of function in the context of interruption has to happen as fast as possible. Therefore if it is required to the driver to execute some large volume of work at the time of interruption that makes sense to place function in the task queue (workqueue), for its deferred execution. After putting the functions in the queue (schedule_work), it will be implemented in the kernel.

An example of a module with a calling of the deferable function. The structure is created statically at compile time.

/*
 *  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 for module building:

obj-m += stat_queue.o

all:
                make -C /usr/src/linux-headers-`uname -r` M=$(PWD) modules

clean:
                rm -rf *.o *.ko

The structure struct work_struct can be created dymamically.

An example of the delayed execution of the function (workqueue) with the dynamic structure creating:

/*
 *  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);

If we want to transfer any data in the deferred function, then we need to create a struct with a required member field struct work_struct.
And then in the deffered function using the macro container_of to access
their data.

An example of the deferable function (workqueue) with the transfer of parameters

/*  
 *  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);