Mykola Niemtsov
Mykola Niemtsov

Reputation: 540

I/O to device from kernel module fails with EFAULT

I have created block device in kernel module. When some I/O happens I read/write all data from/to another existing device (let's say /dev/sdb).

It opens OK, but read/write operations return 14 error(EFAULT,Bad Address). After some research I found that I need map address to user space(probably buffer or filp variables), but copy_to_user function does not help. Also I looked to mmap() and remap_pfn_range() functions, but I can not get how to use them in my code, especially where to get correct vm_area_struct structure. All examples that I found, used char devices and file_operations structure, not block device.

Any hints? Thanks for help.

Here is my code for reading:

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

Upvotes: 4

Views: 1334

Answers (1)

Mykola Niemtsov
Mykola Niemtsov

Reputation: 540

I found a better way for I/O to block device from kernel module. I have used bio structure for that. Hope this information save somebody from headache.

1) So, if you want to redirect I/O from your block device to existing block device, you have to use own make_request function. For that you should use blk_alloc_queue function to create queue for your block device like this:

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

Than into own_make_request function change bi_bdev member into bio structure to device in which you redirecting I/O and call generic_make_request function:

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

More information here at 16 chapter. If link is broken by some cause, here is name of the book - "Linux Device Drivers, Third Edition"

2) If you want read or write your own data to existing block device from kernel module you should use submit_bio function.

Code for writing into specific sector(you need to implement writeComplete function also):

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

Code for reading from specific sector(you need to implement readComplete function also):

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 can be allocated with alloc_page(GFP_KERNEL). Also for changing data in page use page_address(page). It returns void* so you can interpret that pointer as whatever you want.

Upvotes: 2

Related Questions