LinuxCon Europe 2013 - О поиске "гонок" в ядре Linux

Материал из Rosalab Wiki
Перейти к: навигация, поиск
м
Строка 1: Строка 1:
 
<!-- Смело пишите здесь свою заметку. Можно использовать все возможности вики, включать картинки и другие статьи — все они автоматически отреплицируются наружу -->
 
<!-- Смело пишите здесь свою заметку. Можно использовать все возможности вики, включать картинки и другие статьи — все они автоматически отреплицируются наружу -->
  
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.
+
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:
 
* и т.д.
 
* и т.д.
  
Слайды к этому выступлению и пояснения к ним доступны на [http://code.google.com/p/kernel-strider/ странице проекта], в разделе «Talks and Slides».
+
В обсуждении 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 (слайды, пояснения к слайдам).

LinuxConEurope2013 Races.jpg
По одному из определений, data race - это такая ситуация, когда два или более потока выполнения (threads) одновременно обращаются к одной и той же области памяти и хотя бы один из этих потоков что-то записывает в эту область памяти.

Такие ситуации далеко не всегда просто выявить, а последствия у них могут быть самыми разными, от незначительных до критических. Для ядра 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 (самое первое в списке).

Войдите, чтобы комментировать.