Почему этот код тупик?
Я создал 2 потока ядра Linux в своем загружаемом модуле и привязал их к отдельным ядрам процессора, работающим на двухъядерном устройстве Android. После того, как я запустил это несколько раз, я заметил, что устройство перезагружается с сбросом сторожевого таймера HW. Я ударил вопрос последовательно. Что может быть причиной тупика?
По сути, мне нужно сделать так, чтобы оба потока одновременно выполняли do_something() на разных ядрах, чтобы никто не крал циклы процессора (т. Е. Прерывания отключены). Я использую спинлок и переменную переменную для этого. У меня также есть семафор для родительского потока для ожидания дочернего потока.
#define CPU_COUNT 2
/* Globals */
spinlock_t lock;
struct semaphore sem;
volatile unsigned long count;
/* Thread util function for binding the thread to CPU*/
struct task_struct* thread_init(kthread_fn fn, void* data, int cpu)
{
struct task_struct *ts;
ts=kthread_create(fn, data, "per_cpu_thread");
kthread_bind(ts, cpu);
if (!IS_ERR(ts)) {
wake_up_process(ts);
}
else {
ERR("Failed to bind thread to CPU %d\n", cpu);
}
return ts;
}
/* Sync both threads */
void thread_sync()
{
spin_lock(&lock);
++count;
spin_unlock(&lock);
while (count != CPU_COUNT);
}
void do_something()
{
}
/* Child thread */
int per_cpu_thread_fn(void* data)
{
int i = 0;
unsigned long flags = 0;
int cpu = smp_processor_id();
DBG("per_cpu_thread entering (cpu:%d)...\n", cpu);
/* Disable local interrupts */
local_irq_save(flags);
/* sync threads */
thread_sync();
/* Do something */
do_something();
/* Enable interrupts */
local_irq_restore(flags);
/* Notify parent about exit */
up(&sem);
DBG("per_cpu_thread exiting (cpu:%d)...\n", cpu);
return value;
}
/* Main thread */
int main_thread()
{
int cpuB;
int cpu = smp_processor_id();
unsigned long flags = 0;
DBG("main thread running (cpu:%d)...\n", cpu);
/* Init globals*/
sema_init(&sem, 0);
spin_lock_init(&lock);
count = 0;
/* Launch child thread and bind to the other CPU core */
if (cpu == 0) cpuB = 1; else cpuB = 0;
thread_init(per_cpu_thread_fn, NULL, cpuB);
/* Disable local interrupts */
local_irq_save(flags);
/* thread sync */
thread_sync();
/* Do something here */
do_something();
/* Enable interrupts */
local_irq_restore(flags);
/* Wait for child to join */
DBG("main thread waiting for all child threads to finish ...\n");
down_interruptible(&sem);
}
1 ответ
Я не уверен, это реальная причина, но ваш код содержит некоторые серьезные ошибки.
Сначала в while (count != CPU_COUNT);
, Вы не должны читать переменную общего доступа без удержания блокировки, если только чтение не является атомарным. С count
это не гарантировано.
Вы должны защищать от чтения count
с замком. Вы можете заменить цикл while следующим:
unsigned long local_count;
do {
spin_lock(&lock);
local_count = count;
spin_unlock(&lock);
} while (local_count != CPU_COUNT);
В качестве альтернативы вы можете использовать атомарные типы. Обратите внимание на отсутствие блокировки
atomic_t count = ATOMIC_INIT(0);
...
void thread_sync() {
atomic_inc(&count);
while (atomic_read(&count) != CPU_COUNT);
}
Вторая проблема с прерываниями. Я думаю, вы не понимаете, что делаете.
local_irq_save()
сохраняет и отключает прерывания. Затем вы отключаете прерывания снова с local_irq_disable()
, После некоторой работы вы восстанавливаете предыдущее состояние с помощью local_irq_restore()
и разрешить прерывания с local_irq_enable()
, Это включение совершенно неправильно. Вы разрешаете прерывания независимо от их предыдущего состояния.
Третья проблема Если основной поток не связан с процессором, вы не должны использовать smp_processor_id()
если вы не уверены, что ядро не будет перепланировано сразу после того, как вы получите номер процессора. Лучше использовать get_cpu()
, который отключает выгрузку ядра, а затем возвращает идентификатор процессора. Когда закончите, позвоните put_cpu()
,
Но когда вы звоните get_cpu()
, это ошибка для создания и запуска других потоков. Вот почему вы должны установить сродство основного потока.
В-четвертыхlocal_irq_save()
а также local_irq_restore()
макросы, которые принимают переменную, а не указатель на unsigned long
, (У меня есть ошибка и некоторые предупреждения, передающие указатели. Интересно, как вы скомпилировали свой код). Удалить ссылку
Окончательный код доступен здесь: http://pastebin.com/Ven6wqWf