]>
Commit | Line | Data |
---|---|---|
133ff0ea JG |
1 | /* |
2 | * Copyright 2013 Red Hat Inc. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * Authors: Jérôme Glisse <jglisse@redhat.com> | |
15 | */ | |
16 | /* | |
17 | * Heterogeneous Memory Management (HMM) | |
18 | * | |
19 | * See Documentation/vm/hmm.txt for reasons and overview of what HMM is and it | |
20 | * is for. Here we focus on the HMM API description, with some explanation of | |
21 | * the underlying implementation. | |
22 | * | |
23 | * Short description: HMM provides a set of helpers to share a virtual address | |
24 | * space between CPU and a device, so that the device can access any valid | |
25 | * address of the process (while still obeying memory protection). HMM also | |
26 | * provides helpers to migrate process memory to device memory, and back. Each | |
27 | * set of functionality (address space mirroring, and migration to and from | |
28 | * device memory) can be used independently of the other. | |
29 | * | |
30 | * | |
31 | * HMM address space mirroring API: | |
32 | * | |
33 | * Use HMM address space mirroring if you want to mirror range of the CPU page | |
34 | * table of a process into a device page table. Here, "mirror" means "keep | |
35 | * synchronized". Prerequisites: the device must provide the ability to write- | |
36 | * protect its page tables (at PAGE_SIZE granularity), and must be able to | |
37 | * recover from the resulting potential page faults. | |
38 | * | |
39 | * HMM guarantees that at any point in time, a given virtual address points to | |
40 | * either the same memory in both CPU and device page tables (that is: CPU and | |
41 | * device page tables each point to the same pages), or that one page table (CPU | |
42 | * or device) points to no entry, while the other still points to the old page | |
43 | * for the address. The latter case happens when the CPU page table update | |
44 | * happens first, and then the update is mirrored over to the device page table. | |
45 | * This does not cause any issue, because the CPU page table cannot start | |
46 | * pointing to a new page until the device page table is invalidated. | |
47 | * | |
48 | * HMM uses mmu_notifiers to monitor the CPU page tables, and forwards any | |
49 | * updates to each device driver that has registered a mirror. It also provides | |
50 | * some API calls to help with taking a snapshot of the CPU page table, and to | |
51 | * synchronize with any updates that might happen concurrently. | |
52 | * | |
53 | * | |
54 | * HMM migration to and from device memory: | |
55 | * | |
56 | * HMM provides a set of helpers to hotplug device memory as ZONE_DEVICE, with | |
57 | * a new MEMORY_DEVICE_PRIVATE type. This provides a struct page for each page | |
58 | * of the device memory, and allows the device driver to manage its memory | |
59 | * using those struct pages. Having struct pages for device memory makes | |
60 | * migration easier. Because that memory is not addressable by the CPU it must | |
61 | * never be pinned to the device; in other words, any CPU page fault can always | |
62 | * cause the device memory to be migrated (copied/moved) back to regular memory. | |
63 | * | |
64 | * A new migrate helper (migrate_vma()) has been added (see mm/migrate.c) that | |
65 | * allows use of a device DMA engine to perform the copy operation between | |
66 | * regular system memory and device memory. | |
67 | */ | |
68 | #ifndef LINUX_HMM_H | |
69 | #define LINUX_HMM_H | |
70 | ||
71 | #include <linux/kconfig.h> | |
72 | ||
73 | #if IS_ENABLED(CONFIG_HMM) | |
74 | ||
75 | ||
76 | /* | |
77 | * hmm_pfn_t - HMM uses its own pfn type to keep several flags per page | |
78 | * | |
79 | * Flags: | |
80 | * HMM_PFN_VALID: pfn is valid | |
81 | * HMM_PFN_WRITE: CPU page table has write permission set | |
82 | */ | |
83 | typedef unsigned long hmm_pfn_t; | |
84 | ||
85 | #define HMM_PFN_VALID (1 << 0) | |
86 | #define HMM_PFN_WRITE (1 << 1) | |
87 | #define HMM_PFN_SHIFT 2 | |
88 | ||
89 | /* | |
90 | * hmm_pfn_t_to_page() - return struct page pointed to by a valid hmm_pfn_t | |
91 | * @pfn: hmm_pfn_t to convert to struct page | |
92 | * Returns: struct page pointer if pfn is a valid hmm_pfn_t, NULL otherwise | |
93 | * | |
94 | * If the hmm_pfn_t is valid (ie valid flag set) then return the struct page | |
95 | * matching the pfn value stored in the hmm_pfn_t. Otherwise return NULL. | |
96 | */ | |
97 | static inline struct page *hmm_pfn_t_to_page(hmm_pfn_t pfn) | |
98 | { | |
99 | if (!(pfn & HMM_PFN_VALID)) | |
100 | return NULL; | |
101 | return pfn_to_page(pfn >> HMM_PFN_SHIFT); | |
102 | } | |
103 | ||
104 | /* | |
105 | * hmm_pfn_t_to_pfn() - return pfn value store in a hmm_pfn_t | |
106 | * @pfn: hmm_pfn_t to extract pfn from | |
107 | * Returns: pfn value if hmm_pfn_t is valid, -1UL otherwise | |
108 | */ | |
109 | static inline unsigned long hmm_pfn_t_to_pfn(hmm_pfn_t pfn) | |
110 | { | |
111 | if (!(pfn & HMM_PFN_VALID)) | |
112 | return -1UL; | |
113 | return (pfn >> HMM_PFN_SHIFT); | |
114 | } | |
115 | ||
116 | /* | |
117 | * hmm_pfn_t_from_page() - create a valid hmm_pfn_t value from struct page | |
118 | * @page: struct page pointer for which to create the hmm_pfn_t | |
119 | * Returns: valid hmm_pfn_t for the page | |
120 | */ | |
121 | static inline hmm_pfn_t hmm_pfn_t_from_page(struct page *page) | |
122 | { | |
123 | return (page_to_pfn(page) << HMM_PFN_SHIFT) | HMM_PFN_VALID; | |
124 | } | |
125 | ||
126 | /* | |
127 | * hmm_pfn_t_from_pfn() - create a valid hmm_pfn_t value from pfn | |
128 | * @pfn: pfn value for which to create the hmm_pfn_t | |
129 | * Returns: valid hmm_pfn_t for the pfn | |
130 | */ | |
131 | static inline hmm_pfn_t hmm_pfn_t_from_pfn(unsigned long pfn) | |
132 | { | |
133 | return (pfn << HMM_PFN_SHIFT) | HMM_PFN_VALID; | |
134 | } | |
135 | ||
136 | ||
137 | /* Below are for HMM internal use only! Not to be used by device driver! */ | |
138 | void hmm_mm_destroy(struct mm_struct *mm); | |
139 | ||
140 | static inline void hmm_mm_init(struct mm_struct *mm) | |
141 | { | |
142 | mm->hmm = NULL; | |
143 | } | |
144 | ||
145 | #else /* IS_ENABLED(CONFIG_HMM) */ | |
146 | ||
147 | /* Below are for HMM internal use only! Not to be used by device driver! */ | |
148 | static inline void hmm_mm_destroy(struct mm_struct *mm) {} | |
149 | static inline void hmm_mm_init(struct mm_struct *mm) {} | |
150 | ||
151 | #endif /* IS_ENABLED(CONFIG_HMM) */ | |
152 | #endif /* LINUX_HMM_H */ |