Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a struct page from any address in the Linux kernel

I have existing code that takes a list of struct page * and builds a descriptor table to share memory with a device. The upper layer of that code currently expects a buffer allocated with vmalloc or from user space, and uses vmalloc_to_page to obtain the corresponding struct page *.

Now the upper layer needs to cope with all kinds of memory, not just memory obtained through vmalloc. This could be a buffer obtained with kmalloc, a pointer inside the stack of a kernel thread, or other cases that I'm not aware of. The only guarantee I have is that the caller of this upper layer must ensure that the memory buffer in question is mapped in kernel space at that point (i.e. it is valid to access buffer[i] for all 0<=i<size at this point). How do I obtain a struct page* corresponding to an arbitrary pointer?

Putting it in pseudo-code, I have this:

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);     } } 

and I now need to change upper_layer to cope with any valid buffer (without changing lower_layer).

I've found virt_to_page, which Linux Device Drivers indicates operates on “a logical address, [not] memory from vmalloc or high memory”. Furthermore, is_vmalloc_addr tests whether an address comes from vmalloc, and virt_addr_valid tests if an address is a valid virtual address (fodder for virt_to_page; this includes kmalloc(GFP_KERNEL) and kernel stacks). What about other cases: global buffers, high memory (it'll come one day, though I can ignore it for now), possibly other kinds that I'm not aware of? So I could reformulate my question as:

  1. What are all the kinds of memory zones in the kernel?
  2. How do I tell them apart?
  3. How do I obtain page mapping information for each of them?

If it matters, the code is running on ARM (with an MMU), and the kernel version is at least 2.6.26.

like image 561
Gilles 'SO- stop being evil' Avatar asked May 12 '11 17:05

Gilles 'SO- stop being evil'


People also ask

What is a struct page in Linux?

Linux manages physical memory by dividing it into PAGE_SIZE pieces. Usually this is the same as the CPU's page size, between 4KiB and 64KiB. Each page has a small data structure (about 64 bytes) called struct page, which contains various pieces of information about the page.

How the pages are addressed in Linux?

Linux uses a virtual memory system where all of the addresses are virtual addresses and not physical addresses. These virtual addresses are converted into physical addresses by the processor. To make this translation easier, virtual and physical memory are divided into pages.

Does kernel have its own address space?

The kernel address space is statically mapped into the address space. The top 1 GB of the user's space is reserved for system elements while the bottom 1 GB holds the user code, data, stack, and heap.


2 Answers

I guess what you want is a page table walk, something like (warning, not actual code, locking missing etc):

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); 

But you you should be very very careful with this. the kmalloc address you got might very well be not page aligned for example. This sounds like a very dangerous API to me.

like image 193
gby Avatar answered Sep 22 '22 14:09

gby


Mapping Addresses to a struct page

There is a requirement for Linux to have a fast method of mapping virtual addresses to physical addresses and for mapping struct pages to their physical address. Linux achieves this by knowing where, in both virtual and physical memory, the global mem_map array is because the global array has pointers to all struct pages representing physical memory in the system. All architectures achieve this with very similar mechanisms, but, for illustration purposes, we will only examine the x86 carefully.

Mapping Physical to Virtual Kernel Addresses

any virtual address can be translated to the physical address by simply subtracting PAGE_OFFSET, which is essentially what the function virt_to_phys() with the macro __pa() does:

/* 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 } 

Obviously, the reverse operation involves simply adding PAGE_OFFSET, which is carried out by the function phys_to_virt() with the macro __va(). Next we see how this helps the mapping of struct pages to physical addresses.

There is one exception where virt_to_phys() cannot be used to convert virtual addresses to physical ones. Specifically, on the PPC and ARM architectures, virt_to_phys() cannot be used to convert addresses that have been returned by the function consistent_alloc(). consistent_alloc() is used on PPC and ARM architectures to return memory from non-cached for use with DMA.

What are all the kinds of memory zones in the kernel? <---see here

like image 39
Lars Avatar answered Sep 21 '22 14:09

Lars