LinuxCon Europe 2013 - О поиске "гонок" в ядре Linux
м |
|||
Строка 1: | Строка 1: | ||
<!-- Смело пишите здесь свою заметку. Можно использовать все возможности вики, включать картинки и другие статьи — все они автоматически отреплицируются наружу --> | <!-- Смело пишите здесь свою заметку. Можно использовать все возможности вики, включать картинки и другие статьи — все они автоматически отреплицируются наружу --> | ||
− | 22 октября состоялось [http://linuxconcloudopeneu2013.sched.org/event/918f111663374602fe344259b188d807 выступление] нашего сотрудника Евгения Шатохина на конференции LinuxCon Europe 2013. Речь шла о средствах поиска таких трудноуловимых ошибок, как [http://ru.wikipedia.org/wiki/Race_condition «состояния гонки»] | + | 22 октября состоялось [http://linuxconcloudopeneu2013.sched.org/event/918f111663374602fe344259b188d807 выступление] нашего сотрудника Евгения Шатохина на конференции LinuxCon Europe 2013. Речь шла о средствах поиска таких трудноуловимых ошибок, как [http://ru.wikipedia.org/wiki/Race_condition «состояния гонки»], они же [http://en.wikipedia.org/wiki/Race_condition «data races»] в компонентах ядра Linux ([http://cdn.2safe.com/153759033759/LinuxCon_2013-Shatokhin-v03.pdf слайды], [http://cdn.2safe.com/200700033759/speaker_notes.odt пояснения к слайдам]). |
− | По одному из определений, ''data race'' - это такая ситуация, когда два или более потока выполнения (threads) одновременно обращаются к одной и той же области памяти и хотя бы один из этих потоков что-то записывает в эту область памяти. | + | [[File:LinuxConEurope2013_Races.jpg|right|384px]] По одному из определений, ''data race'' - это такая ситуация, когда два или более потока выполнения (threads) одновременно обращаются к одной и той же области памяти и хотя бы один из этих потоков что-то записывает в эту область памяти. |
Такие ситуации далеко не всегда просто выявить, а последствия у них могут быть самыми разными, от незначительных до критических. Для ядра Linux это особенно актуально: код драйверов, например, может выполняться многими потоками одновременно. Добавим к этому обработку прерываний и других асинхронных событий, а также учтём, что правила синхронизации доступа к общим данным из разных потоков далеко не всегда описаны чётко (а нередко - не описаны вообще)... | Такие ситуации далеко не всегда просто выявить, а последствия у них могут быть самыми разными, от незначительных до критических. Для ядра Linux это особенно актуально: код драйверов, например, может выполняться многими потоками одновременно. Добавим к этому обработку прерываний и других асинхронных событий, а также учтём, что правила синхронизации доступа к общим данным из разных потоков далеко не всегда описаны чётко (а нередко - не описаны вообще)... | ||
− | Об инструментах, позволяющих выявлять data races в компонентах ядра Linux, в основном, и шла речь в выступлении. Наиболее подробно - о системах [http://code.google.com/p/kernel-strider/ KernelStrider] и [https://github.com/winnukem/racehound RaceHound], | + | Об инструментах, позволяющих выявлять data races в компонентах ядра Linux, в основном, и шла речь в выступлении. Наиболее подробно - о системах [http://code.google.com/p/kernel-strider/ KernelStrider] и [https://github.com/winnukem/racehound RaceHound], одним из основных разработчиков которых Евгений является. |
KernelStrider собирает информацию об анализируемом компоненте ядра (например, драйвере) в процессе работы этого компонента. Информация об обращениях к памяти, выделении и освобождении памяти, блокировках и т.д. затем, уже в user space, анализируется инструментом [http://code.google.com/p/data-race-test/ ThreadSanitizer] (Google). Алгоритм поиска races коротко описан [http://code.google.com/p/data-race-test/wiki/ThreadSanitizerAlgorithm тут]. | KernelStrider собирает информацию об анализируемом компоненте ядра (например, драйвере) в процессе работы этого компонента. Информация об обращениях к памяти, выделении и освобождении памяти, блокировках и т.д. затем, уже в user space, анализируется инструментом [http://code.google.com/p/data-race-test/ ThreadSanitizer] (Google). Алгоритм поиска races коротко описан [http://code.google.com/p/data-race-test/wiki/ThreadSanitizerAlgorithm тут]. | ||
Строка 31: | Строка 31: | ||
* и т.д. | * и т.д. | ||
− | + | В обсуждении data races, найденных указанными выше инструментами, активно участвовали сотрудники Intel, что немудрено: речь шла о [http://sourceforge.net/mailarchive/message.php?msg_id=31245543 races в сетевом драйвере e1000], разработанном как раз в этой компании. Во время обсуждения выяснился интересный факт: для обращений к памяти в некоторых частях сетевых драйверов средства синхронизации использовать почему-то не принято, хотя там из-за этого могут быть "гонки" (они там и обнаружились). Это, в частности, относится к NAPI и функциям драйвера, участвующим в передаче данных по сети. Вроде бы всё так делается, чтобы избежать потерь производительности из-за блокировок, но ни оценок этих потерь, ни конкретных рекомендаций, как при этом избежать проблем из-за "гонок", пока найти не удалось. | |
+ | |||
+ | В целом, похоже, что отношение к data races у многих разработчиков ядра такое: | ||
+ | |||
+ | <blockquote> | ||
+ | - Что-то из-за этой data race упало или стало работать неправильно? | ||
+ | |||
+ | - Пока не замечали. | ||
+ | |||
+ | - А, ну, ладно. | ||
+ | </blockquote> | ||
+ | |||
+ | И на этом разговор кончается. | ||
+ | |||
+ | Логично? Вроде бы да, но, если вспомнить, например, [https://www.usenix.org/legacy/event/hotpar11/tech/final_files/Boehm.pdf вот эту статью], всё уже не так очевидно. | ||
<!-- | <!-- | ||
Строка 47: | Строка 61: | ||
[[Category:ToROSAPoint]] | [[Category:ToROSAPoint]] | ||
+ | {{wl-publish: 2013-10-29 13:15:07 +0400 | Eugene.shatokhin }} |
Версия 12:15, 29 октября 2013
22 октября состоялось выступление нашего сотрудника Евгения Шатохина на конференции LinuxCon Europe 2013. Речь шла о средствах поиска таких трудноуловимых ошибок, как «состояния гонки», они же «data races» в компонентах ядра Linux (слайды, пояснения к слайдам).
Такие ситуации далеко не всегда просто выявить, а последствия у них могут быть самыми разными, от незначительных до критических. Для ядра Linux это особенно актуально: код драйверов, например, может выполняться многими потоками одновременно. Добавим к этому обработку прерываний и других асинхронных событий, а также учтём, что правила синхронизации доступа к общим данным из разных потоков далеко не всегда описаны чётко (а нередко - не описаны вообще)...
Об инструментах, позволяющих выявлять data races в компонентах ядра Linux, в основном, и шла речь в выступлении. Наиболее подробно - о системах KernelStrider и RaceHound, одним из основных разработчиков которых Евгений является.
KernelStrider собирает информацию об анализируемом компоненте ядра (например, драйвере) в процессе работы этого компонента. Информация об обращениях к памяти, выделении и освобождении памяти, блокировках и т.д. затем, уже в user space, анализируется инструментом ThreadSanitizer (Google). Алгоритм поиска races коротко описан тут.
KernelStrider может в некоторых случаях выдавать сообщения о data races там, где data races нет («false positives»). Например, при анализе сетевых драйверов такое было в случаях, когда драйвер отключал генерацию прерываний соотв. устройством и затем обращался к каким-то общим данным, не опасаясь конфликтов с функцией-обработчиком прерываний.
Система RaceHound позволяет проверить результаты, полученные с помощью KernelStrider, найти среди них те, которые, действительно, говорят о data races. RaceHound работает так.
- На инструкцию в коде ядра, которая может быть «замешана» в data race, ставится программная точка прерывания («software breakpoint»).
- Когда эта точка прерывания срабатывает, RaceHound определяет адрес области памяти, куда эта инструкция обратится, и ставит аппаратную точку прерывания («hardware breakpoint»), чтобы отследить обращения нужного вида к этой области (только запись или произвольные обращения).
- Делается небольшая задержка перед выполнением интересующей инструкции.
- Если во время задержки какой-то другой поток обратится к указанной области памяти, аппаратная точка прерывания сработает и RaceHound сообщит о найденной data race.
Т.е. при анализе компонентов ядра KernelStrider работает своего рода «детективом-аналитиком», сужая круг «подозреваемых» - мест в коде, которые могут участвовать в data races. RaceHound тогда - система слежки за этими подозреваемыми. Если ей удаётся поймать подозреваемого с поличным (т.е. при выполнении конфликтующих доступов к памяти) - всё ясно. Если не удаётся - это ничего не значит.
Как во время, так и после доклада, вопросов было довольно много. Слушателей интересовали, например, такие вещи, как:
- Планируется ли поддержка ARM (пока всё работает только на x86), - возможно, но не в ближайшем будущем.
- Может ли KernelStrider пропускать data races (т.е. возможны ли false negatives) - да, может в некоторых случаях, в основном, из-за особенностей алгоритма работы ThreadSanitizer и из-за неточностей используемых правил определения порядка событий.
- Переживает ли KernelStrider suspend/resume - да, переживает.
- Можно ли с помощью KernelStrider и RaceHound анализировать не только модули ядра, но и само ядро - пока нет.
- Можно ли в KernelStrider инструментировать код анализируемого драйвера не при загрузке этого драйвера, как сейчас, а при компиляции - очень вероятно; это одно из возможных направлений развития.
- и т.д.
В обсуждении data races, найденных указанными выше инструментами, активно участвовали сотрудники Intel, что немудрено: речь шла о races в сетевом драйвере e1000, разработанном как раз в этой компании. Во время обсуждения выяснился интересный факт: для обращений к памяти в некоторых частях сетевых драйверов средства синхронизации использовать почему-то не принято, хотя там из-за этого могут быть "гонки" (они там и обнаружились). Это, в частности, относится к NAPI и функциям драйвера, участвующим в передаче данных по сети. Вроде бы всё так делается, чтобы избежать потерь производительности из-за блокировок, но ни оценок этих потерь, ни конкретных рекомендаций, как при этом избежать проблем из-за "гонок", пока найти не удалось.
В целом, похоже, что отношение к data races у многих разработчиков ядра такое:
- Что-то из-за этой data race упало или стало работать неправильно?
- Пока не замечали.
- А, ну, ладно.
И на этом разговор кончается.
Логично? Вроде бы да, но, если вспомнить, например, вот эту статью, всё уже не так очевидно.
[ Хронологический вид ]Комментарии
Понятно что в сетевых никто и не будет сделать за этим - если гонки заложены в основу сетевых алгоритмов и лечатся банальным реконнектом, то смысл?
Речь идёт об обращениях к внутренним структурам драйвера, а тут всё не так очевидно. Например, в одном из случаев, о котором я писал интеловцам, были одновременные обращений к переменной, в которой хранится текущее количество пакетов в очереди передачи (Tx queue). Один thread читал эту переменную, другой в то же время увеличивал её значение на 1. Может, это и не приведёт в данном случае к неприятностям, но, как минимум, тут надо разобраться.
Вот описание этой "гонки", если интересно: http://sourceforge.net/mailarchive/message.php?msg_id=31452821 (самое первое в списке).
Войдите, чтобы комментировать.