Как заставить resched после прерывания в ядре Linux? - PullRequest
2 голосов
/ 04 ноября 2011

Я играю с kretprobes и столкнулся с проблемой. Я хотел бы, чтобы в ответ на определенные события пользовательского процесса (например, определенные системные вызовы) считывал данные из адресного пространства этого процесса. Поскольку в обработчике ввода kretprobe мы находимся в контексте прерывания, я не могу получить страницы пользователя отсюда (это может спать), поэтому я откладываю работу в system_rq (schedule_work ()).

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

Что еще нужно сделать, чтобы переключение задач произошло сразу после iret?

Заранее спасибо

Ответы [ 2 ]

1 голос
/ 13 января 2012

Ну, я обнаружил сегодня, что это действительно хороший способ сделать это. Проблема, с которой я столкнулся при выполнении процесса, заключалась в том, что я не был в контексте прерывания: kprobe был оптимизирован (то есть инструкция jmp вместо int3 на x86), в результате чего мой код выполнялся в пользовательском контексте в среде ядра. Это должно было бы быть выполнено гладко, если бы функция kprobe_optimized () работала правильно, и в этом случае мы можем вызвать schedule () непосредственно после установки для задачи значения INTERRUPTIBLE вместо того, чтобы возвращать и позволять прологу обработчика прерываний проверять флаг TIF_NEED_RESCHED. Действительно, kprobe_optimized() возвращает false в любом случае, если это kretprobe, что связано с тем, как kretprobe обрабатывается внутри: он использует агрегатор kprobes, который для оптимизатора установлен правильно для агрегатора, но не для kprobes в списке. Я обошел это, экспортировав функцию get_kprobe() и используя ее для получения адреса агрегатора kprobe, из которого я наконец смог правильно проверить, оптимизирован он или нет.

Я думаю, что лучший способ (с точки зрения производительности) исправить это в ядре - реплицировать оптимизированный флаг из агрегатора на каждый kprobe, который он перечисляет. Таким образом, kprobe_optimized() вернет правильное значение. Другой способ сделать это - добавить больше кода в kprobe_optimized(), чтобы проверить, является ли этот kprobe частью списка агрегаторов, и проверить агрегатор, а не фактический kprobe.

В любом случае, это было весело!

1 голос
/ 04 ноября 2011

Ваш подход в корне ошибочен. Прерывания носят асинхронный характер; даже если вы закроете гонку между возвратом прерывания и отложенным рабочим элементом, вы можете получить ту же гонку, если прерывание задержано. Рассмотрим:

  • ПОЛЬЗОВАТЕЛЬ: установить X = 1
  • ПРЕРЫВАНИЕ: График работы
  • ПОЛЬЗОВАТЕЛЬ: установить X = 2
  • ЧАСТЬ РАБОТЫ: Читать X

против

  • ПОЛЬЗОВАТЕЛЬ: установить X = 1
  • ПРЕРЫВАНИЕ: (задерживается из-за аппаратной странности ...)
  • ПОЛЬЗОВАТЕЛЬ: установить X = 2
  • ПРЕРЫВАНИЕ: График работы
  • РАБОЧАЯ ОЧЕРЕДЬ: Читать X

Тот же результат, нет? Так что даже не пытайтесь.

Более того, прерывания могут возникать даже в коде ядра. Если приложение находится в середине системного вызова, который не блокирует и не изменяет память, оно должно завершить этот вызов, прежде чем оно позволит вам заблокировать. Заставить его прерваться в состояние, угрожающее тупиками; код ядра может содержать спин-блокировку или иным образом не находиться в безопасном состоянии для планирования.

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

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

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

...