Я пишу драйвер ядра 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)
?