Как Linux знае за отложена работа в драйвер и кога точно да използва данните, донесени от хардуерното устройство?

Когато ядрото се опита да прочете блок от твърд диск, то изпраща софтуерно прекъсване, което ще бъде обработено от драйвера на устройството. Ако драйверът на устройството разделя работата по обработката на заявката на горна и долна половина чрез работни опашки, как ядрото знае, че данните не са налични, докато долната половина не приключи?

С други думи, как ядрото знае, че драйверът все още не е извлякъл необходимия блок и не го е копирал в предоставения буфер?

Очевидно, ако ядрото очаква данните да са лесно достъпни, след като горната половина завърши изпълнението и се върне, тогава може да прочете нежелани данни.


person hebbo    schedule 10.09.2013    source източник


Отговори (1)


Приложният програмен интерфейс (API) на драйвера на блоково устройство се е променил няколко пъти от създаването на Linux, но днес изглежда по същество както следва.

Функцията за инициализация извиква blk_init_queue, предавайки обратно извикване на заявка и незадължително заключване за тази опашка:

struct request_queue *q;
q = blk_init_queue(my_request_cb, &my_dev->lock);

my_request_cb е обратно извикване, което ще обработва всички I/O за това блоково устройство. Заявките за вход/изход ще бъдат изпратени в тази опашка и my_request_cb ще бъде извикан, за да ги обработва една след друга, когато нивото на драйвера на блока на ядрото реши. След това тази опашка се добавя към диска:

struct gendisk *disk;
disk->queue = q;

и след това дискът се добавя към системата:

add_disk(disk);

disk има друга информация като главното число, първото второстепенно число и други файлови операции (open, release, ioctl и други, но не и read и write, както се срещат в символните устройства).

Сега my_request_cb може да бъде извикан по всяко време и не е задължително да бъде извикан от контекста на процеса, който е инициирал четене/запис на блоковото устройство. Това повикване е асинхронно от ядрото.

Тази функция се декларира по следния начин:

static void my_request_cb(struct request_queue *q);

Опашката q съдържа подреден списък от заявки към това блокиращо устройство. След това функцията може да разгледа следващата заявка (blk_fetch_request(q)). За да отбележи дадена заявка като изпълнена, тя ще извика blk_end_request_all (съществуват и други варианти в зависимост от ситуацията).

И тук отговарям на въпроса ви: ядрото знае, че определена заявка за блокиращо устройство е направена, когато неговият драйвер извика blk_end_request_all или подобна функция за тази заявка. Драйверът не трябва да прекрати заявка в рамките на my_request_cb: той може например да започне DMA прехвърляне, да постави заявката отново в опашката, да игнорира други и само когато бъде заявено прекъсването за завършено DMA прехвърляне, да я прекрати, ефективно казвайки на ядрото че тази специфична операция за четене/запис е завършена.

LDD3/глава 16 може да помогне, но някои неща се промениха след 2.6.

person eepp    schedule 11.09.2013
comment
Отлично! Благодаря ти! - person hebbo; 17.09.2013