2017年2月12日 星期日

Block device 的 plug

記憶體, 硬碟皆可以掛載成block device. Kenel 會收集io request, 並且針對讀寫方向性,以及sector做排序的動作, 所採用的是elevator algorithm. Kernel 4.4.21所使用的elevator algorithmnoop [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);

參考網址:

沒有留言:

張貼留言