来自内核模块的设备I / O因EFAULT而失败

时间:2013-06-27 14:22:37

标签: linux-kernel kernel-module kernel

我在内核模块中创建了块设备。当发生一些I / O时,我从/向另一个现有设备读取/写入所有数据(假设为/dev/sdb)。

打开OK,但读/写操作返回14错误(EFAULT,错误地址)。经过一些研究后,我发现我需要地图地址到用户空间(可能是bufferfilp变量),但copy_to_user函数没有帮助。我还查看了mmap()remap_pfn_range()函数,但我无法在代码中使用它们,特别是在哪里获得正确的vm_area_struct结构。我找到的所有示例都使用了char设备和file_operations结构,而不是阻止设备。

任何提示?谢谢你的帮助。

这是我的阅读代码:

mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
filp = filp_open("/dev/sdb",  O_RDONLY | O_DIRECT | O_SYNC, 00644);
if(IS_ERR(filp))
{
    set_fs(old_fs);
    int err = PTR_ERR(filp);
    printk(KERN_ALERT"Can not open file - %d", err);
    return;
}
else
{
    bytesRead = vfs_read(filp, buffer, nbytes, &offset);  //It gives 14 error
    filp_close(filp, NULL);
}
set_fs(old_fs);

1 个答案:

答案 0 :(得分:2)

我找到了一种更好的I / O方法来阻止来自内核模块的设备。我已经使用了bio结构。希望这些信息能让人免于头痛。

1)因此,如果要将I / O从块设备重定向到现有块设备,则必须使用自己的make_request功能。为此,您应该使用blk_alloc_queue函数为块设备创建队列,如下所示:

device->queue = blk_alloc_queue(GFP_KERNEL);
blk_queue_make_request(device->queue, own_make_request);

own_make_request函数更改bi_bdev成员转换为bio结构到您重定向I / O并调用generic_make_request函数的设备:

bio->bi_bdev = device_in_which_redirect;
generic_make_request(bio);

更多信息here,第16章。如果链接因某种原因而中断,这里是本书的名称 - “Linux设备驱动程序,第三版”

2)如果您想从内核模块向现有块设备读取或写入您自己的数据,则应使用submit_bio函数。

写入特定部门的代码(您还需要实现writeComplete功能):

void writePage(struct block_device *device,
           sector_t sector, int size, struct page *page)
{
    struct bio *bio = bio_alloc(GFP_NOIO, 1);
    bio->bi_bdev = vnode->blkDevice;
    bio->bi_sector = sector;
    bio_add_page(bio, page, size, 0);
    bio->bi_end_io = writeComplete;
    submit_bio(WRITE_FLUSH_FUA, bio);
}

从特定部门读取的代码(您还需要实现readComplete功能):

int readPage(struct block_device *device, sector_t sector, int size,
     struct page *page)
{
    int ret;
    struct completion event;
    struct bio *bio = bio_alloc(GFP_NOIO, 1);
    bio->bi_bdev = device;
    bio->bi_sector = sector;
    bio_add_page(bio, page, size, 0);
    init_completion(&event);
    bio->bi_private = &event;
    bio->bi_end_io = readComplete;
    submit_bio(READ | REQ_SYNC, bio);
    wait_for_completion(&event);
    ret = test_bit(BIO_UPTODATE, &bio->bi_flags);
    bio_put(bio);
    return ret;
}
可以使用alloc_page(GFP_KERNEL)分配

page。同样用于更改page使用page_address(page)中的数据。它返回void*,因此您可以将该指针解释为您想要的任何内容。