spinlock_irqsave с взаимоблокировкой

Я написал модуль ядра, который выполняет nf_register_hook и использует механизм символьных устройств для передачи захваченных пакетов в пользовательское пространство с перехватом чтения с устройства. Я использую глобальные переменные буфера и размера буфера, поэтому мне нужно заблокировать его, когда приходит новый пакет или пользователь читает мое устройство. Я использовал splinlock_irqsave и spin_unlock_irqrestore(&locker,flags), но мой модуль зашел в тупик и система зависла.

unsigned int main_hook(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int(*okfn)(struct sk_buff*)) {
unsigned long flags;
spin_lock_irqsave(&locker,flags);
...
spin_unlock_irqrestore(&locker,flags);
}

ssize_t sniffer_dev_read(struct file *filep, char *buff, size_t count, loff_t *offp) {
spin_lock_irqsave(&locker,flags);
...
spin_unlock_irqrestore(&locker,flags);
}

main_hook is registered in nf_register_hook()
sniffer_dev_read is registered in register_chrdev

когда пользователь читает с устройства, система переходит в тупик. идеи? или может быть сохранение/восстановление irq несовместимо с чтением устройства netfiler hook/char, и я должен использовать здесь специальную блокировку?


person SeregASM    schedule 24.08.2011    source источник


Ответы (3)


Во-первых, вы должны перекомпилировать ваше ядро ​​с включенными параметрами отладки блокировки и повторить попытку. Они могут помочь указать на причину.

Есть несколько возможных причин взаимоблокировки в spin_lock_irqsave. Это может быть рекурсивная блокировка (то есть вы пытаетесь снова вызвать spin_lock_* в разделе кода, где вы удерживаете заблокированную спин-блокировку). Возможно, вы спите с заблокированной спиновой блокировкой (никогда не делайте этого - для каждой функции, которую вы вызываете с заблокированной блокировкой, вы должны знать, может ли она спать или нет). Это может быть тупик AB/BA (одна часть кода сначала блокирует A, а затем B; другая часть сначала блокирует B, а затем A; если первая часть заблокировала A, но не B, а вторая часть заблокировала B, но не A, у вас есть тупик). И так далее. Параметры отладки блокировки могут обнаружить и предупредить вас о многих из них.

Поскольку то, что вы блокируете, представляет собой «глобальный буфер и переменные размера буфера», постарайтесь уменьшить заблокированную область до минимума. Вместо того, чтобы блокировать в начале функции и разблокировать в конце, делайте как можно больше вне блокировки и блокируйте только во время манипулирования вашим буфером. В идеале заблокированный раздел должен состоять из нескольких инструкций без вызовов функций. В этом случае зайти в тупик намного сложнее.


Теперь, когда я все это сказал, моя попытка психической отладки (т. е. угадать, в чем проблема): вы вызываете copy_to_user (который может спать) с удерживаемой спин-блокировкой.

person CesarB    schedule 24.08.2011

Вы не должны использовать спин-блокировки для блокировки ресурса, который можно использовать с разных уровней контекста. Занят-блокирует процессор, на котором залочен.

Вызывается ли main_hook из контекста прерывания/нижней половины? Если это так, вы можете использовать work_queues, чтобы «задание» (memcpy...) выполнялось с более низким приоритетом. Как правило, вы должны как можно меньше находиться внутри спин-блокировки.

person Patrick B.    schedule 24.08.2011

Я предполагаю, что у вас есть, вероятно, простой программный сбой (например, попытка уважать NULL и т. д.), но поскольку spin_lock_irqsave отключает прерывания, когда вы попадаете в этот режим исключения, вы переходите с отключенными прерываниями, поэтому вся машина заблокирована.

Обратите внимание, что, поскольку ваш обработчик NF работает в контексте нижней половины, вам действительно не нужно отключать прерывания - только нижние половины. Это облегчит отладку.

person gby    schedule 24.08.2011