Реальные варианты использования барьеров (DSB, DMB, ISB) в ARM

Я понимаю, что DSB, DMB и ISB являются барьерами для предотвращения изменения порядка команд. Я также могу найти много очень хороших объяснений для каждого из них, но довольно сложно представить, что я должен их использовать.

Кроме того, из открытых исходных кодов я время от времени вижу эти барьеры, но довольно сложно понять, почему они используются. Для примера: в ядре Linux 3.7 с функцией tcp_rcv_synsent_state_process есть следующая строка:

    if (unlikely(po->origdev))
            sll->sll_ifindex = orig_dev->ifindex;
    else
            sll->sll_ifindex = dev->ifindex;

    smp_mb();

    if (po->tp_version <= TPACKET_V2)
            __packet_set_status(po, h.raw, status);

где smp_mb() в основном DMB. Не могли бы вы привести примеры из жизни? Это поможет понять больше о барьерах.

3 ответа

Решение

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

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

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

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

Пол МакКенни (Paul McKenney) написал отличную статью о необходимости барьеров памяти и о том, какие эффекты они на самом деле оказывают на процессор: барьеры памяти: аппаратное представление для хакеров программного обеспечения

Если это слишком хардкорно, я написал серию блогов из 3-х частей, которая немного более легковесна и завершается представлением, специфичным для ARM. Первая часть - порядок доступа к памяти - введение.

Но если это конкретно список примеров, за которыми вы следите, особенно для архитектуры ARM, вы могли бы сделать намного хуже, чем Barrier Litmus Tests и Cookbook.

Сверхлегкий взгляд программиста и не совсем архитектурно правильная версия:

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

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

Он очень часто используется в DMA-переводах. Простые структуры управления DMA могут выглядеть так:

struct dma_control {
  u32 owner;
  void * data;
  u32 len;
};

Для владельца обычно задается что-то вроде OWNER_CPU или OWNER_HARDWARE, чтобы указать, кому из двух участников разрешено работать со структурой.

Код, который меняет это, обычно так

dma->data = data;
dma->len  = length;
smp_mb();
dma->owner = OWNER_HARDWARE;

Таким образом, данные всегда устанавливаются перед передачей права собственности на аппаратное обеспечение DMA. В противном случае движок может получить устаревшие данные, такие как указатель или длина, которые не были обновлены, поскольку ЦП переупорядочил доступ к памяти.

То же самое касается процессов или потоков, работающих на разных ядрах. Они могут общаться подобным образом.

Одним из простых примеров барьерного требования является спин-блокировка. Если вы реализуете спин-блокировку с использованием сравнения и замены (или LDREX/STREX в ARM) и без барьера, процессору разрешается умозрительно загружать значения из памяти и лениво сохранять вычисленные значения в памяти, и ни одно из них не требуется, чтобы происходило в порядке загрузки / сохранения в потоке команд.

В частности, DMB предотвращает переупорядочение доступа к памяти вокруг DMB. Без DMB процессор мог бы переупорядочить хранилище в память, защищенную спин-блокировкой, после освобождения спин-блокировки. Или процессор может считывать память, защищенную спин-блокировкой, до того, как спин-блокировка была фактически заблокирована или когда она была заблокирована другим контекстом.

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

Другие вопросы по тегам