Невозможное возможно, или как использовать NEON в модуле ядра

ARMv7NEONШироко распространенным мнением является то, что работа с плавающей точкой в режиме ядра (модуле ядра) является невозможной. Строго говоря, работа с ними все же нежелательна по нескольким причинам, которые рассмотрим чуть ниже, но иногда из-за архитектурных просчетов или другим причинам необходимо обработать данные на уровне ядра. В статье рассмотрим простейший пример использования операций с плавающей точкой и сопроцессора NEON в модуле ядра Linux. Предполагается, что читатель знает основы создания модулей ядра.

Почему нельзя? Какие ограничения?

Общепринятый запрет на использование VPF и Neon в режиме ядра основывается на следующем ограничении. Из соображений производительности в режиме ядра при переключения контекста не происходит сохранения (и, соответственно, восстановления) содержимого регистров VPF/NEON, в связи с этим использование этого функционала сильно затруднено. Кроме того, использование чисел с плавающей точкой в режиме ядра сильно ограничено и по причине того, что большинство математических библиотек работают только в пространстве пользователя (в том числе math.h) и сколько-то нибудь сложная обработка данных в ядре затруднена.

Кроме того, при использовании накладывается еще несколько ограничений:

  • Код NEON/VFP не разрешен в контексте прерывания;
  • Код NEON/VFP не может использовать засыпание (sleep);
  • Код NEON/VFP выполняется с отключенной вытесняющей многозадачностью;
  • Код NEON/VFP должен быть изолирован от остального кода на уровне компиляции.

И, наконец, для использования рассматриваемого функционала необходимо явно активировать и отключать модули NEON/VFP функциями kernel_neon_begin()  и kernel_neon_end(), соответственно.

Создание модуля, выполняющего вычисления с плавающей точкой в NEON

В примере рассмотрен простейший модуль, производящий инкремент счетчика на 10 с помощью инструкции NEON при каждом чтении.

В связи с тем, что код NEON должен быть изолирован, необходима раздельная компиляция основного и вычислительного модуля. Поэтому структура проекта является следующей (Жирным выделены директории, курсивом — файлы):

  • TestMod
  • Makefile
    • NeonMod
      • Makefile
      • neon_operation.c
      • neon_operation.h
    • MainMod
      • Makefile
      • main_mod.c

Вычислительный модуль

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

В заголовочном файле neon_operation.h содержится только объявление функций, которые должны быть доступны извне. В нашем случае neon_add.

В файле neon_operation.c реализованы основные функции для реакции на загрузку/выгрузку модуля и, непосредственно, функция neon_add выполняющая инкремент операнда a на b с помощью инструкции NEON vadd_u64. 

В строке 18 вызывается инструкция NEON.

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

Сборка модуля

Основная особенность сборки такого модуля содержится в Makefile:

Обратите внимание: в строке 2 необходимо подставить ваш путь до исходников ядра.

Основным отличием от обычной сборки модуля является наличие дополнительных флагов описанных в строке 6.

Основной модуль

Функционал основного не должен вызывать вопросов, поэтому не будет подробно его рассматривать, однако рассмотрим интересующий нас кусок кода, отвечающий за вызов функции с операцией NEON:

Тут в строке 2 импортируется необходимый для работы с neon заголовочный файл, содержащий функционал активации и отключения, в строке 3 импортируется заголовочный файл нашей библиотеки, выполняющей функции вычисления.

В строках 12 и 14 производится активация и отключение блока NEON/VFP, а в строке 13 вызывается непосредственно функция вычисления.

Полностью код модуля может выглядеть следующим образом:

Сборка основного модуля полностью аналогично обычному способу:

Тестирование модулей

Для тестирования модуля необходимо выполнить их установку на устройство и поочередно загрузить сначала модуль neon_mod, затем main_module_test. В противном случае будет получена ошибка об отсутствии символа.

После загрузки основного модуля необходимо создать для него устройство и произвести из него чтение, например, командой cat. При вызове команды вы увидите сообщения с увеличивающимся счетчиком (в зависимости от настройки ОС непосредственно в терминале и/или в системных логах).

Поделиться
  • 4
    Поделились

Добавить комментарий