Почему пустая критическая секция внутри крючков netfilter, возникает "BUG: планирование при атомарной ошибке"?
Я написал этот крючок:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/skbuff.h>
#include <linux/mutex.h>
static struct nf_hook_ops nfho;
static struct mutex critical_section;
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *)) {
mutex_lock(&critical_section);
mutex_unlock(&critical_section);
return NF_ACCEPT;
}
int init_module() {
nfho.hook = hook_func;
nfho.hooknum = NF_INET_PRE_ROUTING;
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST;
mutex_init(&critical_section);
nf_register_hook(&nfho);
return 0;
}
void cleanup_module() {
nf_unregister_hook(&nfho);
}
секция init:
mutex_init(&queue_critical_section);
mutex_init(&ioctl_critical_section);
Я определил статическую переменную:
static struct mutex queue_critical_section;
Так как нет кода между lock
и unlock
, я не ожидаю ошибки, но когда я запускаю этот модуль, ядро выдает следующие ошибки:
Ошибка обновлена:
[email protected]: # pppd call 80-2
[ 519.722190] PPP generic driver version 2.4.2
[email protected]:# [ 519.917390] BUG: scheduling while atomic: swapper/0/0/0x10000100
[ 519.940933] Modules linked in: ppp_async crc_ccitt ppp_generic slhc netfilter_mutex(P) nls_utf8 isofs udf crc_itu_t bnep rfcomm bluetooth rfkill vboxsf(O) vboxvideo(O) drm]
[ 520.022203] CPU 0
[ 520.023270] Modules linked in: ppp_async crc_ccitt ppp_generic slhc netfilter_mutex(P) nls_utf8 isofs udf crc_itu_t bnep rfcomm bluetooth rfkill vboxsf(O) vboxvideo(O) drm]
[ 520.087002]
[ 520.088001] Pid: 0, comm: swapper/0 Tainted: P O 3.2.51 #3 innotek GmbH VirtualBox/VirtualBox
[ 520.130047] RIP: 0010:[<ffffffff8102d17d>] [<ffffffff8102d17d>] native_safe_halt+0x6/0x8
[ 520.135010] RSP: 0018:ffffffff81601ee8 EFLAGS: 00000246
[ 520.140999] RAX: 0000000000000000 RBX: ffffffff810a4cfa RCX: ffffffffffffffbe
[ 520.145972] RDX: 0000000000000001 RSI: 0000000000000000 RDI: 0000000000000001
[ 520.158759] RBP: ffffffff81601ee8 R08: 0000000000000000 R09: 0000000000000000
[ 520.163392] R10: 0000000000000400 R11: ffff88003fc13680 R12: 0000000000014040
[ 520.172784] R13: ffff88003fc14040 R14: ffffffff81067fd2 R15: ffffffff81601e58
[ 520.177767] FS: 0000000000000000(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
[ 520.188208] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[ 520.196486] CR2: 00007fff961a3f40 CR3: 0000000001605000 CR4: 00000000000006f0
[ 520.201437] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 520.212332] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 520.217155] Process swapper/0 (pid: 0, threadinfo ffffffff81600000, task ffffffff8160d020)
[ 520.228706] Stack:
[ 520.234394] ffffffff81601ef8
Message from [email protected]i at Dec 22 17:45:46 ...
kernel:[ 520.228706] Stack:
ffffffff81014857 ffffffff81601f28 ffffffff8100d2a3
[ 520.255069] ffffffffffffffff 0d64eb669fae50fc ffffffff81601f28 0000000000000000
[ 520.269238] ffffffff81601f38 ffffffff81358c39 ffffffff81601f78 ffffffff816acb8a
[ 520.274148] Call Trace:
[ 520.275573] [<ffffffff81014857>] default_idle+0x49/0x81
[ 520.278985] [<ffffffff8100d2a3>] cpu_idle+0xbc/0xcf
[ 520.291491] [<ffffffff81358c39>] rest_init+0x6d/0x6f
вот полная ошибка syslog: http://paste.ubuntu.com/6617614/
Ответы
Ответ 1
Это крючок внутри ядра. Спящий режим, блокировка семафора (перемотка) или любые блокирующие операции не допускаются; Вы блокируете ядро!
Если вам нужен объект синхронизации, вы можете попытаться использовать блокировки спина.
Как этот ответ на аналогичный вопрос, mutex_lock будет запускать планировщик; Но ядро будет озадачено, потому что вы пытаетесь планировать другую задачу, в то время как вы находитесь в критическом разделе (сам обратный вызов является большой критической секцией).
Отметьте эту тему Понимание контекста исполнения ссылок netfilter для аналогичного случая.
Ответ 2
Даже если mutex_lock()
, вероятно, не будет спать в этом случае, он все равно может спать.
Поскольку это вызвано в атомном контексте, ошибка возникает.
В частности, это вызвано mutex_lock()
вызовом might_sleep()
, который, в свою очередь, может вызвать __schedule()
Если вам нужно синхронизировать, используйте соответствующие вызовы, например. спин-блоки и rcu.
Ответ 3
Вы видите это сообщение, если ваша задача запланирована, когда она содержит синхронизацию, скорее всего, спин-блокировку.
Когда вы блокируете спин-блокировку, она увеличивает preempt_count; когда планировщик отталкивает ситуацию планирования с увеличенным значением preempt_count, он выводит именно это сообщение:
/*
* Планирование печати при атомарной ошибке:
*/
static noinline void __schedule_bug(struct task_struct *prev)
{
if (oops_in_progress)
return;
printk(KERN_ERR "BUG: scheduling while atomic: %s/%d/0x%08x\n",
prev->comm, prev->pid, preempt_count());
debug_show_held_locks(prev);
print_modules();
if (irqs_disabled())
print_irqtrace_events(prev);
dump_stack();
}
Итак, возможно, вы держите блокировку или вы должны разблокировать блокировку.
PS. Из описания мьютекса в документации Linux:
- Семантика
-
'struct mutex' четко определена и применяется, если включен CONFIG_DEBUG_MUTEXES. Семафоры, с другой стороны, имеют практически нет кода отладки или инструментария. Подсистема мьютекса
проверяет и применяет следующие правила:
-
- только одна задача может удерживать мьютекс за раз * - только владелец может разблокировать мьютекс * - множественные разблокировки не разрешены
-
- рекурсивная блокировка не разрешена * - объект mutex должен быть инициализирован через API * - объект mutex не должен быть инициализирован через memset или копирование * - задача не может выйти с сохраненным мьютексом * - области памяти, в которых хранятся удерживаемые замки, не должны быть освобождены * - проведены мьютексы не должны быть повторно инициализированы * - мьютексы не могут использоваться в аппаратные или программные прерывания * контексты, такие как задачи и таймеры
В вашем дизайне один и тот же мьютекс можно было использовать несколько раз одновременно:
- вызов 1 → код → mutex_lock
- планировщик прерывает ваш код.
- вызов 2 → ваш код → mutex_lock (уже заблокирован) → ОШИБКА
Удачи.
Ответ 4
Это явно проблема контекста выполнения. Чтобы использовать соответствующую блокировку в коде ядра, необходимо знать, в каком контексте выполнения (hard_irq
| bottom_half
| process_context
) код вызывается.
mutex_lock
| mutex_unlock
предназначены исключительно для защиты кода process_context.
согласно http://www.gossamer-threads.com/lists/iptables/devel/56853
ваша функция hook_func
может быть вызвана в sot_irq
или process_context
.
Таким образом, вам необходимо использовать механизм блокировки, подходящий для защиты между этими двумя контекстами.
Я предлагаю вам пройти руководство по блокировке ядра (https://www.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/). В руководстве также объясняются особенности, связанные с тем, что система SMP (очень распространенная) и приостановка включена.
для быстрого тестирования вы можете использовать блокировку spin_lock_irqsave
в hook_func
. spin_lock_irqsave
всегда безопасен, он отключает прерывания на текущем процессоре, а spinlock гарантирует работу атома в SMP-системе.
Теперь, слово о сбое:
mutex_lock
| mutex_unlock
может использоваться только в коде process_context. Когда ваш hook_func
получает вызов из soft_irq
, mutex_lock
заставляет текущий процесс спать и, в свою очередь, вызывает планировщик. Спящий код ядра не разрешен в атомном контексте (здесь это soft_irq
).