記憶體, 硬碟皆可以掛載成block device. Kenel 會收集io request, 並且針對讀寫方向性,以及sector做排序的動作, 所採用的是elevator algorithm. Kernel 4.4.21所使用的elevator algorithm為noop [deadline] cfq這三種算法.
當io進到block device, 此時block device會進入到plugged state, 此時並不會立即性操作driver,
io傳遞結束, process 進入到wait io finish時, 此時block device 會進入到unplugged state, 並且開始操作driver. 這樣的作法, 是想要將多個io打包, 一次性送給driver, 提高硬體使用率.
3:4.4.21的作法
write_cache_pages()->__writepage()->__block_write_full_page()->submit_bh_wbc()->submit_bio()->generic_make_request()->dm_make_request()->__split_and_process_bio()->generic_make_request()->md_make_request()->raid0_make_request()->generic_make_request()->dm_make_request()->__split_and_process_bio()->generic_make_request()->blk_queue_bio()
write_cache_pages原先想法是固定時間間隔執行, 或者alloc page buffer失敗時, 會做write_cache_pages(),
搭配blkdev_write_iter()的
blk_start_plug(&plug);
ret
= __generic_file_write_iter(iocb, from);
blk_finish_plug(&plug);
理論上, write_cache_pages會是隨機的, 並不會因為上述寫法, 就會在start,finish中間執行, 但是經過printk
之後, 的確不會在這blk_start_plug()到blk_finish_plug()過程中, 執行到blk_queue_bio(),而這個函數是將request放入plug->list
透過printk發現, blk_finish_plug()裡的plug->list幾乎為空.
那麼plug->list什麼時候會有值?
當io寫入到透過blkdev_write_iter()將io寫入到vfs cahche buffer, 由上述的write_cache_pages()---->blk_queue_bio() 在這個函數執行list_add_tail(&req->queuelist, &plug->list);
此時plug->list不為空時, 此時blk_finish_plug()才會執行到__elv_add_request().
如何跟更底層的driver溝通?
A:blk_finish_plug()->blk_flush_plug_list()->queue_unplugged()->__blk_run_queue()->__blk_run_queue_uncond()->scsi_request_fn()
下面的註解, 往更底層的driver送資料
/*
* Dispatch the command to the low-level
driver.
*/
cmd->scsi_done =
scsi_done;
rtn =
scsi_dispatch_cmd(cmd);
如何找elevator
algorithm註冊函數?
用elevator_merge_fn()去搜尋noop
[deadline] cfq相關的註冊函數. 在elv_merge()裡面, 這邊會判斷函數是否存在,要不要做merge,noop正是不用merge.
if
(e->type->ops.elevator_merge_fn)
return
e->type->ops.elevator_merge_fn(q, req, bio);
參考網址:
elevator algorithm: https://en.wikipedia.org/wiki/Elevator_algorithm
沒有留言:
張貼留言