Как я могу предотвратить блокировку семафора, когда поток завершается с ошибкой шины - PullRequest
1 голос
/ 25 марта 2011

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

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

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

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

Пример функции 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;
}

Ответы [ 2 ]

1 голос
/ 31 марта 2011

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

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

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;
}
1 голос
/ 28 марта 2011

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

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

...