Ядро: «Что может привести к тому, что чтение из памяти, которое считывалось все время, внезапно начнет вызывать «Неспособность обработать запрос подкачки ядра»?

#linux-kernel #kernel #kernel-module

Вопрос:

Я сталкиваюсь со странной проблемой с драйвером для контроллера Ethernet на пользовательской плате с ядром на основе 2.6.31.6. Ядро содержит множество модулей, предназначенных для этой платы SoC, которая была разработана в начале 2010-х годов в соответствии с NDA. Это чрезвычайно затрудняет обновление до более новых версий ядра, и я бы держался подальше от этой опции. Я много покопался в этой проблеме, и я был бы очень признателен за любую информацию, которую кто-нибудь мог бы дать.

Проблема

Сетевое устройство находится на специальной плате, используемой для передачи данных со скоростью примерно 20 Мбит / с 24/7. Иногда он может нормально работать в течение нескольких минут, иногда в течение нескольких дней. Однако, в конечном счете, это вызывает сбой ядра со следующим сообщением об ошибке:

  Unable to handle kernel paging request at virtual address fd40101c
 

Если я проанализирую трассировку стека, ПК и LR в ядре, я могу обнаружить, что проблема заключается в механизме опроса драйверов NAPI. Я могу подтвердить, что запрос подкачки выполняется методом, который устанавливает бит в регистре устройства, который включает физические прерывания.

Анализ

Когда это устройство получает физическое прерывание для обозначения наличия данных, в регистре устанавливается бит для отключения физических прерываний. Затем буферизованные данные, передаваемые в память через DMA, считываются до тех пор, пока больше данных не станет доступно. Когда больше нет доступных данных, он считывает регистр, содержащий конфигурацию для включения физических прерываний, устанавливает бит для повторного включения физических прерываний, а затем записывает значение в регистр. Проблема возникает во время чтения непосредственно перед повторным включением физических прерываний.

Дело в том, что память выделяется при инициализации драйвера и никогда не освобождается. В устройстве есть регистры размером 24х4 байта. Тот, который считывается, имеет смещение 0x101c, рассчитанное следующим образом:

 (dmaBase   (reg_num << 2)) -> (0x1000   (7 << 2)) == 0x101c.
 

Это соответствует считываемому адресу, который вызвал Oops, где базовый адрес был 0xfd400000, со смещением регистра 0x101c, в конечном итоге становится адресом 0xfd40101c.

Вопрос

Эта память действительно часто считывается и записывается, как я подтвердил, добавив printk в метод, который включил прерывания. Итак, почему это вызывает проблему сейчас, поскольку оно выделяется при инициализации драйвера и никогда не освобождается в течение жизненного цикла драйвера?

Что может привести к тому, что чтение памяти в ранее выделенный регистр, который все время считывался, внезапно начнет вызывать «Неспособность обработать запрос подкачки ядра»?

Заранее спасибо за любую информацию!

Комментарии:

1. Наиболее очевидной причиной было бы то, что по какой-то причине он был освобожден. Возможно ли это? Поскольку Linux является открытым исходным кодом, возможно, вы можете добавить BUG_ON в функцию освобождения, которая проверяет, что она не освобождает эту конкретную страницу.