【问题标题】:I/O to device from kernel module fails with EFAULT从内核模块到设备的 I/O 失败并显示 EFAULT
【发布时间】:2013-06-27 14:22:36
【问题描述】:

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

打开正常,但读/写操作返回 14 错误(EFAULT,Bad Address)。经过一番研究,我发现我需要将地址映射到用户空间(可能是buffer 或filp 变量),但copy_to_user 函数没有帮助。我还查看了mmap() 和remap_pfn_range() 函数,但我不知道如何在我的代码中使用它们,尤其是在哪里可以获得正确的vm_area_struct 结构。我发现的所有示例都使用了字符设备和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);

【问题讨论】:

  • buffer 是一个 char* 变量。我们从/dev/sdb 读取数据给它。它来自request 函数,如request->buffer。

标签: linux-kernel kernel-module kernel


【解决方案1】:

我找到了一种更好的 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 Device Drivers, Third Edition”

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;
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-28
    • 2021-03-01
    • 2014-11-17
    • 2014-07-21
    • 1970-01-01
    • 2017-10-18
    相关资源
    最近更新 更多