Как получить структуру страницы с любого адреса в ядре Linux - PullRequest
25 голосов
/ 12 мая 2011

У меня есть существующий код, который принимает список 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 проверяет, является ли адрес действительным виртуальным адресом (fodder для virt_to_page; сюда входит kmalloc(GFP_KERNEL) и стеки ядра). А как насчет других случаев: глобальные буферы, большой объем памяти (это придет однажды, хотя я могу пока игнорировать это), возможно, другие виды, о которых я не знаю? Так что я мог бы переформулировать свой вопрос как:

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

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

Ответы [ 5 ]

13 голосов
/ 23 мая 2011

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

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 для меня.

7 голосов
/ 23 мая 2011

Сопоставление адресов со структурной страницей

Для 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.

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

1 голос
/ 04 июня 2015

Для 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);
1 голос
/ 27 августа 2014

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

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

1 голос
/ 16 мая 2011

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

...