14
votes

I am reading about Linux memory management. I know that

The Linux kernel is responsible for creating and maintaining page tables but employs the CPU’s memory management unit (MMU) to translate the virtual memory accesses of a process into corresponding physical memory accesses.

But, I also know kernel can use some its functions to manage memory such as virt_to_phys(), virt_to_page(), __pa(), ...

Example:

static inline unsigned long virt_to_phys(volatile void *address)
{
    return __pa(address);
}

used to translate a virtual address to the physical address.

I am very confused about them. Please help to show me the relations between MMU's translation and kernel's translation and distinguish them?

6

6 Answers

10
votes

In systems with virtual memory the OS kernel is responsible for establishing the mapping between physical and virtual addresses.

However, when the CPU executes instructions that access memory, the CPU performs the translation from the process's virtual address to the physical address that indicates the actual location in memory.

The functions you mentioned can be used inside kernel code to get the translation for virtual to physical addresses for some addresses that are used in kernel code. For example for the x86 target you can see the definition on virt_to_phys in io.h:

/**
 *  virt_to_phys    -   map virtual addresses to physical
 *  @address: address to remap
 *
 *  The returned physical address is the physical (CPU) mapping for
 *  the memory address given. It is only valid to use this function on
 *  addresses directly mapped or allocated via kmalloc.
 *
 *  This function does not give bus mappings for DMA transfers. In
 *  almost all conceivable cases a device driver should not be using
 *  this function
 */

static inline phys_addr_t virt_to_phys(volatile void *address)
{
    return __pa(address);
}

and if you follow the definition of __pa(address) you will see that it ends up calling __phys_addr which is defined as:

unsigned long __phys_addr(unsigned long x)
{
    if (x >= __START_KERNEL_map) {
        x -= __START_KERNEL_map;
        VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE);
        x += phys_base;
    } else {
        VIRTUAL_BUG_ON(x < PAGE_OFFSET);
        x -= PAGE_OFFSET;
        VIRTUAL_BUG_ON(!phys_addr_valid(x));
    }
    return x;
}

So you can see the kernel is calculating the physical address from the virtual address using an offset. Depending on the architecture that the code is compiled for the translation will be different. And as the comment for virt_to_phys mentions, this only works for memory in the kernel that is directly mapped or allocated via kmalloc it does not translate arbitrary physical to virtual addresses. That translation relies on looking up the page table mapping.

In either case the actual instructions that execute on the CPU as part of the kernel will still rely on the CPU's MMU to translate from the virtual addresses that they operate on to the physical addresses where the data is actually located in memory.

2
votes

MMU address translation is a hardware(cpu) behavior. The translation must be done as physical address is the valid address that could be used by hardware to access the memory. On the other hand, kernel function like va_to_pa() is used to convert kernel logical address(va) to physical address(pa), which means kernel uses virtual address other than physical address, although it is just constant shift between va and pa.

Kernel instruction and data are in a virtual address, but kernel use physical address to do a lot of thing, such as preparing the page table entry, get dma address for device and soon. So kernel needs functions like va_to_pa().

1
votes

From my understanding, any use of the physical address on the kernel side is simply for reference and will most likely just be translated back for any actual changes/moves. There are a number of reasons for that, one being that the memory may be re-allocated for another purpose, IE move the data from physical memory to page file (on-disk memory), or moved within the physical memory allocation if more space is needed for that data. Thus it would not be good to use the physical address if the kernel is just going to move it without telling you. Here is an interesting bit on the subject. And a lot more detail here.

1
votes

The virt_to_phys() and others actually makes use of different attributes of Page tables, like PAGE_OFFSET etc. These Page tables are created by the kernel's memory management subsystem, but they in-turn make use of the MMU hardware to read/write a physical page in the main memory.

You can also refer different documentations available, one of them being: https://www.kernel.org/doc/gorman/html/understand/understand006.html

1
votes

Here are the points.

1.) Kernel and other soft works in terms of virtual addresses. Everytime for finding corresponding physical address hardware page tables lookup (or TLB fetch) is needed.

2.) At the boot time kernel establishes virtual mapping: for simplicity i will say that it maps memory at addresses 0x0 .. n to 0xc0000000 .. 0xc0000000 + n (so called low memory).

3.) Established mapping is static. For lowmem addresses the following functions are suitable:

virt_to_page(), __pa(), ...

It means that

virtual address = physical address + some offset

So you can easily get phys/virt address for corresponding lowmem page in kernel code (MMU uses generic mechanisms i.e. page table walk everytime). This offset is just convention, nothing more.

0
votes

You can read Understanding Linux Kernel book to get more information.