0

Довольно уверен, что я уже знаю ответ на этот вопрос, так как есть связанные вопросы на SO уже (here, here и here ,, и this был полезен) ,,, но я хотел быть абсолютно уверенным, прежде чем погрузиться в ядро- космический водитель земли (никогда там не было).Двусторонняя связь с устройством PCIe через/dev/mem в пользовательском пространстве Linux?

У меня есть устройство PCIe, с которым мне нужно общаться (и наоборот) из приложения в пользовательском пространстве linux. Открыв /dev/mem, затем mmap 'ing, мне удалось написать драйвер для пользовательского пространства, построенный поверх pciutils, что позволило мне mmap BAR и успешно записать данные на устройство. Теперь нам нужно, чтобы comm отправился в другое направление, от устройства PCIe до пользовательского приложения linux. Для того, чтобы это сработало, мы полагаем, что нам понадобится большой кусок (~ 100 МБ) физически смежной памяти, который никогда не будет выгружен/заменен. После выделения этот адрес должен быть передан на устройство PCIe, чтобы он знал, где записать свои данные (таким образом, я не вижу, как это может быть виртуальная, сменная память). Есть ли способ сделать это без драйвера пространства ядра? Одна идея здесь была размещена, возможно, мы можем открыть /dev/mem, а затем подать команду ioctl, чтобы выделить то, что нам нужно? Если это возможно, я пока еще не смогу найти какие-либо примеры в Интернете, и вам придется исследовать его более интенсивно.

Предполагая, что нам нужен драйвер пространства ядра, лучше всего выделить наш большой патрон во время загрузки, а затем использовать ioremap, чтобы получить виртуальный адрес ядра, а затем mmap оттуда в пользовательское пространство, правильно? Из того, что я читал на kmalloc, мы не будем приближаться к 100 МБ, используя этот вызов, а vmalloc не годится, так как это виртуальная память. Чтобы выделить при загрузке, драйвер должен быть статически связан с ядром, правильно? Это в основном встроенное приложение, поэтому переносимость для меня не вызывает большого беспокойства. Возможно, работает модуль, а не статически связанный драйвер, но я беспокоюсь, что фрагментация памяти может помешать обнаружению физически смежного региона, поэтому я хотел бы выделить его как можно скорее из включения питания. Любая обратная связь?

EDIT1: Мой процессор - архитектура ARM7.

ответ

1

Hugepages-1G

Текущие x86_64-процессоры не только поддерживают 4k и 2М, но и 1G-страниц (флаг pdpe1gb в/Proc/CPUInfo указывает на поддержку).

Эти 1G-страницы должны быть зарезервированы при загрузке ядра, поэтому должны быть указаны параметры загрузки hugepagesz=1GB hugepages=1.

Затем hugetlbfs должны быть установлены:

mkdir /hugetlb-1G 
mount -t hugetlbfs -o pagesize=1G none /hugetlb-1G 

Затем откройте какой-то файл и MMAP его:

fd = open("/hugetlb-1G/page-1", O_CREAT | O_RDWR, 0755); 
addr = mmap(NULL, SIZE_1G, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 

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

Даже если ваш процесс выходит из строя, огромная страница будет зарезервирована для его сопоставления, как указано выше, поэтому устройство pci-e не будет записывать мошенников в системную память или память процесса.

Вы можете узнать физический адрес, прочитав /proc/pid/pagemap.

+0

стрелять, возможно, должен был упомянуть, что процессор, который я использую (по крайней мере на данный момент), является архитектурой ARM7. Не уверен, что это делает ваш ответ спорным или нет. Большое спасибо за информацию, хотя, я обязательно исследую этот метод в ближайшие дни. – yano

+0

Думаю, 32-битная рука не имеет 1G-огромных страниц (может быть, меньше, хотя). Другим способом можно было зарезервировать часть вашей памяти (например, bootparameter ядра «memmap = 100M @ 0x10000000» резервирует 100 МБ в физической памяти 1 ГБ) и mmap из/dev/mem в память процессов. Вы должны убедиться, что ядро ​​не было скомпилировано с CONFIG_DEVMEM_STRICT, однако – Ctx

+0

Для драйвера ядра, 'dma_alloc_coherent' может выделить память, подходящую для доступа как к устройству, так и к CPU. Обработчик операций файла драйвера для 'mmap' может вызывать' dma_mmap_coherent', чтобы сопоставить его с адресным пространством пользователя. Трудность состоит в том, что размер памяти, которую вы можете выделить с помощью 'dma_alloc_coherent', обычно довольно ограничен. Вы можете преодолеть это, используя параметр загрузки ядра 'cma', например. 'CMA = 100M'. На практике вам обычно нужно сделать параметр 'cma' больше, чем вам нужно, поскольку другие драйверы могут украсть некоторые из них до вашего драйвера. –

0

На самом деле комментарий Ctx около memmap - это то, что привело меня к правильному пути. Чтобы зарезервировать память, я дал аргумент загрузчика как memmap=[size]$[location], который я нашел here. Различные символы означают разные вещи, и они не совсем интуитивно понятны. Еще одна небольшая коррекция: флаг CONFIG_STRICT_DEVMEM, с которым я не скомпилировал ядро.

Есть еще загадки. Например, [location] в аргументе memmap казался бессмысленным. Независимо от того, что я установил для местоположения, linux взял все, что не было зарезервировано с [size] в одном смежном фрагменте, и пространство, которое я зарезервировал, было в конце. Единственное указание на это - посмотреть на /proc/iomem. Объем зарезервированного пространства соответствовал промежутку между концом пространства памяти Linux и концом пространства системной памяти. Я не мог найти никаких указаний в любом месте, где linux сказал: «Я вижу ваш зарезервированный фрагмент, и я его не трону», кроме того, что Linux не использовал его в /proc/iomem. Но FPGA пишет в это пространство уже несколько дней без видимых явлений для linux, поэтому я думаю, что мы все хороши! Я могу просто mmap в это место и прочитать данные (это удивляет, так как linux не указывает, что это существует, но рад, что это так). Спасибо за помощь! Ян, я вернусь к вашему комментарию, если поеду в пространство драйвера ядра.