Ошибка в простом символьном модуле устройства - PullRequest
0 голосов
/ 01 сентября 2018

Я пытаюсь написать модуль ядра простого символьного устройства, который выделяет 100 байт памяти в пространстве ядра и имеет функции чтения, записи и поиска, которые работают с выделенными 100 байтами памяти ядра. Я попытался отобразить текст «Hello World» в соответствующем файле устройства, но получил ошибку «Недопустимый аргумент» и предупреждение о dmesg.

Файл драйвера устройства c с именем "kernelbuffer.c"

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>   /* printk() */
#include <linux/slab.h>     /* kmalloc() */
#include <linux/fs.h>       /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>    /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>    /* copy_*_user */

static int major;

struct buffer_dev{
    char data[100];
    struct semaphore sem;
    struct cdev cdev;
};

struct buffer_dev *buff_device;

static void buffer_open(struct inode *inode, struct file *filep){
    struct buffer_dev *dev;
    printk(KERN_NOTICE "Opening buffer.\n");
    dev = container_of(inode->i_cdev, struct buffer_dev, cdev);
    filep->private_data = dev;
    printk(KERN_NOTICE "Buffer opened.\n");
    return 0;
}

ssize_t buffer_read(struct file *filep, char __user *buf, size_t count,
                loff_t *f_pos)
{
    printk(KERN_NOTICE "Reading from buffer.\n");
    struct buffer_dev *dev = filep->private_data;
    ssize_t retval = 0;
    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;
    if (*f_pos >= 100)
        goto out;
    if (*f_pos + count > 100)
        count = 100 - *f_pos;
    printk(KERN_NOTICE "First stage fine.\n");
    if(copy_to_user(buf, dev->data + *f_pos, count)){
        retval = -EFAULT;
        goto out;
    }
    *f_pos += count;
    retval = count;

    out:
        up(&dev->sem);
    return retval;
}

ssize_t buffer_write(struct file *filep, char __user *buf, size_t count,
                 loff_t *f_pos)
{
    printk(KERN_NOTICE "Writing to buffer.\n");
    struct buffer_dev *dev = filep->private_data;
    ssize_t retval = -ENOMEM; /* value used in "goto out" statements */

    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;

    if (*f_pos >= 100)
        goto out;
    if (*f_pos + count > 100)
        count = 100 - *f_pos;

    if (copy_from_user(dev->data + *f_pos, buf, count)) {
        retval = -EFAULT;
        goto out;
    }
    printk(KERN_NOTICE "Write success.\n");
    *f_pos += count;
    retval = count;

    out:
        up(&dev->sem);
    return retval;

}

loff_t buffer_llseek(struct file *filp, loff_t off, int whence)
{
    struct buffer_dev *dev = filp->private_data;
    loff_t newpos;

    switch(whence) {
        case 0: /* SEEK_SET */
            newpos = off;
            break;

        case 1: /* SEEK_CUR */
             newpos = filp->f_pos + off;
             break;

        case 2: /* SEEK_END */
            newpos = 100 + off;
            break;

        default: /* can't happen */
            return -EINVAL;
     }
    if (newpos < 0 || newpos >= 100) return -EINVAL;
    filp->f_pos = newpos;
    return newpos;
}

int buffer_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations buffer_fops = {
    .owner =    THIS_MODULE,
    .llseek =   buffer_llseek,
    .read =     buffer_read,
    .write =    buffer_write,
    .open =     buffer_open,
    .release =  buffer_release,
};

static void setup_cdev(struct buffer_dev *dev){
    int err, devno = MKDEV(major, 0);

    cdev_init(&dev->cdev, &buffer_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &buffer_fops;
    err = cdev_add (&dev->cdev, devno, 1);

    if (err)
        printk(KERN_NOTICE "Error %d adding device\n", err);

}

void cleanup_module_buffer(void)
{
    printk(KERN_NOTICE "Removing buffer device.\n");
    dev_t devno = MKDEV(major, 0);
    if(buff_device){
        cdev_del(&buff_device->cdev);
        kfree(buff_device);
    }
    unregister_chrdev_region(devno,1);
}

int module_init_buffer(void)
{
    printk(KERN_NOTICE "Initializing buffer device.\n");
    int result, i;
    dev_t dev = 0;

    result = alloc_chrdev_region(&dev, 0, 1,
            "buffer");
    major = MAJOR(dev);
    if (result < 0) {
        printk(KERN_WARNING "Can't get major %d\n", major);
        return result;
    }

    buff_device = kmalloc(sizeof(struct buffer_dev), GFP_KERNEL);
    if (!buff_device) {
        result = -ENOMEM;
        goto fail;  /* Make this more graceful */
    }
    memset(buff_device, 0, sizeof(struct buffer_dev));

    sema_init(&(buff_device->sem), 1);
    setup_cdev(&buff_device);

    return 0; /* succeed */

  fail:
      cleanup_module_buffer();
  return result;
}


module_init(module_init_buffer);
module_exit(cleanup_module_buffer);

MODULE_AUTHOR("Gautam Ramakrishnan");
MODULE_LICENSE("GPL");

Makefile, используемый для его компиляции:

obj-m := kernelbuffer.o 

KDIR  := /lib/modules/$(shell uname -r)/build

all:    $(MAKE) -C $(KDIR) M=$(shell pwd) modules

clean:
    $(MAKE) -C $(KDIR) M=$(shell pwd) clean
    $(RM) Module.markers modules.order

Я успешно запустил insmod для файла kernelbuffer.ko.

На ходу cat /proc/devices он появляется с большим числом 247.

Затем я выполнил sudo mknod /dev/kbuffer c 247 0, чтобы создать соответствующий файл драйвера.

При запуске echo "Hello World > /dev/kbuffer в качестве суперпользователя я получаю ответ bash: /dev/kbuffer: Invalid argument

При запуске dmesg я получаю следующий вывод:

[  583.814364] Opening buffer.
[  583.814367] Buffer opened.
[  583.814369] ------------[ cut here ]------------
[  583.814392] WARNING: CPU: 0 PID: 2673 at /build/linux-dcxD3m/linux-4.4.0/fs/namei.c:3206 path_openat+0xb19/0x1330()
[  583.814393] Modules linked in: kernelbuffer(OE) nls_utf8 isofs vboxsf(OE) crct10dif_pclmul crc32_pclmul snd_intel8x0 snd_ac97_codec ac97_bus aesni_intel joydev aes_x86_64 snd_pcm lrw gf128mul snd_seq_midi glue_helper ablk_helper snd_seq_midi_event input_leds vboxvideo(OE) cryptd snd_rawmidi ttm snd_seq serio_raw snd_seq_device drm_kms_helper snd_timer drm snd soundcore fb_sys_fops syscopyarea sysfillrect 8250_fintek sysimgblt i2c_piix4 vboxguest(OE) mac_hid parport_pc ppdev lp parport autofs4 hid_generic usbhid hid psmouse ahci libahci e1000 pata_acpi fjes video
[  583.814427] CPU: 0 PID: 2673 Comm: bash Tainted: G           OE   4.4.0-31-generic #50-Ubuntu
[  583.814429] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[  583.814430]  0000000000000286 00000000c588cd8a ffff8800593ebcb8 ffffffff813f1143
[  583.814433]  0000000000000000 ffffffff81cd8430 ffff8800593ebcf0 ffffffff81081102
[  583.814435]  ffff8800593ebdd0 0000000000008241 ffff8800593ebef4 00000000ffffffea
[  583.814437] Call Trace:
[  583.814442]  [<ffffffff813f1143>] dump_stack+0x63/0x90
[  583.814445]  [<ffffffff81081102>] warn_slowpath_common+0x82/0xc0
[  583.814447]  [<ffffffff8108124a>] warn_slowpath_null+0x1a/0x20
[  583.814449]  [<ffffffff8121bfb9>] path_openat+0xb19/0x1330
[  583.814452]  [<ffffffff814f0a41>] ? copy_termios+0x71/0x80
[  583.814455]  [<ffffffff8121d9c1>] do_filp_open+0x91/0x100
[  583.814457]  [<ffffffff810ca961>] ? __raw_callee_save___pv_queued_spin_unlock+0x11/0x20
[  583.814459]  [<ffffffff8122b2d8>] ? __alloc_fd+0xc8/0x190
[  583.814462]  [<ffffffff8120c248>] do_sys_open+0x138/0x2a0
[  583.814464]  [<ffffffff81092230>] ? SyS_rt_sigaction+0xa0/0xd0
[  583.814466]  [<ffffffff8120c3ce>] SyS_open+0x1e/0x20
[  583.814469]  [<ffffffff8182db32>] entry_SYSCALL_64_fastpath+0x16/0x71
[  583.814470] ---[ end trace ad5061abc2861b8c ]---

Мне не удалось найти решение этой проблемы. Было бы очень полезно, если бы кто-нибудь мог указать причину, по которой мой код не работает, и любые исправления, если это возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...