As I understand, when an userspace process accesses to some virtual address, the MMU tries to find the PTE for the requested virtual address. In the PTE there is the encoded struct page's PFN and some flags.

At this point, my question is: if the translated address points to a struct page, how exactly is physical memory is accessed? I think struct page is just page descriptor, not a empty physical memory region.


when an userspace process accesses to some virtual address, the MMU tries to find the PTE for the requested virtual address

Yes, this is correct. The page table is walked down to the specific PTE (if any).

In the PTE there is the encoded struct page's PFN and some flags.

Not really. The PTE contains the PFN (Page Frame Number) of the actual physical memory page that the virtual address translates to. In other words, it points to the actual page in physical memory, not to the corresponding struct page.

if the translated address points to a struct page, how exactly is physical memory is accessed? I think struct page is just page descriptor, not a empty physical memory region.

The translated address does not point to a struct page, it points to physical memory. Indeed, the struct page is just a "descriptor" used by the system to keep track of the nature and state of a page, and is stored somewhere else.

All the struct page structures are stored in some specific memory area which depends on the underlying architecture. You can read more about it in Chapter 2: Describing Physical Memory of Mel Gorman's book "Understanding the Linux Virtual Memory Manager".

Once you have a PTE (pte_t), the pte_page() macro can be used to get the address of the corresponding struct page. This address is calculated using a set of macros (e.g. __pfn_to_page()) which basically end up indexing a mem_section which contains a pointer to an array of struct page (.section_mem_map). There is a global array of struct mem_section, and each PTE PFN has a section index encoded in it, which is used to select the correct section.