• Разрешаются прерывания (нет необходимости восстанавливать состояние системы прерываний в первоначальное значение, так как этот код может выполняться только в обработчике отложенного прерывания, который вызывается только при разрешенных прерываниях).
• Организовывается цикл по всем тасклетам в полученном списке.
• Если данная машина является многопроцессорной, то нужно проверить не выполняется ли текущий тасклет на другом процессоре, то есть проверить не установлен ли флаг TASLET_STATE_RUN
. Если тасклет уже выполняется, то его необходимо пропустить и перейти к следующему тасклету в списке (вспомним, что только один тасклет данного типа может выполняться в любой момент времени).
• Если тасклет не выполняется, то нужно установить флаг TASLET_STATE_RUN
, чтобы другой процессор не мог выполнить этот тасклет.
• Проверяется значение поля count
на равенство нулю, чтобы убедиться, что тасклет не запрещен. Если тасклет запрещен (поле count не равно нулю), то нужно перейти к следующему тасклету, который ожидает на выполнение.
• Теперь можно быть уверенным, что тасклет нигде не выполняется, нигде не будет выполняться (так как он помечен как выполняющийся на данном процессоре) и что значение поля count равно нулю. Необходимо выполнить обработчик тасклета. После того как тасклет выполнился, следует очистить флаг TASLET_STATE_RUN
и поле state
.
• Повторить описанный алгоритм для следующего тасклета, пока не останется ни одного тасклета, ожидающего выполнения.
Реализация тасклетов проста, но в то же время очень остроумна. Как видно, все тасклеты реализованы на базе двух отложенных прерываний TASKLET_SOFTIRQ
и HI_SOFTIRQ
. Когда тасклет запланирован на выполнение, ядро генерирует одно из этих двух отложенных прерываний. Отложенные прерывания, в свою очередь, обрабатываются специальными функциями, которые выполняют все запланированные на выполнение тасклеты. Эти специальные функции гарантируют, что только один тасклет данного типа выполняется в любой момент времени (но тасклеты разных типов могут выполняться одновременно). Вся эта сложность спрятана за простым и ясным интерфейсом.
Использование тасклетов
В большинстве случаев тасклеты — это самый предпочтительный механизм, с помощью которого следует реализовать обработчики нижних половин для обычных аппаратных устройств. Тасклеты можно создавать динамически, их просто использовать, и они сравнительно быстро работают.
Тасклеты можно создавать статически и динамически. Какой вариант лучше выбрать, зависит от того, как необходимо (или желательно) пользователю обращаться к тасклету: прямо или через указатель. Для статического создания тасклета (и соответственно, обеспечения прямого доступа к нему) необходимо использовать один из двух следующих макросов, которые определены в файле
:
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);
Оба макроса статически создают экземпляр структуры struct_tasklet_struct
с указанным именем (name
). Когда тасклет запланирован на выполнение, то вызывается функция func
, которой передается аргумент data
. Различие между этими макросами состоит в значении счетчика ссылок на тасклет (поле count
). Первый макрос создает тасклет, у которого значение поля count равно нулю, и, соответственно, этот тасклет разрешен. Второй макрос создает тасклет и устанавливает для него значение поля count
, равное единице, и, соответственно, этот тасклет будет запрещен. Можно привести следующий пример.
DECLARE_TASKLET(my_tasklet, my_tasklet_handler, dev);
Эта строка эквивалентна следующей декларации.
struct tasklet_struct my_tasklet = {
NULL, 0, ATOMIC_INIT(0), tasklet_handler, dev
};
В данном примере создается тасклет с именем my_tasklet
, который разрешен для выполнения. Функция tasklet_handler
будет обработчиком этого тасклета. Значение параметра dev
передается в функцию-обработчик при вызове данной функции.
Для инициализации тасклета, на который указывает заданный указатель struct tasklet_struct* t
— косвенная ссылка на динамически созданную ранее структуру, необходимо использовать следующий вызов.
tasklet_init(t, tasklet_handler, dev); /* динамически, а не статически */
Функция-обработчик тасклета должна соответствовать правильному прототипу.
void tasklet_handler(unsigned long data);