Reputation: 540
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
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