我在内核模块中创建了块设备。当发生一些I / O时,我从/向另一个现有设备读取/写入所有数据(假设为/dev/sdb
)。
打开OK,但读/写操作返回14错误(EFAULT
,错误地址)。经过一些研究后,我发现我需要地图地址到用户空间(可能是buffer
或filp
变量),但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);
答案 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*
,因此您可以将该指针解释为您想要的任何内容。