2013-06-27 55 views
3

我在內核模塊中創建了塊設備。當某些I/O發生時,我將讀取/寫入所有數據到另一個現有設備(比如/dev/sdb)。從內核模塊到設備的I/O到EFAULT失敗

它打開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); 
+0

什麼是「緩衝區」? –

+0

'buffer'是一個'char *'變量。我們從'/ dev/sdb'讀取數據。它來自'request-> buffer'這樣的'request'函數。 –

回答

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*,所以你可以將該指針解釋爲任何你想要的。

+1

我已將代碼更改爲更正確的代碼。前一個不完全正確。對於那個很抱歉。 –

+0

太棒了,謝謝。 –