Jump to content

    

отмапить виртуальный адрес ядра в пространство пользователя

В pcie драйвере вызвал функцию pci_alloc_consistent(). Благодаря этому получил два адреса - один виртуальный адрес ядра, другой - физический адрес. Физический адрес сообщил в плис. Плис пишет данные по этому адресу, выставляет прерывание. В обработчике прерывания печатаю содержимое буфера с виртуальным адресом. Вижу свои данные, все работает.

 

Вопрос 1. Функция pci_alloc_consistent завершается удачно, если размер запрашиваемого буфера не превышает 4 МБайт. С чем это связано, почему не удаётся получить 8 и больше МБайт?

 

Вопрос 2. Можно ли замапить виртуальный буфер ядра в пространство пользователя?

Это вообще возможно?

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

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

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

Share this post


Link to post
Share on other sites
В pcie драйвере вызвал функцию pci_alloc_consistent(). Благодаря этому получил два адреса - один виртуальный адрес ядра, другой - физический адрес. Физический адрес сообщил в плис. Плис пишет данные по этому адресу, выставляет прерывание. В обработчике прерывания печатаю содержимое буфера с виртуальным адресом. Вижу свои данные, все работает.

 

Вопрос 1. Функция pci_alloc_consistent завершается удачно, если размер запрашиваемого буфера не превышает 4 МБайт. С чем это связано, почему не удаётся получить 8 и больше МБайт?

 

Вопрос 2. Можно ли замапить виртуальный буфер ядра в пространство пользователя?

Это вообще возможно?

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

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

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

 

Сейчас не помню точно, но я бы посмотрел на то, что не все память может быть маппирована. Можно посмотреть в поиске про аллокацию для DMA, что то же самое.

И еще для маппированной памяти должен быть доступен большой сплошной кусок. Для vmalloc такое уже не обязательно.

Share this post


Link to post
Share on other sites
Вопрос 1. Функция pci_alloc_consistent завершается удачно, если размер запрашиваемого буфера не превышает 4 МБайт. С чем это связано, почему не удаётся получить 8 и больше МБайт?

покопайте в сторону CMA в ядре..

Share this post


Link to post
Share on other sites

Удалось отмапить буфер ядра в user space, но данные в буфере юзера циклически повторяются каждые 4096 байт.

 

В ядре маплю как-то так:

static u64* dma_buf_virt_core_addr_ptr;
static dma_addr_t  dma_bus_addr_for_fpga;

#ifndef VM_RESERVED
#define VM_RESERVED    (VM_DONTEXPAND | VM_DONTDUMP)
#endif

struct mmap_info
{
    uint64_t* data;
    int reference;
};

void mmap_open(struct vm_area_struct* vma)
{
    struct mmap_info* info = (struct mmap_info*)vma->vm_private_data;
    info->reference++;
}

void mmap_close(struct vm_area_struct* vma)
{
    struct mmap_info* info = (struct mmap_info*)vma->vm_private_data;
    info->reference--;
}

static int mmap_fault(struct vm_area_struct* vma, struct vm_fault* vmf)
{
    // если в user space был отмаплен буфер с помощью функции mmap, в которую передали размер буфера 65536,
    // то здесь (vma->vm_end - vma->vm_start) как раз будет равно 65536,
    // то есть делаю вывод, что vma соотносится с созданным буфером в user space.
    struct page* page;
    struct mmap_info* info;

    info = (struct mmap_info*)vma->vm_private_data;
    if (!info->data)
    {
        printk( KERN_INFO "[ag_pcie_driver]:[mmap_fault]: no data");
        return 0;
    }
    
    //get the page
    page = virt_to_page(info->data);
    // increment the reference count of this page
    get_page(page);
    
    vmf->page = page;
    return 0;
}

struct vm_operations_struct mmap_vm_ops =
{
    .open = mmap_open,
    .close = mmap_close,
    .fault = mmap_fault,
};

int mmapfop_mmap(struct file* filp, struct vm_area_struct* vma)
{
    vma->vm_ops = &mmap_vm_ops;
    vma->vm_flags |= VM_RESERVED;
    vma->vm_private_data = filp->private_data;
    mmap_open(vma);
    return 0;
}

int mmapfop_close(struct inode* inode, struct file* filp)
{
    filp->private_data = NULL;
    return 0;
}

int mmapfop_open(struct inode* inode, struct file* filp)
{
    struct mmap_info* info = kmalloc(sizeof(struct mmap_info),GFP_KERNEL);
    info->data = dma_buf_virt_core_addr_ptr;
    filp->private_data = info;
    return 0;
}

static const struct file_operations mmap_fops =
{
    .owner = THIS_MODULE,
    .open = mmapfop_open,
    .release = mmapfop_close,
    .mmap = mmapfop_mmap
};

static struct miscdevice ag_user_struct_miscdevice3 = 
{
    MISC_DYNAMIC_MINOR,
    "ag_pcie_driver3",
    &mmap_fops
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int ag_pciedr_probe( struct pci_dev *dev, const struct pci_device_id *id )
{
   ....
   // выделяю буфер в пространстве ядра
   dma_buf_virt_core_addr_ptr = pci_alloc_consistent( dev, 4194304, &dma_bus_addr_for_fpga);
   
   // передаю в ПЛИС шинный адрес
   iowrite32( (u32)((dma_bus_addr_for_fpga))               ,  0x10);    //low
   iowrite32( (u32)((dma_bus_addr_for_fpga>>32)&0xffffffff), 0x14);    //high
   ....
}


........

 

 

В user space открываю устройство и маплю буфер:

ssize_t g_file_fid3 = open ("/dev/ag_pcie_driver3", O_RDWR);

char* g_ptr_mmap3 = (char*)mmap( NULL, 65536, PROT_READ | PROT_WRITE, MAP_SHARED, g_file_fid3, 0);

uint32_t* ptr = (uint32_t*)g_ptr_mmap3;  // для удобства

 

 

Теперь засылаю данные из ПЛИС на комп.

Засылаю, например, 100 слов по 32 бит, начиная с 0 адреса.

В ядре вывожу на печать содержимое ядерного буфера dma_buf_virt_core_addr_ptr.

В нем все нормально - первые 100 слов из ПЛИС, дальше все нули.

 

Печатаю в user space буфер ptr с 0 по 4096 слова.

Вижу первые 100 слов из ПЛИС, дальше нули до 1024 слова.

С 1024 слова опять 100 слов из ПЛИС, дальше нули до 2048 слова.

И т.д.

Как будто первая страница в 4096 Байт (а это 1024 слова по 32 бит) циклически выделяется и повторяется для всего моего буфера g_ptr_mmap3.

 

 

Само отображение ядерного буфера в пользовательский буфер происходит в методе mmap_fault.

Если я обращаюсь к g_ptr_mmap3, то я попадаю в mmap_fault.

 

Мой ядерный адрес лежит в info->data.

Преобразование адреса в номер страницы происходит в строке

page = virt_to_page(info->data);

 

И получается, что я всегда нахожу первую страницу для моего ядерного адреса.

Как я думаю, нужно как то добавить нужное смещение в эту строчку и все заработает.

Даже нашел, что в переменной vmf->pgoff хранится номер страницы пользовательского буфера, к которой я и обращаюсь.

Но правильно реализовать мозгов не хватает...

 

Пробовал просто добавить смещение в строчку

page = virt_to_page(info->data+4096);

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

 

Подскажите, как правильно отмапить нужную страницу, осталось то вроде чуть чуть.

 

 

 

 

 

Share this post


Link to post
Share on other sites

Разобрался с мапингом :smile3009: . Выкинул метод fault, оставил mmap, в нем применил remap_pfn_range.

Все работает. Отмапил 4 МБт. Из плис записываю в эти 4МБт счетчик. Затем в юзер спейсе этот счетчик проверяю - все верно.

 

Вот мой код, может кому пригодится:

#define AG_DEF_DMA_SIZE 4194304

static u64* dma_buf_virt_core_addr_ptr;
static dma_addr_t  dma_bus_addr_for_fpga;

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Мапинг буфера, созданного в виртуальном пространстве ядра с помощью функции dma_alloc_coherent(),
// в виртуальное пространство пользователя
static int kernel_buf_mmap(struct file *file, struct vm_area_struct *vma)
{
    if (remap_pfn_range( vma, vma->vm_start, virt_to_phys((void *)dma_buf_virt_core_addr_ptr) >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
    {
        return -EAGAIN;
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
int kernel_buf_open(struct inode *inode, struct file *file)
{
    printk( KERN_INFO "[ag_pcie_driver]:[kernel_buf_open]: kernel_buf_open success");
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
static const struct file_operations kernel_buf_fops =
{
    .owner          = THIS_MODULE,
    .open            = kernel_buf_open,
    .mmap            = kernel_buf_mmap

};

//////////////////////////////////////////////////////////////////////////////////////////////////////
static struct miscdevice ag_user_struct_miscdevice_kernel_buf =
{
    MISC_DYNAMIC_MINOR,
    "ag_pcie_driver_kernel_buf",
    &kernel_buf_fops
};


//********************************************************************//
// Зондирующая функция в драйвере PCI
static int ag_pciedr_probe( struct pci_dev *dev, const struct pci_device_id *id )
{
    .......
    dma_buf_virt_core_addr_ptr = dma_alloc_coherent( &dev->dev, AG_DEF_DMA_SIZE, &dma_bus_addr_for_fpga, GFP_KERNEL);
    .......
}


//********************************************************************//
static int __init ag_pcie_driver_init(void)
{
    ..........
    res = misc_register(&ag_user_struct_miscdevice_kernel_buf);
    ..........
}
    
//*********************************************************************//
static void __exit ag_pcie_driver_exit(void)
{
    ..........
    misc_deregister(&ag_user_struct_miscdevice_kernel_buf);
    ..........
}

 

Осталось разобраться, как отмапить больше 4 МБайт.

Share this post


Link to post
Share on other sites
Вопрос 1. Функция pci_alloc_consistent завершается удачно, если размер запрашиваемого буфера не превышает 4 МБайт. С чем это связано, почему не удаётся получить 8 и больше МБайт?

 

Может задачу озвучите?

На опенкорес есть проект, там через cmem загоняют непрерывные потоки данных с pcie.

Драйвера там фактически два:

cmem - "Its purpose is to provide user applications with contiguous data",

wupper - драйвер pcie-железки на плис 7 серии от xilinx,

исходники присутствуют.

 

Share this post


Link to post
Share on other sites

Все еще не решил проблему с ограничением в 4 МБайт при выделении буфера функцией dma_alloc_coherent. В интернетах пишут про следующие способы:

1. С помощью опции mem для ядра.

Попытался воспользоваться этим.

Для grub добавил строчку mem=512M. После ребута все стало сильно тормозить. Я так понял этой строчкой я указал линуксу использовать только 512M в качестве ОЗУ.

Поставил mem=2G. Тормоза исчезли, но буфер в 8МБайт не выделился.

Еще пробовал ставить mem=2G memmap=2G$3G. Тоже не помогло.

С помощью mem опции вообще можно 8мбайт выделить?

 

2. Второй способ - увеличить параметр MAX_ORDER=11 в исходниках ядра и перекомпилить ядро.

Пишут, что ядро может стать не стабильным.

Завтра этот способ проверю.

 

3. Применить патчи для ядра. Как это делать даже не знаю.

 

 

Может кто что посоветует, подскажет?

 

 

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

С помощью dma_alloc_coherent мне это удалось сделать.

Сейчас драйвер уже не трогал с месяц. Все в нем работает.

А вот ограничение в 4мбайт доставляет, хочется большего.

 

cmem еще вечером посмотрю, что они там делают.

Share this post


Link to post
Share on other sites
А вот ограничение в 4мбайт доставляет, хочется большего.

я вам в конце января советовал:

http://electronix.ru/forum/index.php?showt...t&p=1400061

не увидели? пробовали? не ваш случай?

Share this post


Link to post
Share on other sites
я вам в конце января советовал:

http://electronix.ru/forum/index.php?showt...t&p=1400061

не увидели? пробовали? не ваш случай?

 

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

я с линуксом, как муравей вокруг слона...

 

У меня стоит opensuse 13.2 с ядром 3.16.

Как узнать, есть там cma или нету?

Share this post


Link to post
Share on other sites
видел, не пробовал, может и мой случай, ничего не понял, как этот CMA в ядре использовать,

я с линуксом, как муравей вокруг слона...

У меня стоит opensuse 13.2 с ядром 3.16.

Как узнать, есть там cma или нету?

zcat /proc/config.gz | grep CMA

 

в menuconfig ядра нажмите правый слеш "/" и там слово для поиска CMA

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

Share this post


Link to post
Share on other sites

Jury093 спасибо за CMA, с этой опцией все заработало, выделил буфер больше 4МБ.

 

Как то не особо много на русском написано про CMA, поэтому распишу немного как я это делал.

 

По умолчанию мой Линукс оказался без включенного CMA.

Пришлось компилить свое ядро из исходников.

Для opensuse 13.2 установил через yast2 исходники с установочного диска

(В установочнике яста нужно ввести kernel, в списке выбрать kernel-source).

Исходники будут лежать по пути usr/src/linux...

В этой папке даем команду

make menuconfig

В появившемся конфигураторе переходим Processor type and features,

тут находим пункт Contiguous Memory Allocator, помечаем его.

Также помечаем CMA debug messages.

Переходим от корня в Device Drivers->Generic Driver Options,

помечаем пункт DMA Contiguous Memory Allocator,

появятся еще пункты.

Увеличил размер до 256 size in megabytes.

Остальное не менял.

Еще можно поменять имя для собираемого ядра:

General setup->Local version, чтобы потом не путаться.

Сохраняем настройки и выходим.

 

Теперь компилим ядро:

make bzImage

make modules

make modules_install

make install

Процесс не быстрый.

После компиляции в папке /boot/ должны появиться образы нового ядра с заданным именем.

Также в /boot/grub2/grub.cfg должен автоматически добавиться пункт меню для загрузки нового ядра.

В этот пукнт меню в опции ядра добавил строчку CMA=256M.

 

Перезагружаемся в новое ядро.

Если сделать dmesg, то можно будет найти записи о CMA, при этом Линукс начинает черпать память из тех 256 МБ и для других дма-шных устройств.

 

Никаких изменений в самом драйвере делать не нужно.

Теперь функция dma_alloc_coherent сможет выделить до 256 МБ.

У меня сначала ругалось, не хотело выделять больше 4МБ, потому что флаг в функции стоял GFP_DMA, заменил на GFP_KERNEL и выделил 128МБ. 256МБ выделить не смог, я так понял из-за того, что часть памяти CMA линукс отдал другим устройствам.

 

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

На 16МБ буфере тест прошел.

 

 

Share this post


Link to post
Share on other sites

Спасибо. Полезная информация.

 

По поводу патчей:

Вот что надо знать для понимания.

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

Команда для превращения одного проекта во второй -- "patch":

http://linux.die.net/man/1/patch

 

 

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this