2011-03-25 4 views
1

Я разрабатываю драйвер устройства Linux, работающий на встроенном CPU. Этот драйвер устройства управляет некоторыми внешними устройствами. Внешнее оборудование имеет собственный DDR-контроллер и внешнюю DDR. Аппаратный DDR отображается на встроенном CPU через окно с подвижным ядром (поэтому я получил доступ к внешней DDR из драйвера Linux). Я использую ядро ​​Linux 2.6.33.Как предотвратить блокировку семафора, когда поток завершается с ошибкой шины

Мой драйвер использует sysfs для управления внешним оборудованием из пользовательского пространства. В качестве примера, внешнее оборудование генерирует счетчик сердечных сокращений, который увеличивает конкретный адрес во внешней DDR. Драйвер читает это, чтобы определить, работает ли внешнее оборудование.

Если внешний DDR работает некорректно, доступ к внешней DDR вызывает ошибку шины на встроенном CPU. Для защиты от одновременного многопоточного доступа драйвер использует семафор.

Теперь проблема. Если поток захватывает семафор, то завершается с ошибкой шины, семафор все еще заблокирован. Все последующие призывы захватить блок семафора неопределенно. Какие методы я могу использовать, чтобы избежать навешивания драйвера навсегда?

Функция Пример sysfs (упрощенно):

static ssize_t running_attr_show(struct device *dev, struct device_attribute *attr, char *buffer) 
{ 
    struct my_device * const my_dev = container_of(dev, struct my_device, dev); 
    int ret; 

    if(down_interruptible(&my_dev->sem)) 
    { 
     ret = -ERESTARTSYS; 
    } 
    else 
    { 
     u32 heartbeat; 
     int running; 

     // Following line could cause bus error 
     heartbeat = mwindow_get_reg(&my_dev->mwindow, HEARTBEAT_COUNTER_ADDR); 

     running = (heartbeat != my_dev->last_heartbeat) ? 1 : 0; 
     my_dev->last_heartbeat = heartbeat; 

     ret = sprintf(buffer, "%d\n", result); 

     /* unlock */ 
     up(&my_dev->sem); 
    } 

    return ret; 
} 

ответ

1

Вам нужно изменить mwindow_get_reg() и, возможно, архитектура обработчик ошибок, который вызывается на ошибки шины, так что mwindow_get_reg() может вернуть ошибку, а не выключая обработать.

Вы можете обработать эту ошибку изящно, освободив семафор и вернув ошибку в пользовательское пространство.

+0

спасибо. Конечно, что-то рассмотреть. Есть ли у вас предложения по тому, как обработчик исключений может знать, что причиной ошибки шины был вызов от mwindow_get_reg? У нас уже есть код, который делает некоторое исследование, которое полагается на статическую переменную, установленную перед доступом, обнаруженную во время сбоя, чтобы можно было установить другой статический элемент, а затем проверить после доступа. Прерывания отключены в течение всего процесса. Это не кажется очень элегантным и означало бы, что прерывания были отключены в течение длительного времени при копировании большого количества прошивки в целевую DDR через окно памяти. – qbert220

+0

@ qbert220: Посмотрите на реализацию вашей архитектуры 'copy_from_user()' - эта функция обязательно устанавливает вещи так, чтобы неустранимые ошибки страницы возвращали «EFAULT» вызывающему абоненту, а не вызывали панику (вам также нужно будет увидеть как обработчик ошибок страницы выполняет свою часть задания). – caf

+0

Это довольно простая архитектура. Реализация copy_to_user вызывает access_ok (который проверяет адрес в ожидаемом диапазоне) и возвращает -EFAULT, если это возвращает false. Во всех остальных случаях он вызывает memcpy и возвращает количество байтов, которые были запрошены для копирования (возвращаемое значение memcpy игнорируется). – qbert220

1

Благодаря @caf, вот решение, которое я реализовал.

Я преобразовал часть mwindow_get_reg в сборку. Для возможного считывания ошибок я добавил запись в раздел ex_table с адресом ошибки и адресом исправления. Это приводит к тому, что обработчик исключений переходит к коду исправления, а не к завершению потока, если исключение происходит по этому адресу. Fixup ассемблер устанавливает «поврежденной» флаг, который я могу затем проверить и в моем коде на Си:

unsigned long ret = 0; 
int faulted; 

asm volatile(
     " 1:  lwi  %0, %2, 0;   "  // ret = *window_addr 
     " 2:  addik %1, r0, 0;   "  // faulted = 0 
     " 3:         " 
     "   .section .fixup, \"ax\"; "  // fixup code executed if exception occurs 
     " 4:  brid 3b;    "  // jump to next line of c code 
     "   addik %1, r0, 1;   "  // faulted = 1 (in delay slot) 
     "   .previous;     " 
     "   .section __ex_table,\"a\"; " 
     "   .word 1b,4b;    "  // ex_table entry. Gives fault address and jump address if fault occurs 
     "   .previous;     " 
      : "=r" (ret), "=r" (faulted)    // output registers 
      : "r" (window_addr)      // input registers 
); 

if (faulted) 
{ 
    printk(KERN_ERROR "%s: %s: FAULTED!", MODNAME, __FUNCTION__); 
    ret = 0xdeadbeef; 
} 

Я также должен был изменить свой обработчик исключений Dbus, добавив следующее:

const struct exception_table_entry *fixup; 
fixup = search_exception_tables(regs->pc); 
if (fixup) { 
    printk(KERN_ERROR "DBUS exception: calling fixup\n"); 
    regs->pc = fixup->fixup; 
    return; 
} 
Смежные вопросы