Разыменование указателя NULL в swiotlb_unmap_sg_attrs() на дисковом вводе-выводе

Я получаю сообщение об ошибке, которую действительно не понимаю, при чтении или записи файлов с использованием драйвера блочного устройства PCIe. Кажется, я столкнулся с проблемой в swiotlb_unmap_sg_attrs(), которая похоже, выполняет разыменование NULL указателя sg, но я не знаю, откуда это берется, поскольку единственный scatterlist, который я использую сам, выделяется как часть информационной структуры устройства и сохраняется до тех пор, пока это делает драйвер.

Существует stacktrace, чтобы разобраться с проблемой. Он имеет тенденцию немного различаться в точных деталях, но всегда дает сбой в swiotlb_unmap_sq_attrs().

Я думаю, что, вероятно, у меня проблема с блокировкой, так как я не знаю, как справиться с блокировкой функций ввода-вывода. Блокировка уже удерживается, когда вызывается функция request, я освобождаю ее до того, как будут вызваны сами функции ввода-вывода, поскольку для их завершения требуется (MSI) IRQ. Обработчик IRQ обновляет значение «статуса», которого ожидает функция ввода-вывода. Когда функция ввода-вывода возвращается, я снова снимаю блокировку и возвращаюсь к обработке очереди запросов.

Сбой происходит в blk_fetch_request() во время следующего:

if (!__blk_end_request(req, res, bytes)){
    printk(KERN_ERR "%s next request\n", DRIVER_NAME);

    req = blk_fetch_request(q);
} else {
    printk(KERN_ERR "%s same request\n", DRIVER_NAME);
}

где bytes обновляется обработчиком запроса как общая длина IO (суммарная длина каждого сегмента разброса-сбора).


person Inductiveload    schedule 04.12.2012    source источник


Ответы (1)


Оказалось, это произошло из-за повторного входа функции request. Поскольку я разблокировался в середине, чтобы разрешить поступление IRQ, функция request могла быть вызвана снова, взяла блокировку (пока первоначальный обработчик запросов ждал ввода-вывода), а затем не тот обработчик получил IRQ, и все пошло не так. со стеками неудачных операций ввода-вывода.

Способ, которым я решил это, заключался в том, чтобы установить флаг «занят» в начале функции запроса, очистить его в конце и немедленно вернуться в начале функции, если это установлено:

static void mydev_submit_req(struct request_queue *q){
    struct mydevice *dev = q->queuedata;

    // We are already processing a request
    // so reentrant calls can take a hike
    // They'll be back
    if (dev->has_request)
        return;

    // We own the IO now, new requests need to wait
    // Queue lock is held when this function is called
    // so no need for an atomic set
    dev->has_request = 1; 

    // Access request queue here, while queue lock is held

    spin_unlock_irq(q->queue_lock);

    // Perform IO here, with IRQs enabled
    // You can't access the queue or request here, make sure 
    // you got the info you need out before you release the lock

    spin_lock_irq(q->queue_lock);

    // you can end the requests as needed here, with the lock held

    // allow new requests to be processed after we return
    dev->has_request = 0;

    // lock is held when the function returns
}

Однако я до сих пор не уверен, почему я постоянно получал трассировку стека от swiotlb_unmap_sq_attrs().

person Inductiveload    schedule 13.12.2012