Вопрос о direct I/O (get_user_pages)

Вопросы программного кода и архитектуры Linux

Модератор: Olej

bose
Писатель
Сообщения: 107
Зарегистрирован: 23 фев 2012, 14:41
Откуда: Киев
Контактная информация:

Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение bose » 27 мар 2013, 13:13

В драйвере есть необходимость доступа к структурам данных процесса пользователя, а имеенно нужно отобразить пользовательскую структуру данных, которая занимает ок 18 Мб, на виртуальные адреса ядра. Для этого в ядрах > 2.6 используется механизм direct I/O (в замен старого kiobuf).
Существует функция get_user_pages, которая и реализовывает этот механизм.
Механизм работы функции таков, что она берёт размер и указатель на виртуальный адрес (внимание) непрервыного!!! пользовательского пространства, и возвращает указатели на страницы физической памяти текущего процесса. Затем в ядре (в драйвере), есть возможность получить (вызовом kmap) виртуальные адресса ядра, указывающие на эти страницы. Есть одно но - отображение не является линейным. Т.е. в ядре пользовательская структура данных (которая, повторюсь, непрерывна в виртуальном адрессном пространстве процесса) разбивается на куски размером в PAGE_SIZE, которые в виртуальном адрессном пространстве ядра находятся разрозненно! По сути вопрос - как "склеить" эти куски, что бы в ядре можно было работать с указателем на структуру данных, а не участками данных размером в страницу.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение Olej » 27 мар 2013, 14:01

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

bose
Писатель
Сообщения: 107
Зарегистрирован: 23 фев 2012, 14:41
Откуда: Киев
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение bose » 27 мар 2013, 15:29

Olej писал(а): Как всегда в подобных случаях ответ на этот вопрос начинается с другого вопроса ;-) :
Я не достаточно подробно описал проблему?
Более обобщённо - как отобразить непрерывный диапазон виртуальных адресов пользовательского процесса, на непрерывный диапазон виртуальных адресов ядра такого же размера?
Olej писал(а): - как сформулировать тестовую задачу, моделирующую эту ситуацию?
- и написать демонстрационный проект ... да ещё и как можно проще, чтобы не загружать лишними подробностями.
У меня есть модуль, который в паре с пользовательским ПО воспроизводит проблему (тестовым я его конечно не назову), и я могу его выложить (после небольшого редактирования - чтобы не загружать лишними подробностями ;-) )... но данный вопрос скорее не практический (покажитке как), а концептуальный (существует ли такая возможность)... Т.е. сталкивался кто-то с подобным вопросом или нет...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение Olej » 27 мар 2013, 15:40

bose писал(а): У меня есть модуль, который в паре с пользовательским ПО воспроизводит проблему (тестовым я его конечно не назову), и я могу его выложить (после небольшого редактирования - чтобы не загружать лишними подробностями ;-) )...
Вот об этом и речь ... только не небольшое редактирование, а как можно большее ;-) , чтобы оставить только суть проблемы.
bose писал(а): но данный вопрос скорее не практический (покажитке как), а концептуальный (существует ли такая возможность)... Т.е. сталкивался кто-то с подобным вопросом или нет...
А никто вам "концептуально" не скажет вот так на пальцах, без кода ... разве что 1-2-3 непосредственных разработчиков кода этого места в ядре ... - но это если вы их разыщите ;-)

bose
Писатель
Сообщения: 107
Зарегистрирован: 23 фев 2012, 14:41
Откуда: Киев
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение bose » 28 мар 2013, 19:00

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

Как всегда:

Код: Выделить всё

make
sudo make install
, и далее:

Код: Выделить всё

./devtest
dmesg |tail -n 5
Собственно на что нужно обратить внимание... - несмотря на то что в драйвере мы корректно (последовательно, страница за страницей) получили доступ к всему содержимому пользовательского буфера, виртуальные адреса пространства ядра, указывающие на отдельные страницы внутри пользовательского буфера (получили вызовом kmap), не являются последовательными....

Выхлоп dmesg |tail -n 5 на моём десктопе (Linux 3.2.0-39-generic x86_64):
[30273.676525] ioctl: succes pin 4 pages ( user address: 2229000 ).
[30273.676532] Request: 4096; Read: 4096; Offset: 0; Vaddress: 0xffff88011a22c000;
[30273.676574] Request: 4096; Read: 4096; Offset: 4096; Vaddress: 0xffff88011a23a000;
[30273.676623] Request: 4096; Read: 4096; Offset: 8192; Vaddress: 0xffff8801184a9000;
[30273.678308] Request: 4096; Read: 4096; Offset: 12288; Vaddress: 0xffff88011a235000;
Например:
$ python -c 'print 0xffff88011a23a000-0xffff88011a22c000'
57344 - ожидаю 4096
$ python -c 'print 0xffff8801184a9000-0xffff88011a23a000'
-31002624 - опять ожидаю 4096
Коряво псевдографикой:
User Space VA | Kernel Space VA
|P1|P2|P3|P4| -> |P3|...|P2|..|P1|P4|
Вложения
directio.tgz
Модуль псевдосимвольного устр-ва usrdev
(3.35 КБ) 376 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение Olej » 28 мар 2013, 20:48

bose писал(а): Собственно на что нужно обратить внимание... - несмотря на то что в драйвере мы корректно (последовательно, страница за страницей) получили доступ к всему содержимому пользовательского буфера, виртуальные адреса пространства ядра, указывающие на отдельные страницы внутри пользовательского буфера (получили вызовом kmap), не являются последовательными....
По первому взгляду ... по-верхам:

1. Как я понимаю, страницы буфера и не должны отображаться последовательно, потому что страницы эти выделяются в физической RAM по произвольным, далеко не последовательным ядресам. Это происходит когда ваш вызов memalign() в пользовательском приложении devtest.c ретранслируется в системный вызов, по которому ядро и выделит RAM вашему приложению, и как я понимаю, выделит её вызовом vmalloc(), а не kmalloc(). А уже приложению devtest.c эти страницы RAM через таблицы MMU будут отображены в последовательные логические адреса.

2. Если хотелось бы последовательного отображения я ядерном пространстве, то, наверное, нужно было бы добиться, чтобы буфер выделялся в непрерывной физической области RAM, вызовом типа kmalloc()?

3. А зачем вам непрерывное отображение? Я думаю, для копирования данных единым оператором копирования (типа memcpy()).
- а сильно существенна ли разница?
- выделяя непрерывную область (в физической RAM) этого можно было бы, возможно, добиться...
- или организовывать передачу данных операциями по типу векторного ввода-вывода ... такие операции копирования между памятью есть в ядре, я их видел и использовал в примерах драйверов блочных устройств (в первоначальном тексте этого не было, но добавлено позже, см. http://mylinuxprog.blogspot.com/2013/01/linux.html)

4. ... это уже не относится к существу дела, но по коду приложения devtest.c (драйвер я ещё не успел пересмотреть):

4.1. вызов getpagesize() считают устаревшим и непереносимым ... об этом пишет и man getpagesize(2) - согласно нему строку:

Код: Выделить всё

const unsigned long psize = (unsigned long) getpagesize();
лучше заменить на:

Код: Выделить всё

const unsigned long psize = sysconf( _SC_PAGESIZE );
4.2. опять же ;-) (man memalign(3))

Код: Выделить всё

pbuffer = (void *) memalign( psize, PAGE_COUNT * psize );
поменять на:

Код: Выделить всё

pbuffer = valloc( PAGE_COUNT * psize );
Оно и не так важно, но уменьшает перегруженность текста - легче понимать написанное.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение Olej » 28 мар 2013, 22:08

bose писал(а):Ну вот, аттачу драйвер и пользовательское приложение, которые в паре позволяют показать проблему...
Как всегда:

Код: Выделить всё

make
sudo make install
, и далее:

Код: Выделить всё

./devtest
dmesg |tail -n 5
bose, вы где-то намудрячили, когда готовили пример (а перед отправкой его не проверили):

Код: Выделить всё

bash-4.2$ sudo insmod usrdev.ko 
bash-4.2$ dmesg | tail -n1
[36962.035502] Device userdev registred.
bash-4.2$ lsmod | head -n2
Module                  Size  Used by
usrdev                 12673  0 
Но! :

Код: Выделить всё

bash-4.2$ ./devtest 
Start buffer 0x9219000.
Problem opening the node /dev/usrdev: No such file or directory.
bash-4.2$ ls /dev/usr*
ls: невозможно получить доступ к /dev/usr*: Нет такого файла или каталога
Модуль usrdev.ko вовсе не создаёт имени /dev/usrdev, которое ожидает получить devtest (да и символьные эти имена записаны явно в текстах devtest.c и userdev.c ... вместо того, чтобы определяться согласованно один раз в utok.h).

bose
Писатель
Сообщения: 107
Зарегистрирован: 23 фев 2012, 14:41
Откуда: Киев
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение bose » 28 мар 2013, 22:41

Olej писал(а): 3. А зачем вам непрерывное отображение? Я думаю, для копирования данных единым оператором копирования (типа memcpy()).
На самом деле буфер в пользовательском пространстве в данном случае абстракция... в реальности там находиться структура данных, со своими полями, которые в свою очередь также являются составными типами данных + массивы... эти поля и масивы, понятное дело, не выравнены на границы страниц...
Пользовательское приложение заполняет и периодически модифицирует эти данные, драйвер должен принимать во внимание эти изменения...
Т.е. драйверу удобно работать не с сырым буфером, а со структурой... она большая ок. 24 Мб.
Вроде того:

Код: Выделить всё

struct VeryBigStructure;
struct VeryBigStructure *  pvbs = (struct VeryBigStructure *) kmap( pages[0] );
treshold = pvbs->tooFarThan4096bytes;
kmalloc - может выделить макс 4 Мб непрерывных данных... т.е. нужно будет управлять 6 буферами как одним + отображать их на структуру...
и потом mmap - занят для других нужд
Olej писал(а): 4.1. вызов getpagesize() считают устаревшим и непереносимым ... об этом пишет и man getpagesize(2) - согласно нему строку:

Код: Выделить всё

const unsigned long psize = (unsigned long) getpagesize();
лучше заменить на:

Код: Выделить всё

const unsigned long psize = sysconf( _SC_PAGESIZE );
Исключительно для нужд данного примера, дальше это никуда не пойдёт... getpagesize - более красноричивей поясняет что я хотел сделать...

Olej писал(а): 4.2. опять же ;-) (man memalign(3))

Код: Выделить всё

pbuffer = (void *) memalign( psize, PAGE_COUNT * psize );
поменять на:

Код: Выделить всё

pbuffer = valloc( PAGE_COUNT * psize );
из того же мана:
The obsolete function memalign()...
The obsolete function valloc()...
А вообще, маны предмет отдельного обсуждения... притензий много... в них пишут как на заборах, содержимое обновляют редко...
а как известно устаревшая информаця хуже её отсутсвия...

Вообще пользовался функцией posix_memalign
В мане сказано...
POSIX requires that memory obtained from posix_memalign() can be freed using free(3).
Но самый простецкий пример с выделением и последующим освобождением памяти этой функцией приводит к double free or corruption

bose
Писатель
Сообщения: 107
Зарегистрирован: 23 фев 2012, 14:41
Откуда: Киев
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение bose » 28 мар 2013, 22:47

Olej писал(а): Модуль usrdev.ko вовсе не создаёт имени /dev/usrdev, которое ожидает получить devtest (да и символьные эти имена записаны явно в текстах devtest.c и userdev.c ... вместо того, чтобы определяться согласованно один раз в utok.h).
Олег, я не создаю ноду в коде... если посмотрите makefile то увидите там в цели install

я же написал...
sudo make install
Это не методический пример, и потом мы же договорились не перегружать код дабы не уходить от вопроса... только то что нужно что бы осветить вопрос

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Вопрос о direct I/O (get_user_pages)

Непрочитанное сообщение Olej » 29 мар 2013, 01:41

bose писал(а):
Olej писал(а): Модуль usrdev.ko вовсе не создаёт имени /dev/usrdev, которое ожидает получить devtest (да и символьные эти имена записаны явно в текстах devtest.c и userdev.c ... вместо того, чтобы определяться согласованно один раз в utok.h).
Олег, я не создаю ноду в коде... если посмотрите makefile то увидите там в цели install

я же написал...
sudo make install
Это не методический пример, и потом мы же договорились не перегружать код дабы не уходить от вопроса... только то что нужно что бы осветить вопрос
Не досмотрел (mknod и т.д.) ;-)
Я очень боюсь всяких sudo, и sudo make install, и действий от root, которые выполняются из чужого кода - никогда не делаю ;-)

Ответить

Вернуться в «Linux изнутри»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 18 гостей