Как получить структуру страницы с любого адреса в ядре Linux

У меня есть существующий код, который принимает список struct page * и создает таблицу дескрипторов для совместного использования памяти с устройством. Верхний уровень этого кода в настоящее время ожидает буфер, выделенный vmalloc или из пространства пользователя, и использует vmalloc_to_page получить соответствующий struct page *,

Теперь верхний слой должен справляться со всеми видами памяти, а не только с памятью, полученной через vmalloc, Это может быть буфер, полученный с kmalloc указатель внутри стека потока ядра или другие случаи, о которых я не знаю. Единственная гарантия, которую я имею, состоит в том, что вызывающая сторона этого верхнего уровня должна гарантировать, что рассматриваемый буфер памяти отображается в пространстве ядра в этой точке (то есть он действителен для доступа buffer[i] для всех 0<=i<size с этой точки зрения). Как мне получить struct page* соответствует произвольному указателю?

Положив его в псевдокод, у меня есть это:

lower_layer(struct page*);
upper_layer(void *buffer, size_t size) {
    for (addr = buffer & PAGE_MASK; addr <= buffer + size; addr += PAGE_SIZE) {
        struct page *pg = vmalloc_to_page(addr);
        lower_layer(pg);
    }
}

и теперь мне нужно поменять upper_layer справиться с любым действительным буфером (без изменения lower_layer).

я обнаружил virt_to_page, который указывает Драйверы устройств Linux, работает с "логическим адресом, а не с vmalloc или высокая память ". Более того, is_vmalloc_addr проверяет, приходит ли адрес vmalloc, а также virt_addr_valid проверяет, является ли адрес действительным виртуальным адресом (корм для virt_to_page; Это включает kmalloc(GFP_KERNEL) и ядро ​​стеки). А как насчет других случаев: глобальные буферы, большой объем памяти (это придет однажды, хотя я могу пока игнорировать это), возможно, другие виды, о которых я не знаю? Так что я мог бы переформулировать свой вопрос следующим образом:

  1. Каковы все виды зон памяти в ядре?
  2. Как я могу отличить их?
  3. Как получить информацию о сопоставлении страниц для каждого из них?

Если это имеет значение, код работает на ARM (с MMU), а версия ядра - как минимум 2.6.26.

5 ответов

Я думаю, что вы хотите, это просмотр таблицы страниц, что-то вроде (предупреждение, не реальный код, блокировка отсутствует и т. Д.):

struct mm_struct *mm = current->mm;
pgd = pgd_offset(mm, address);
pmd = pmd_offset(pgd, address);  
pte = *pte_offset_map(pmd, address);  
page = pte_page(pte);

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

Отображение адресов на странице структуры

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

Отображение физических адресов в виртуальные ядра

любой виртуальный адрес можно преобразовать в физический адрес, просто вычтя PAGE_OFFSET, что по сути и делает функция virt_to_phys() с макросом __pa():

/* from <asm-i386/page.h> */
132 #define __pa(x)        ((unsigned long)(x)-PAGE_OFFSET)

/* from <asm-i386/io.h> */
 76 static inline unsigned long virt_to_phys(volatile void * address)
 77 {
 78         return __pa(address);
 79 }

Очевидно, что обратная операция включает в себя простое добавление PAGE_OFFSET, которое выполняется функцией phys_to_virt() с макросом __va(). Далее мы увидим, как это помогает отображению структурных страниц на физические адреса.

Существует одно исключение, когда virt_to_phys() не может использоваться для преобразования виртуальных адресов в физические. В частности, в архитектурах PPC и ARM virt_to_phys() не может использоваться для преобразования адресов, которые были возвращены функцией constant_alloc(). Согласованная_аллока () используется в архитектурах PPC и ARM для возврата памяти из не кешированных для использования с DMA.

Каковы все виды зон памяти в ядре? <--- смотрите здесь

Для пользовательского пространства, выделенного памяти, вы хотите использовать get_user_pages, который выдаст вам список страниц, связанных с памятью malloc, а также увеличит их счетчик ссылок (вам нужно вызвать page_cache_release на каждой странице, однажды сделанной с ними.)

Для страниц vmalloc'd, vmalloc_to_page твой друг, и я не думаю, что тебе нужно что-то делать.

Для 64-битных архитектур ответ gby должен быть адаптирован к:

 pgd_t * pgd;
 pmd_t * pmd;
 pte_t * pte;
 struct page *page = NULL;
 pud_t * pud;
 void * kernel_address;

 pgd = pgd_offset(mm, address);
 pud = pud_offset(pgd, address);
 pmd = pmd_offset(pud, address);
 pte = pte_offset_map(pmd, address);
 page = pte_page(*pte);

 // mapping in kernel memory:
 kernel_address = kmap(page);

 // work with kernel_address....

 kunmap(page);

Вы могли бы попробовать virt_to_page, Я не уверен, что это то, что вы хотите, но, по крайней мере, это где-то, чтобы начать искать.

Другие вопросы по тегам