pfn_to_page and page_to_pfn

###summary
page_to_pfn and pfn_to_page are often used in kernel.

for PP81, kernel uses CONFIG_DISCONTIGMEM + NUMA.
Every node has node_mem_map and node_start_pfnto help the page/pfn covertion.
node_start_pfn is the first page’s pfn of this node.
node_mem_map stores all the struct page of this node.

so thete is a map between them:

`node_start_pfn + i` <===> `node_mem_map[i]`

The key is how to get node id by pfn or page.
the corresponding function is pfn_to_nid and page_to_nid

page_to_nid is simpile.
the node id is store in struct page ->flags
while pfn_to_nid(for pp81), it convert to pageaddress and then turn to pa_to_nid.

感觉这个实现有点罗嗦!
直接在include/asm-generic/memory_model.h 把pa_to_nid 定义一个 arch_pfn_to_nid宏。
省得pfn转为pa以后,又转为pfn。

pfn_to_page

funtion defination:
include/asm-generic/memory_model.h

1
73 #define pfn_to_page __pfn_to_page
1
2
3
4
5
6
7
33 #elif defined(CONFIG_DISCONTIGMEM)
34
35 #define __pfn_to_page(pfn) \
36 ({ unsigned long __pfn = (pfn); \
37 unsigned long __nid = arch_pfn_to_nid(__pfn); \
38 NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\
39 })

arch_local_page_offset

1
2
3
4
18 #ifndef arch_local_page_offset
19 #define arch_local_page_offset(pfn, nid) \
20 ((pfn) - NODE_DATA(nid)->node_start_pfn)
21 #endif

arch_pfn_to_nid

call trace:

1
2
3
arch_pfn_to_nid 
--> pfn_to_nid
-->pa_to_nid((pfn) << PAGE_SHIFT)
1
2
3
14 #ifndef arch_pfn_to_nid
15 #define arch_pfn_to_nid(pfn) pfn_to_nid(pfn)
16 #endif

arch/mips/include/asm/mmzone.h

1
2
3
11 #ifdef CONFIG_DISCONTIGMEM
12
13 #define pfn_to_nid(pfn) pa_to_nid((pfn) << PAGE_SHIFT)

arch/mips/include/asm/mach-netlogic/mmzone.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 52 static inline unsigned int pa_to_nid(unsigned long addr)
53 {
54 unsigned int i;
55 unsigned long pfn = addr >> PAGE_SHIFT;
56
57 /* TODO: Implement this using NODE_DATA */
58 for (i = 0; i < NLM_MAX_CPU_NODE; i++) {
59
60 if ((!node_online(i)) || ((NODE_MEM_DATA(i)->low_pfn == 0) && (NODE_MEM_DATA(i)->high_pfn == 0)))
61 continue;
62
63 if (pfn >= NODE_MEM_DATA(i)->low_pfn && pfn <= NODE_MEM_DATA(i)->high_pfn)
64 return i;
65 }
....

page_to_pfn

include/asm-generic/memory_model.h

1
72 #define page_to_pfn __page_to_pfn
1
2
3
4
5
6
41 #define __page_to_pfn(pg)                                               \
42 ({ struct page *__pg = (pg); \
43 struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg)); \
44 (unsigned long)(__pg - __pgdat->node_mem_map) + \
45 __pgdat->node_start_pfn; \
46 })

page_to_nid

1
2
3
4
5
6
7
8
538 #ifdef NODE_NOT_IN_PAGE_FLAGS
539 extern int page_to_nid(struct page *page);
540 #else
541 static inline int page_to_nid(struct page *page)
542 {
543 return (page->flags >> NODES_PGSHIFT) & NODES_MASK;
544 }
545 #endif