Ошибка сегментации в драйвере устройства Linux

Следующий код вызывает ошибку сегментации при попытке чтения с устройства (например: cat /dev/device_name). Однако, если я удаляю вызовы down_interruptible и up из методов klg_read и klg_write и перемещаю их в klg_open и klg_close соответственно, код работает нормально. Я экспериментирую с драйверами устройств и хотел бы понять, почему возникает эта ошибка сегментации. Спасибо!

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/interrupt.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/semaphore.h>
#include<linux/spinlock.h>
#include<linux/sched.h>
#include<linux/device.h>



#define DEVICE_NAME "klg_app"
#define AUTHOR "morpheus15"
#define LICENSE "GPL"
#define CLASS_NAME "klg_class"
#define FOR(i,x,n) for(i=x;i<n;i++)
#define BUF_MAX_SIZE 10
#define FIRST_MINOR_NUM 0
#define MINOR_DEVICE_COUNT 1

typedef struct
{
  char data[BUF_MAX_SIZE];
}klg_device_t;


static klg_device_t klgdev;
static int ret,major;
static dev_t dev_num;
static struct cdev *kcdev = NULL;
static struct class *klg_class = NULL;
static struct semaphore semm;

static int klg_open(struct inode *klg_inode,struct file *filp)
{
  filp->private_data = &klgdev;
  /*if(down_interruptible(&semm))
    {
      printk(KERN_INFO "could not open device %s\n",DEVICE_NAME);
      return -ERESTARTSYS;
    }
    */
  printk(KERN_INFO "device %s called open()\n",DEVICE_NAME);
  return 0;
}

static int klg_close(struct inode *klg_inode,struct file *filp)
{
  //up(&semm);
  printk(KERN_INFO "device %s called close()\n",DEVICE_NAME);
  return 0;
}

static ssize_t klg_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)
{
  printk(KERN_INFO "device %s called read()\n",DEVICE_NAME);
  klg_device_t *klgp = filp->private_data;
   if (down_interruptible(&semm))
    {
      printk(KERN_INFO "device %s already in use. could not open for reading\n",DEVICE_NAME);
      return -ERESTARTSYS;
    }

  int len = strlen(klgp->data);
  if (!len || (*pos) > len)
    {
       up(&semm);
      return 0;
    }


  if (*pos + count > len)
    {
      count = len - *pos;
    }

  ret = copy_to_user(buf,klgp->data,count);
  if (ret)
    {
      printk(KERN_INFO "copy_to_user() failed for device %s, exit with error %d\n",DEVICE_NAME,ret);
      up(&semm);
      return -EFAULT;
    }

  (*pos) += count;
   up(&semm);
  return count;

}

static ssize_t klg_write(struct file *filp,char __user *buf,size_t count,loff_t *pos)
{


  printk(KERN_INFO "device %s called write()\n",DEVICE_NAME);
  klg_device_t *klgp = filp->private_data;
   if (down_interruptible(&semm))
    {
      printk(KERN_INFO "device %s already in use. Could not open for writing\n",DEVICE_NAME);
      return -ERESTARTSYS;
    }


  memset(klgp->data,0,BUF_MAX_SIZE);

  if (count  > BUF_MAX_SIZE)
    {
      count = BUF_MAX_SIZE;
    }

  ret = copy_from_user(klgp->data,buf,count);
  if (ret)
    {
      printk(KERN_INFO "copy_from_user() failed for device %s, exit with error %d\n",DEVICE_NAME,ret);
        up(&semm);
      return -EFAULT;
    }

  up(&semm);
  return count;


}


static struct file_operations fops = 
  {
    .owner = AUTHOR,
    .open = klg_open,
    .release = klg_close,
    .read = klg_read,
    .write = klg_write
  };



 static void initialize_constructs(void)
{
  sema_init(&semm,1);
}

static int __init klg_init(void)
{
  ret = alloc_chrdev_region(&dev_num,FIRST_MINOR_NUM,MINOR_DEVICE_COUNT,DEVICE_NAME);
  if (ret)
    {
      printk(KERN_INFO "could not alloc_chrdev_region for device %s\n",DEVICE_NAME);
      return -1;
    }
  major = MAJOR(dev_num);
  printk(KERN_INFO "registered device %s with major number %d\n",DEVICE_NAME,major);


  // add the device to /sys/class/CLASS_NAME
  klg_class = class_create(DEVICE_NAME,CLASS_NAME);
  if (!klg_class)
    {
      printk(KERN_INFO "could not add device %s to /sys/class/%s\n",CLASS_NAME);
      unregister_chrdev_region(dev_num,MINOR_DEVICE_COUNT);
      return -2;
    }

  // create an entry in /dev for the device file
  if (!device_create(klg_class,NULL,dev_num,NULL,DEVICE_NAME))
    {
      printk(KERN_INFO "could not create entry /dev/%s for device %s\n",DEVICE_NAME,DEVICE_NAME);
      class_destroy(klg_class);
      unregister_chrdev_region(dev_num,MINOR_DEVICE_COUNT);
      return -2;
    }

  // create the cdev struct for the character device
  kcdev = cdev_alloc();
  kcdev->owner = AUTHOR;
  kcdev->ops = &fops;
  ret = cdev_add(kcdev,dev_num,MINOR_DEVICE_COUNT);
  if (ret)
    {
      printk(KERN_INFO "could not allocate a char dev struct for device %s\n",DEVICE_NAME);
      device_destroy(klg_class,dev_num);
      class_destroy(klg_class);
      unregister_chrdev_region(dev_num,MINOR_DEVICE_COUNT);
      return -1;
    }


  initialize_constructs();
  printk(KERN_INFO "successfully created a character device driver for device %s\n",DEVICE_NAME);
  return 0;
}

static void __exit klg_exit(void)
{
  // destroy in the reverse order
  cdev_del(kcdev);
  device_destroy(klg_class,dev_num);
  class_destroy(klg_class);
  unregister_chrdev_region(dev_num,MINOR_DEVICE_COUNT);
  printk(KERN_INFO "successfully unregistered character device %s and all its components\n",DEVICE_NAME);
}


module_init(klg_init);
module_exit(klg_exit);
MODULE_AUTHOR(AUTHOR);
MODULE_LICENSE(LICENSE);
MODULE_DESCRIPTION("this is a sample application");

person Rohit Gurunath    schedule 28.07.2015    source источник
comment
При возникновении ошибки в модуле ядра в системном журнале должно быть сообщение (dmesg), описывающее эту ошибку. Обычно стек вызовов также поставляется с этим сообщением. Добавьте этот стек в свой вопросительный пост. Кроме того, попробуйте собрать свой модуль с параметрами компилятора ccflags := -Wall. Должно быть хотя бы одно предупреждение о строке kcdev->owner = AUTHOR; (правильное kcdev->owner = THIS_MODULE;).   -  person Tsyvarev    schedule 28.07.2015
comment
Что ты имел в виду под этим filp->private_data = &klgdev;?!   -  person 0andriy    schedule 28.07.2015
comment
Часть filp-›private_data = &klgdev была просто экспериментальной, и в ней не было никакого конкретного применения. Код вполне может обойтись без этой строки. Спасибо!   -  person Rohit Gurunath    schedule 28.07.2015


Ответы (1)


Как заметил @Tsyvarev, просто взглянув на ваш код, вы неправильно инициализируете поле владельца вашего драйвера и файловых операций:

warning: assignment from incompatible pointer type [enabled by default]
kcdev->owner = AUTHOR;

warning: initialization from incompatible pointer type [enabled by default]
 .owner = AUTHOR,

Из-за этого каждый раз, когда вы выполняете системный вызов open для своего драйвера, вы получите что-то вроде этого:

BUG: unable to handle kernel paging request at 75ae1601
IP: [<x109b89f>] try_module_get+0x1f/0x80
*pde = 00000000
Oops: 0000 [#1] SMP
...

Просто измените его на:

kcdev->owner = THIS_MODULE;

а также:

static struct file_operations fops = 
{
    .owner = THIS_MODULE;
    ...
};

В вашем коде есть и другие предупреждения. Возможно, вы захотите взглянуть и на них.

person dragosht    schedule 28.07.2015
comment
Спасибо! Смена владельца на THIS_MODULE сработала. - person Rohit Gurunath; 28.07.2015