Подтвердить что ты не робот

Как получить структуру страницы с любого адреса в ядре 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) и стеки ядра). Что касается других случаев: глобальных буферов, высокой памяти (в один прекрасный день, хотя я могу игнорировать это сейчас), возможно, других видов, о которых я не знаю? Поэтому я мог бы переформулировать свой вопрос следующим образом:

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

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

4b9b3361

Ответ 1

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

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.

Ответ 2

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

Существует требование для 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(). constant_alloc() используется для архитектуры PPC и ARM для возврата памяти из не кэшированной для использования с DMA.

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

Для 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);