ядро linux- ›дейтаграммы многоадресной потоковой передачи в пользовательском пространстве

Я планирую написать драйвер linux для некоторого оборудования с отображением памяти (он находится в FPGA, поэтому при необходимости я могу настроить этот интерфейс с отображением памяти на обоих концах).

Эта логика FPGA генерирует последовательность дейтаграмм, которые я должен обработать, а затем передать по каналу Ethernet. Нет никаких причин для того, чтобы ни обработка, ни сетевой код находились в ядре, поэтому я спрашиваю о «лучшем» механизме перемещения блоков данных с оборудования в пространство пользователя. Самая большая сложность заключается в том, что обработка пользовательского пространства должна распределяться между несколькими процессами.

Скорость передачи данных не очень высока (до 1 Мбит / с), а интерфейс mmio питается от довольно глубокого FIFO (2 КБ в настоящее время может быть увеличено до 8 КБ), поэтому я думаю, что процесс пользовательского режима с высоким приоритетом может не отставать.

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

Пока что я собрал следующие идеи:

  • AF_NETLINK: поддерживает многоадресную рассылку, заботится о буферизации за меня. Но API нестабилен, мне нужно определить новый идентификатор сокета, который может конфликтовать с другими драйверами в дереве, а интерфейс пользовательского режима довольно специализирован, я не могу использовать стандартные инструменты, такие как socat, для тестирования потока данных.

  • Передайте сокет режима дейтаграммы или дескриптор файла FIFO из пользовательского пространства и напишите в него (как?). Есть патч многоадресной рассылки сокета дейтаграммы домена unix, который я мог бы применить.

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

  • Предоставьте устройство символьного режима, которое может быть открыто несколькими приложениями пользовательского режима одновременно, и буферизуйте данные для каждого в ядре.

Я склоняюсь к устройству символьного режима с сервером домена unix, который перенаправляет входящие пакеты. Это избавляет меня от необходимости реализовывать логику буферизации внутри драйвера ядра. Тогда возникает вопрос, как разбудить пользовательский процесс от select вызова или блокирующего чтения при возникновении прерывания. Кажется, моя функция poll может читать регистр управления и возвращать POLLIN|POLLRDNORM, если данные уже доступны, и демаскировать прерывание, если нет. И тогда обработчик прерывания будет использовать wake_up, чтобы пометить wait_queue как готовый. read всегда маскирует прерывание.


person Ben Voigt    schedule 27.04.2011    source источник
comment
Меня заинтриговал этот интересный проект.   -  person whoplisp    schedule 05.07.2011


Ответы (3)


Я думаю, что драйвер char - лучший вариант, потому что вы найдете для него гораздо больше документации, а часть ядра проще. API хорошо известен, и вы найдете больше людей, имеющих опыт работы с символьными драйверами.

Начните с малого, то есть с рабочего чтения блокировки на основе прерывания:

  • если данные недоступны, спать в очереди
  • иначе вернуть доступные данные в пользовательское пространство.

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

Как только он заработает, реализуйте опрос.

person shodanex    schedule 28.04.2011
comment
Так и закончилось. Возможность тестирования с hexdump, dd и socat была большим преимуществом для символьного драйвера по сравнению с netlink. - person Ben Voigt; 19.05.2011

Я думаю, что netlink - ваш лучший вариант. Кстати, вы можете рассматривать сокет netlink как обычный сокет и использовать POLL, EPOLL, select на нем.

Кроме того, что вы имеете в виду, говоря «API нестабилен»? Netlink используется очень часто, и у него довольно приличный API.

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

РЕДАКТИРОВАТЬ: Если у вас есть доступ, обратитесь к стр. 810 и далее (глава 12) книги Professional Linux Kernel Architecture от Wrox. Насколько мне известно, в нем есть (относительно) хорошее описание того, как можно использовать netlink для связи с пользовательским пространством.

Единственный недостаток netlink - скудная документация. В остальном, на мой взгляд, это нормально.

person rmk    schedule 27.04.2011
comment
Говоря о нестабильности API, я имею в виду, что каждый раз, когда я нахожу пример, комментарии ниже жалуются на то, что он больше не работает в следующей версии ядра. например linuxjournal.com/article/7356 Это заставляет меня думать, что любой код, который существует достаточно долго, чтобы найти свой путь в книгу больше не будет работать так, как написано. - person Ben Voigt; 28.04.2011

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

person caf    schedule 04.05.2011