Как отобразить страницы, закрепленные get_user_pages_fast, чтобы использовать их в качестве практически непрерывного буфера в драйвере? - PullRequest
0 голосов
/ 05 июля 2019

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

  • Приложение доставляет адрес буфера для приема данных через ioctl
  • Драйвер использует get_user_pages_fast для обеспечения доступа ядра к выходным даннымбуфер, vmap используется для создания области виртуальной памяти для доступа к буферу в ядре
  • Драйвер заполняется полученными данными в прерываниях
  • После получения всех данных,ядро удаляет сопоставление (с помощью vunmap) и возвращает пользовательские страницы, наконец, оно уведомляет приложение о доступности данных.

Ниже приведены основные части кода (извлеченные из моегои упрощенный):

struct page ** pages = NULL;
long pinned = 0;
int i;
void * vbuf = NULL;
void * vresp = NULL;

if (!access_ok(VERIFY_WRITE, buffer, max_data_length)) {
   pr_alert("wrong access");
   res = -EFAULT;
   goto error1;
}
const unsigned long offset = ((unsigned long) buffer) & (PAGE_SIZE-1);
    int nr_pages = DIV_ROUND_UP(offset + max_data_length, PAGE_SIZE);
pages = (struct page **) kzalloc(sizeof(struct page)*nr_pages, GFP_KERNEL);
if(!pages) {
    pr_alert("can't alloc pages");
    res = -EFAULT;
    goto1;
}
pinned = get_user_pages_fast(((unsigned long) buffer ) & PAGE_MASK,nr_pages,1,pages);
if(pinned != nr_pages) {
    for(i=0; i<pinned; i++) {
        put_page(pages[i]);
    }
    kfree(pages);
    pr_alert("can't pin pages");
    res = -EFAULT;
    goto error1;
}
vbuf = vmap(pages,nr_pages,VM_MAP, pgprot_writecombine(PAGE_KERNEL));
vresp = vbuf + offset;
if(!vbuf) {
    pr_alert("can't vmap pages");
    res = -EFAULT;
    goto error1;
}

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

int i;
vunmap(vbuf);
for(i=0; i < nr_pages; i++) {
   set_page_dirty(pages[i]);
   put_page(pages[i]);
}
kfree(pages);

Исходный код работал в нескольких архитектурах, но не работает на многоядерном компьютере ARM.Похоже, что данные, записанные в буфер, обозначенный vresp, не видны в buffer в приложении пользовательского пространства.Я добавил контрольные отпечатки в коде и проверил, что адреса верны.Правильно ли использовать vmap для создания непрерывного отображения страниц, доставляемых get_user_pages_fast?Может быть, я должен использовать другой флаг, чем VM_MAP или другую защиту, чем pgprot_writecombine(PAGE_KERNEL)?

1 Ответ

0 голосов
/ 06 июля 2019

Сегодня я нашел ответ. Действительно, проблема связана с аргументом prot в функции vmap. Он должен быть установлен на PAGE_KERNEL вместо pgprot_writecombine(PAGE_KERNEL)). В приложении пользовательского пространства эта память доступна через кеш, поэтому если я создал отображение с кешем, частично отключенным через pgprot_writecombine, это привело к несогласованному доступу к памяти. Я изменил строку отображения на:

vbuf = vmap(pages,nr_pages,VM_MAP,PAGE_KERNEL);

и код работает правильно даже на многопроцессорной ARM.

...