LINUX.ORG.RU

copy_to_user из прерывания

 


0

3

В дополнение к моей предыдущей теме, возникло непонимание, которое пока не сильно устранилось чтением LDD и SO. Требуется реализовать blocking read, по этому есть темы как в LDD так и на SO, сам механизм blocking read не вызвал затруднений.

Я выделил DMA буфер на 256 Кбайт, через MMIO передаю адрес этого буфера и он успешно заполняется, после чего приходит прерывание, в котором можно инициировать чтение следующего блока. Скорость около 170 Мбайт/с (если запросить гигабайт).

Возникла проблема при реализации char device, а именно операции read. Если осуществить copy_to_user в самом этом вызове то всё успешно читается на стороне userspace (cat, dd). Однако в обработчике прерывания copy_to_user не работает, хотя бы потому что он может сам уходить в sleep, что очевидно недопустимо для ISR.

Как же тогда быть? Допустим я запросил прочитать мегабайт из устройства, а буфер 256К - нужно при получении прерывания проснуться и вернуть лишь 256К а пользователь будет затем повторять запросы чтения четыре раза с разным offset (у char-то устройства)? Например dd bs=1M count=1 не повторит.

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

slapin tailgunner ebantrop Andrey_Utkin :)

В драйвере устройства алоцировать память с GFP_DMA (можно несколько) и сделать mmap. А чтение делать через ioctl() который возвращает адресу буфера с данными.

vel ★★★★★ ()
Последнее исправление: vel (всего исправлений: 1)

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

pftBest ★★★★ ()

Допустим я запросил прочитать мегабайт из устройства, а буфер 256К - нужно при получении прерывания проснуться и вернуть лишь 256К а пользователь будет затем повторять запросы чтения четыре раза с разным offset (у char-то устройства)? Например dd bs=1M count=1 не повторит.

Поведение dd в данном случае нерелевантно, потому что он сделан для блочных устройств. cat будет работать,

Нужно выделить буфер в драйвере на мегабайт и затем после его заполнения уже просыпаться и делать copy_to_user всего мегабайта?

Нет. Что за странная идея.

Получается двойное копирование.

Откуда? Что ты называешь двойным копированием?

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

В чем говнокод? Быстрее варианта с mmap - нет, а у ТС как раз проблема в производительности(?).

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

Если ТС решил создать chardev, что самое простое и тривиальное, то ioctl() самое правильное средство для манипуляцией буферами.

Я не знаю, что у ТС за устройтсво. Оно генерирует поток данных или оно отдает блок данных по запросу?

Во втором случае все намного проще - можно сначала залочить странички, потом запустить чтение из устройства в них через dma.

vel ★★★★★ ()

Однако в обработчике прерывания copy_to_user не работает, хотя бы потому что он может сам уходить в sleep, что очевидно недопустимо для ISR.

так и не надо там ничего копировать! Максимум, что там можно сделать - это wakeup() тому, кто ждет данных в сисколе.

vel ★★★★★ ()
Ответ на: комментарий от vel

В чем говнокод?

Использование ioctl в роли read.

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

_Если_ вопрос в этом, то для получения сообщения о готовности существую штатные механизмы вроде poll. Но, насколько я могу судить, вопрос в чем-то другом.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

poll() хорош, когда несколько дескрипторов.

Весь этот огород со списком буферов и mmap нужна только если данные идут с устройства неприрывным потоком и важно не терять данные.

А если это старт-стопный режим, то весь этот огород нафиг не нужен.

vel ★★★★★ ()
Ответ на: комментарий от vel

poll() хорош, когда несколько дескрипторов.

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

А если это старт-стопный режим, то весь этот огород нафиг не нужен.

Не очень понимаю, что могло бы являться здесь «стартом» и «стопом», но, естественно, иногда достаточно просто операции read - не нужен ни poll, ни ioctl для чтения.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от DELIRIUM

Почему это?

Потому что код собственно драйвера проще не станет, но добавится взаимодействие с netlink.

Как минимум кода (в кернел спейсе), скорее всего, будет меньше.

Не будет.

А, и как там у netlink с mmap?

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)

copy_to_user из прерывания не сильно здоровая идея. Я бы посмотрел немного в другую сторону:

При открытии вашего устройства к файловому дескриптору (которая структура с read/write/ioctl и тд функциями в ядре) прицепил бы I/O буффер . Когда пользователь делает read - первое что сделать проверить есть ли в I/O буфере данные для чтения. Если есть то copy_to_user, если нет (или есть но не достаточное ко-во) то запросить следующий блок и уйти wait. Когда из придет интерапт что DMA пересылка завершилась то не делать copy_to_user а просто разбудить ожидающий процесс он там дальше сам разберется что делать с данными (и нужны ли они ему еще, может его уже убили давно)

zaz ★★★★ ()
Ответ на: комментарий от DELIRIUM

За такое использование netlink Дэвид Миллер бубном по кумполу бьёт.

slapin ★★★★★ ()
Ответ на: комментарий от tailgunner

С mmap никак (но ничто не мешает его запилить на уровне драйвера), но netlink как интерфейс передачи данных собирает линч-группу во главе с Дэвидом Миллером.

slapin ★★★★★ ()
Ответ на: комментарий от slapin

Чем это лучше обычного char device? Пример смотрел.

netlink как интерфейс передачи данных собирает линч-группу во главе с Дэвидом Миллером.

ЯННП. Кого линчевать будут и за что?

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

дык char device это уже сторона юзера. kfifo абстрагирует всё что нужно чтобы отдать данные от драйвера до character device. Тот пример плох, что они отдают шлак через procfs, его оттуда надо выкинуть.

David Miller очень не любит когда netlink натягивают на не свойственные ему функции. Чревато как минимум лекцией о том, почему /ам. Вторая попытка приводит к management by perkele. Поэтому посыл очень простой - не надо так делать.

slapin ★★★★★ ()
Ответ на: комментарий от slapin

Спасибо, интересно, буду использовать kfifo. Иными словами, я должен засунуть 256К в прерывании в это fifo при помощи kfifo_in, а затем проснувшись, сделать kfifo_to_user. Но прежде всего kfifo_init.

in - первое копирование память-память, to_user - второе копирование - эффективно ли?

Пример сейчас посмотрю, спасибо за эту инфу.

I-Love-Microsoft ★★★★★ ()
Ответ на: комментарий от I-Love-Microsoft

Спасибо, интересно, буду использовать kfifo

Ты сначала разберись, что это.

tailgunner ★★★★★ ()
Ответ на: комментарий от I-Love-Microsoft

ну как тебе сказать...

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

Если ты данные извлекаешь в прерывании, то тебе необходима буферизация. Иначе ты заводишь кернеловый тред, который периодически будишь для копирования. И данные ты должен куда-то сложить всё-равно, так как юзерспейс может их почитать чуть позже. Если ты согласен дропать данные, пока их никто не читает, то можно делать вычитывание железа прямо из read. То есть тут вопрос структуры данных - если у тебя пакетная структура, то тебе нужен буфер на пакет, можешь написать свой драйвер как драйвер сетевухи, тогда ядро эти нужные 2 копирования сделает за тебя. Но в этом случае непрочитанные пакеты будут также дропаться.

Если у тебя просто байтовый поток и ты согасен дропать данные, можешь вычитывать данные прямо в .read по запросу userspace. Из прерывания в таком случае просто через event разблокируем read если что-то пришло, а в read читаем прямо из железки. fifo не используем. Скорость достижения байтами конечного назначения тут будет выше за счет отсутствия буферизации, но дойдут ли байты - зависит от удачи, но я знавал случаи успешного применения данной схемы.

Если нужно, чтобы данные не терялись и флкутуации в userspace не приводили к потерям, я бы советовал использовать kfifo. Но скорость достижения конкретным байтом точки назначения может быть ниже за счёт буферизации.

slapin ★★★★★ ()
Ответ на: комментарий от slapin

Если ты данные извлекаешь в прерывании, то тебе необходима буферизация. Иначе ты заводишь кернеловый тред, который периодически будишь для копирования.

Зачем? Железка заполняет буфер через DMA, а обработчик прерывания просто извлекает заполненные буферы из очереди и сообщает железке о новых.

tailgunner ★★★★★ ()
Ответ на: комментарий от slapin

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

slapin ★★★★★ ()
Ответ на: комментарий от slapin

Не важно кто делает копирование, важно, что оно есть.

Ну речь шла о нити ядра, которая (по крайней мере, без kfifo) не нужна. А вместо копирования можно просто мапить заполненный DMA буфер в юзерспейс, но это, конечно, мало кому нужно.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Мапить заполненный буфер не так просто, не факт что в итоге быстрее получится, тут надо больше знать о том, что с данными потом делать надо будет. Китайцы вон, камерами через /dev/mem рулят, и ничего... только тормозит... немного :) Если нет критической необходимости выжать максимум я за kfifo. mmap - это контекст-свитчи, синхронизация, фу. Особенно если это просто поток байт.

slapin ★★★★★ ()
Ответ на: комментарий от slapin

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

Получится.

mmap - это контекст-свитчи, синхронизация

Эээ... щито? Нет там ни переключений контекста, ни синхронизации.

Особенно если это просто поток байт.

Если просто поток байт - конечно. Но, по-меому, у ТС не совсем поток байт.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

kfifo чтобы положить в буфер не требует нить ядра, так как не спит и не требует блокировок.

Короче, я вижу недопонимание по поводу kfifo

https://lwn.net/Articles/237850/

То есть мы заполняем kfifo с помощью DMA а извлекаем в character device. То есть мы дополнительно не копируем из буфера DMA в kfifo.

slapin ★★★★★ ()
Ответ на: комментарий от tailgunner

Если там пакеты, и здоровые, то есть выигрыш от mmap. Если мы отдаём кэшируемую память обычную, и заново зааллоцированную, то возникает необходимость освободить буфер после обработки userspace. Также трудности бывают, если эта память непосредственно не работает с DMA (так как берётся из userspace map'а), приходится использовать bouncer. Тут стоит посмотреть на менеджмент буферов в video4linux. И городить всю эту радость стоит только если это очень нужно. Можно аллоцировать в ядре а потом перемапливать в userspace, но это тоже медленно. А если не перемапливать, тогда совсем беда. Если пакеты маленькие, не стоит этим заниматься.

ТС, что у тебя за данные вообще?

slapin ★★★★★ ()
Ответ на: комментарий от slapin

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

slapin ★★★★★ ()
Ответ на: комментарий от slapin

kfifo чтобы положить в буфер не требует нить ядра, так как не спит и не требует блокировок.

А кому она требуется?

https://lwn.net/Articles/237850/

Ну вот как раз здесь есть и синхронизация, и сон.

Я не против kfifo, но, судя по названию, оно сделано для потока байтов, а у ТС чуть ли не блочный девайс

Если мы отдаём кэшируемую память обычную, и заново зааллоцированную, то возникает необходимость освободить буфер после обработки userspace

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

И городить всю эту радость стоит только если это очень нужно.

Это да.

Можно аллоцировать в ядре а потом перемапливать в userspace, но это тоже медленно.

Почему? У меня это работало гораздо быстрее, чем read (который копировал данные из ядра в юзерспейс).

tailgunner ★★★★★ ()
Ответ на: комментарий от slapin

То есть мы заполняем kfifo с помощью DMA а извлекаем в character device. То есть мы дополнительно не копируем из буфера DMA в kfifo.

У меня на неделю нет доступа устройству в связи с отъездом, изучаю теоретическую матчасть. Да, я обратил внимание на DMA-возможности этого kfifo. И сразу мысль - что если сразу в него будут данные падать.

Но я понял свою ошибку - раз у меня char device то я могу работать как я и написал в первом посте, не обращая внимание на то как это будет работать с dd. В самом деле, cat запрашивает 32 килобайта (ну такой там объем по дефолту), если нет новых данных - я инициирую передачу из устройства и засыпаю, а если есть - то оставшиеся 7 обращений будут без засыпания выдавать данные по 32К (всего например 256К).

Что за данные и каков их формат? Пока тупо поток байт, которые надо переместить из устройства в программа максимально быстро, без потерь. ПЛИС генерирует паттер, по которому легко понять были ли потери и разрывы данных. Какое это будет устройство - станет ясно потом, а пока - чисто академический интерес.

I-Love-Microsoft ★★★★★ ()

Посмотрите на механизм тасклетов. Пример использования можете увидеть здесь (ищите по строке tasklet в -core.c и -video.c, по ссылке - процедура тасклета) https://github.com/bluecherrydvr/linux/blob/tw5864/drivers/media/pci/tw5864/t...

На ЛОР стал редко заходить, пишите на почту ваши вопросы. А ещё лучше сразу на английском и в почтовую рассылку kernelnewbies :)

Andrey_Utkin ★★ ()
Ответ на: комментарий от ebantrop

А нужен ли вообще read? Я без него обошелся.

Как? Я через read сделал, по 256K за раз читаю. Для теста сойдет (я знаю что могу лишь по 256K корректно читать), а потом более специализированный драйвер будет написан.

I-Love-Microsoft ★★★★★ ()
Ответ на: комментарий от I-Love-Microsoft

Ну возможно наглое и тупое решение, но я просто беру гиг памяти, пиню его, делаю список дескрипторов и говорю железке где голова списка. Дальше ПЛИС льёт данные в буффер и я могу спросить его в любой момент сколько он отгрузил через mmap'нутые регистры. Рид не нужен. Нужен ioctl страницы припинить и mmap регистры почитать/писать.

Потом в юзерспейсе делаю обработку и отдаю результат по gRPC всем желающим. Как-то так. Правда у меня все в руках: и исходники ПЛИСа, и драйвер, и юзерспейс мой.

ebantrop ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.