Linux内核驱动程序:完成'完成'什么时候删除设备

时间:2016-04-25 21:30:57

标签: linux linux-kernel linux-device-driver kernel-module

我正在编写内核驱动程序以使用PCI Express设备发送/接收数据。对于驱动程序的第一个版本,我创建了一个字符设备接口,用户可以使用文件读取数据。

背景

我想实现阻塞读取,其中用户请求数据并且驱动程序填充用户缓冲区。为了屏蔽用户的read电话,我使用的是completion结构。

当加载驱动程序并且用户请求读取时,驱动程序会按预期阻塞。如果我要完成阅读,那么一切都运行正常。

问题

为了安全起见,无论何时移除模块,我都会调用complete_all函数,以防有人在读取事务中删除模块或设备。

调用removeexit函数并且模块和用户应用程序都被阻止。我已经尝试了以下三个功能(显示了相关结果)。

  1. wait_completion(&dev->read_complete); //无限期阻止,我需要重置计算机
  2. retval = wait_for_completion_interruptible(&dev->read_complete); //我可以手动终止用户应用程序,然后删除驱动程序
  3. retval = wait_for_completion_killable(&dev->read_complete); //与可中断相同
  4. 我的期望是,当调用remove函数时,我可以调用complete_all(&dev->read_complete)并且read函数将返回错误。

    为了消除外部因素我已经在github上做了一个回购,所以如果有人想看到自己的行为,他们只需要克隆并按照说明操作:

    Kernel Module Completion Test

    模块的相关部分在这里(/src/mymodule.c)

    typedef struct {
      struct cdev cdv;
      struct class *cls;
      struct device *dev;
      struct completion complete;
    } mymodule_t;
    mymodule_t mymod;
    
    
    //Sysfs 'mymodule_test' attribute (all but the actual function is left out for brevity)
    
    static ssize_t mymodule_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
    {
      int retval = 0;
      int value = 0;
      if (sscanf(buf, "%d", &value) == 1)
      {
        retval = strlen(buf);
      }
      if (value)
      {
        printk("Value is: %d\n", value);
        if (!completion_done(&mymod.complete))
        {
          complete(&mymod.complete);
        }
        printk("Sent Completion\n");
      }
    
      return retval;
    }
    
    //FOPS (all but 'read' function is left out for brevity)
    
    ssize_t mymodule_read(struct file *filp, char * buf, size_t count, loff_t *f_pos)
    {
      printk("Read!\n");
      if (completion_done(&mymod.complete))
      {
        reinit_completion(&mymod.complete);
      }
    
      printk("Wait for Completion\n");
      wait_for_completion_interruptible(&mymod.complete);
      printk("After Completion\n");
      return 0;
    }
    
    
    static int __init mymodule_init(void)
    {
      ...
      //Register class and device
      //Configure character driver with fops
      init_completion(&mymod.complete);
      ...
    }
    static void __exit mymodule_exit(void)
    {
      ...
      if (!completion_done(&mymod.complete))
      {
        printk("Send a completion!\n");
        complete(&mymod.complete);
      }
      //Clean up the rest of the module
      ...
    }
    module_init(mymodule_init);
    module_exit(mymodule_exit);
    

    以下是我用来执行此操作的用户态应用程序:

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <termios.h>
    
    #include "mymodule.h"
    
    
    #define FILEPATH "/dev/mymodule0"
    
    #define TEST_SIZE 10
    
    int main(void)
    {
      int fn = -1;
      char buf[TEST_SIZE];
      printf("Attempting to open file module file...\n");
      fn = open(FILEPATH, O_RDWR);
      if (fn < 0)
      {
        printf("Failed to open file!\n");
        return -1;
      }
      printf("Attempting to read from the file...\n");
      read(fn, &buf, TEST_SIZE);
      printf("Finished reading from file\n");
      return 0;
    }
    

    这是我

    时的dmesg输出
    1. 加载模块
    2. 运行用户应用程序(它打开文件,尝试读取10个字符,然后退出)
    3. 写&#39; 1&#39;到sysfs属性
    4. 卸载模块
    5. [3217633.993937] Registering Driver
      [3217633.993995] Driver Initialized!
      [3217643.747791] Opened!
      [3217643.747800] Read!
      [3217643.747801] Wait for Completion
      [3217646.436780] Value is: 1
      [3217646.436792] Sent Completion
      [3217646.436806] After Completion
      [3217646.437010] Closed!
      [3217727.378388] Cleanup Module
      [3217727.378393] Check if we need to complete anything
      [3217727.378395] Send a completion!
      [3217727.378397] Unregistering Character Driver
      [3217727.378400] Give back all the numbers we requested
      [3217727.378402] Remove the class driver
      [3217727.378571] Release the class
      [3217727.378593] Finished Cleanup Module, Exiting
      

      如果我运行以下命令:

      1. 加载模块
      2. 运行用户应用程序
      3. 卸载模块
      4. [3218223.442777] Registering Driver
        [3218223.442934] Driver Initialized!
        [3218229.378396] Opened!
        [3218229.378419] Read!
        [3218229.378422] Wait for Completion
        

        然后模块没有卸载。如果这是一个真实的设备,如USB硬盘驱动器,用户可能会在读取事务中删除设备。这似乎是错误的,或者我可能错过了一些东西。我错过了什么吗?

1 个答案:

答案 0 :(得分:0)

虽然可以随时删除设备 ,但驱动程序(例如内核模块)无法卸载在使用该设备的某些操作期间(例如,阅读)。是向上级(例如文件系统)报告设备缺失的驱动程序。