Поискал в интернете - может кто уже сталкивался? Множество пользователей вебкамер с драйвером spca5xx сталкивались с этой проблемой. Кто-то советует не дергать камеру. Кто-то винист усб мост, хост или еще что-то. Автор драйвера вообще заявил, что драйвер написал для работы на мощьных компьютерах и собственно все у нас ok. Ближе всех к истине оказался вот этот пост. Но предложенное им решение (править код ядра линуха) это скорее лечение симптомов, а не устранение причины.
В чем суть проблемы. Есть у драйвера spca5xx такслет - это такая задача, которая шедулится из обработчика прерывания на этап окончания работы прерывания, тоесть прерывание побыстрому отработало, и запускается тасклет который может что-то делать продолжительное время (при этом другие прерывания не задисаблены). У каждого тасклета есть флаг состояния TASKLET_STATE_SCHED - если этот флаг включен, то считается, что тасклет еще не выполнялся, а только планируется к выполнению. Проблема возникла в том коде ядра, который активирует выполнение зашедуленного тасклета. Перед выполнением, ядро проверяет состояние этого флага, и если флаг включен, то сбрасывает его и выполняет код задачи. А вот если вдруг этот флаг выключен, но почему-то этот тасклет в очереди, то ядро выполняет функцию BUG() - тоесть распечатывает содержимое стека и вешается. Собственно, автор того поста так и предложил сделать - убрать вызов ф-ии BUG(). Если задуматься, почему к моменту выполнения тасклет оказывает без флага TASKLET_STATE_SCHED, можно предположить:
1. Бага в ядре - маловероятно.
2. Кто-то помимо ядра этот флаг сбрасывает. Проверка показала, что в драйверах spca5xx строка TASKLET_STATE_SCHED вообще отсутствует.
3. Память под тасклет выделяется один раз. Во время работы тасклета возникает прерывание и оно реинциализирует структуру тасклета (решедулит тасклет). После прерывания происходит возврат к тасклету, он оканчивает работу и сбрасывает флаг. А так как тасклет зашедулен в очереди, то к моменту его выполнения флаг оказывается сброшенным. Проверка показала, что это не так. Да и флаг сбрасывается до работы такслета, а не после.
4. Надо смотреть код.
В драйверах spca5xx есть всего одно место где шедулится тасклет. И сразу перед вызовом функции tasklet_schedule стоит функция tasklet_init. Все это обернуто циклом по пакетами полученным от usb драйвера и условием при котором шедулится тасклет: если найден конец фрейма (оноже начало следующего). Получается, что если обнаружен фрейм, то структура тасклета инициализируется и шедулится. А если обнаружено два фрейма? Вот она и бага. Если обнаружено два фрейма, то в ядре в очереди тасклетов окажется две записи которые ссылаются на один и тот же участок памяти.
Как пофиксить:
1. Поставить проверку перед инитом. Если уже зашедулено, то игнорируем и не шедулим больше. Минусы - фрейм теряется. Плюсы - просто.
2. Выделять память под тасклет динамически, в тасклет параметром передавать указатель на структуру в которой указатель на структуру тасклета и фрейм для декодирования. После декодирования память такслета очищать. Минусы - потеря производительности, муторно вообще. Плюсы - фреймы не теряются, правильное решение.
3. Замутить какую-нибудь очередь или пулл.. лениво.
Вобщем выбрал я первый вариант. И так сойдет %). Написал письмо в рассылку с патчем и обьяснениями на ломоном английском. Кому надо сам доделает нормально (open source модель такая вот).
А люблю я все-таки причины ошибок искать. Докапываться до сути... Такой процесс интересный. Расследование.
Комментариев нет:
Отправить комментарий