Почему пустой критический раздел в ловушках netfilter возникает «ОШИБКА: планирование при атомарной ошибке»?

Я написал этот хук:

#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);
}

раздел инициализации:

  mutex_init(&queue_critical_section);
  mutex_init(&ioctl_critical_section);

Я определил статическую переменную:

static struct mutex queue_critical_section;

Поскольку между lock и unlock нет кода, я не ожидаю ошибки, но когда я запускаю этот модуль, ядро ​​​​выдает следующие ошибки:

Ошибка обновлена:

root@khajavi: # pppd call 80-2
[  519.722190] PPP generic driver version 2.4.2
root@khajavi:# [  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 syslogd@khajavi 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

вот полная ошибка системного журнала: http://paste.ubuntu.com/6617614/


person Milad Khajavi    schedule 30.11.2013    source источник
comment
да, я определил и инициализировал его.   -  person Milad Khajavi    schedule 30.11.2013
comment
Это сообщение об ошибке неполное.   -  person CL.    schedule 30.11.2013
comment
@КЛ. Я обновил сообщение об ошибке.   -  person Milad Khajavi    schedule 22.12.2013


Ответы (4)


Это хук изнутри ядра. Спящий режим, блокировка семафора (ожидание) или любые блокирующие операции не допускаются; Вы блокируете ядро!

Если вам нужен объект синхронизации, вы можете попробовать использовать спин-блокировки.

Как указано в этом ответе на аналогичный вопрос, mutex_lock вызовет планировщик; Но ядро ​​будет озадачено, потому что вы пытаетесь запланировать другую задачу, в то время как вы находитесь в критической секции (сам обратный вызов является большой критической секцией).

Проверьте эту тему Понимание контекста выполнения перехватчиков netfilter для аналогичного случая.

person Yousf    schedule 22.12.2013
comment
Если блокировка с помощью мьютекса в ядре не разрешена, то почему разработчики ядра создали API мьютекса внутри ядра? - person Milad Khajavi; 22.12.2013
comment
@Khajavi Mutexes разрешены в некоторых контекстах, но не в хуках netfilter. - person CL.; 24.12.2013

Несмотря на то, что mutex_lock() в этом случае, скорее всего, не заснет, он все же может заснуть. Поскольку это вызывается в атомарном контексте, возникает ошибка.

В частности, это вызвано тем, что mutex_lock() вызывает might_sleep(), который, в свою очередь, может вызывать __schedule()

Если вам нужна синхронизация, используйте соответствующие вызовы, например. спинлоки и rcu.

person Hasturkun    schedule 22.12.2013

Вы видите это сообщение, если ваша задача запланирована, когда она содержит синхронизацию, скорее всего, спин-блокировку. Когда вы блокируете спин-блокировку, она увеличивает 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 включен. С другой стороны, семафоры
    практически не имеют ни кода отладки, ни инструментария. Подсистема мьютекса
    проверяет и применяет следующие правила:

      • only one task can hold the mutex at a time * - only the owner can unlock the mutex * - multiple unlocks are not permitted
      • recursive locking is not permitted * - a mutex object must be initialized via the API * - a mutex object must not be initialized via memset or copying * - task may not exit with mutex held * - memory areas where held locks reside must not be freed * - held mutexes must not be reinitialized * - mutexes may not be used in hardware or software interrupt * contexts such as tasklets and timers

В вашем дизайне один и тот же мьютекс может использоваться несколько раз одновременно:

  1. вызов 1 -> ваш код -> mutex_lock
  2. планировщик прерывает ваш код.
  3. вызов 2 -> ваш код -> mutex_lock (уже заблокирован) -> ОШИБКА

Удачи.

person Sebastian Mountaniol    schedule 26.12.2013

Это явно проблема контекста выполнения. Чтобы использовать соответствующую блокировку в коде ядра, нужно знать, в каком контексте выполнения (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 всегда безопасен, он отключает прерывания на текущем процессоре, а спин-блокировка гарантирует атомарную работу в системе SMP.

Теперь немного о крушении:

mutex_lock | mutex_unlock можно использовать только в коде process_context. Когда ваш hook_func вызывается из soft_irq, mutex_lock переводит текущий процесс в спящий режим и, в свою очередь, вызывает планировщик. Засыпание в коде ядра не разрешено в атомарном контексте (здесь это soft_irq).

person kr99    schedule 27.12.2013