1 /*******************************************************************************
2     Copyright (c) 2022-2023 NVIDIA Corporation
3 
4     Permission is hereby granted, free of charge, to any person obtaining a copy
5     of this software and associated documentation files (the "Software"), to
6     deal in the Software without restriction, including without limitation the
7     rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8     sell copies of the Software, and to permit persons to whom the Software is
9     furnished to do so, subject to the following conditions:
10 
11         The above copyright notice and this permission notice shall be
12         included in all copies or substantial portions of the Software.
13 
14     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20     DEALINGS IN THE SOFTWARE.
21 
22 *******************************************************************************/
23 
24 #ifndef __UVM_VA_POLICY_H__
25 #define __UVM_VA_POLICY_H__
26 
27 #include <linux/numa.h>
28 #include "uvm_linux.h"
29 #include "uvm_forward_decl.h"
30 #include "uvm_processors.h"
31 #include "uvm_range_tree.h"
32 #include "uvm_va_block_types.h"
33 
34 // This enum must be kept in sync with UVM_TEST_READ_DUPLICATION_POLICY in
35 // uvm_test_ioctl.h
36 typedef enum
37 {
38     UVM_READ_DUPLICATION_UNSET = 0,
39     UVM_READ_DUPLICATION_ENABLED,
40     UVM_READ_DUPLICATION_DISABLED,
41     UVM_READ_DUPLICATION_MAX
42 } uvm_read_duplication_policy_t;
43 
44 typedef enum
45 {
46     UVM_VA_POLICY_PREFERRED_LOCATION = 0,
47     UVM_VA_POLICY_ACCESSED_BY,
48     UVM_VA_POLICY_READ_DUPLICATION,
49 } uvm_va_policy_type_t;
50 
51 //
52 // A policy covers one or more contiguous Linux VMAs or portion of a VMA and
53 // does not cover non-existant VMAs.
54 // The VA range is determined from either the uvm_va_range_t for managed
55 // allocations or the uvm_va_policy_node_t for HMM allocations.
56 //
57 struct uvm_va_policy_struct
58 {
59     // Read duplication policy for this VA range (unset, enabled, or disabled).
60     uvm_read_duplication_policy_t read_duplication;
61 
62     // Processor ID of the preferred location for this VA range.
63     // This is set to UVM_ID_INVALID if no preferred location is set.
64     uvm_processor_id_t preferred_location;
65 
66     // If the preferred location is the CPU, this is either the preferred NUMA
67     // node ID or NUMA_NO_NODE to indicate that there is no preference among
68     // nodes.
69     // If preferred_location is a GPU, preferred_nid will be used if CPU
70     // pages have to be allocated for any staging copies. Otherwise, it is
71     // not used.
72     //
73     // TODO: Bug 4148100 - Preferred_location and preferred_nid should be
74     //       combined into a new type that combines the processor and NUMA node
75     //       ID.
76     int preferred_nid;
77 
78     // Mask of processors that are accessing this VA range and should have
79     // their page tables updated to access the (possibly remote) pages.
80     uvm_processor_mask_t accessed_by;
81 
82 };
83 
84 // Policy nodes are used for storing policies in HMM va_blocks.
85 // The va_block lock protects the tree so that invalidation callbacks can
86 // update the VA policy tree.
87 typedef struct uvm_va_policy_node_struct
88 {
89     // Storage for the policy tree node. It also contains the range start and
90     // end. Start and end + 1 have to be PAGE_SIZED aligned.
91     uvm_range_tree_node_t node;
92 
93     uvm_va_policy_t policy;
94 
95 } uvm_va_policy_node_t;
96 
97 // Function pointer prototype for uvm_hmm_split_as_needed() callback.
98 typedef bool (*uvm_va_policy_is_split_needed_t)(const uvm_va_policy_t *policy, void *data);
99 
100 // Default policy to save uvm_va_policy_node_t space in HMM va_blocks.
101 extern const uvm_va_policy_t uvm_va_policy_default;
102 
103 // Return true if policy is the default policy.
104 static bool uvm_va_policy_is_default(const uvm_va_policy_t *policy)
105 {
106     return policy == &uvm_va_policy_default;
107 }
108 
109 bool uvm_va_policy_is_read_duplicate(const uvm_va_policy_t *policy, uvm_va_space_t *va_space);
110 
111 // Returns the uvm_va_policy_t containing addr or default policy if not found.
112 // The va_block can be either a UVM or HMM va_block.
113 // Locking: The va_block lock must be held.
114 const uvm_va_policy_t *uvm_va_policy_get(uvm_va_block_t *va_block, NvU64 addr);
115 
116 // Same as above but asserts the policy covers the whole region
117 const uvm_va_policy_t *uvm_va_policy_get_region(uvm_va_block_t *va_block, uvm_va_block_region_t region);
118 
119 // Return a uvm_va_policy_node_t given a uvm_va_policy_t pointer.
120 static const uvm_va_policy_node_t *uvm_va_policy_node_from_policy(const uvm_va_policy_t *policy)
121 {
122     return container_of(policy, uvm_va_policy_node_t, policy);
123 }
124 
125 #if UVM_IS_CONFIG_HMM()
126 
127 // Module load/exit
128 NV_STATUS uvm_va_policy_init(void);
129 void uvm_va_policy_exit(void);
130 
131 // Returns the uvm_va_policy_node_t containing addr or NULL if not found.
132 // The va_block must be a HMM va_block.
133 // Locking: The va_block lock must be held.
134 uvm_va_policy_node_t *uvm_va_policy_node_find(uvm_va_block_t *va_block, NvU64 addr);
135 
136 // Split the old node. The old node will end at 'new_end' and the new node will
137 // start at 'new_end' + 1 and end at old end.
138 // The va_block must be a HMM va_block.
139 // Locking: The va_block lock must be held.
140 NV_STATUS uvm_va_policy_node_split(uvm_va_block_t *va_block,
141                                    uvm_va_policy_node_t *old,
142                                    NvU64 new_end,
143                                    uvm_va_policy_node_t **new_ptr);
144 
145 // Move hints from 'old' to 'new' which must both be HMM va_blocks.
146 // The old va_block policies should have been pre-split and since we don't
147 // merge policy ranges, they should still be split after locking/unlocking
148 // 'old'. This should be called after splitting a block.
149 // TODO: Bug 1707562: Add support for merging policy ranges.
150 // Locking: The va_block lock must be held for both old and new.
151 void uvm_va_policy_node_split_move(uvm_va_block_t *old_va_block,
152                                    uvm_va_block_t *new_va_block);
153 
154 // Remove all policy in the given range start/end where 'end' is inclusive.
155 // This function may clear a range larger than start/end if clearing the range
156 // requires memory allocation and the memory allocation fails.
157 // The va_block must be a HMM va_block.
158 // Locking: The va_block lock must be held.
159 void uvm_va_policy_clear(uvm_va_block_t *va_block, NvU64 start, NvU64 end);
160 
161 // Fill in any missing policy nodes for the given range and set the policy
162 // to the given value. The caller is expected to split any policy nodes
163 // before calling this function so the range being set does not.
164 // The va_block must be a HMM va_block.
165 // Note that start and end + 1 must be page aligned, 'end' is inclusive.
166 // TODO: Bug 1707562: Add support for merging policy ranges.
167 // Locking: The va_block lock must be held.
168 NV_STATUS uvm_va_policy_set_range(uvm_va_block_t *va_block,
169                                   NvU64 start,
170                                   NvU64 end,
171                                   uvm_va_policy_type_t which,
172                                   bool is_default,
173                                   uvm_processor_id_t processor_id,
174                                   uvm_read_duplication_policy_t new_policy);
175 
176 // This is an optimized version of uvm_va_policy_set_range() where the caller
177 // guarantees that the the processor_id is not the same as the existing
178 // policy for the given region and that the region doesn't require splitting
179 // the existing policy node 'old_policy'.
180 // Returns the updated policy or NULL if memory could not be allocated.
181 // Locking: The va_block lock must be held.
182 const uvm_va_policy_t *uvm_va_policy_set_preferred_location(uvm_va_block_t *va_block,
183                                                             uvm_va_block_region_t region,
184                                                             uvm_processor_id_t processor_id,
185                                                             const uvm_va_policy_t *old_policy);
186 
187 // Iterators for specific VA policy ranges.
188 
189 // Returns the first policy node in the range [start, end], if any.
190 // The va_block must be a HMM va_block.
191 // Locking: The va_block lock must be held.
192 uvm_va_policy_node_t *uvm_va_policy_node_iter_first(uvm_va_block_t *va_block, NvU64 start, NvU64 end);
193 
194 // Returns the next VA policy following the provided policy in address order,
195 // if that policy's start <= the provided end.
196 // The va_block must be a HMM va_block.
197 // Locking: The va_block lock must be held.
198 uvm_va_policy_node_t *uvm_va_policy_node_iter_next(uvm_va_block_t *va_block, uvm_va_policy_node_t *node, NvU64 end);
199 
200 #define uvm_for_each_va_policy_node_in(node, va_block, start, end)            \
201     for ((node) = uvm_va_policy_node_iter_first((va_block), (start), (end));  \
202          (node);                                                              \
203          (node) = uvm_va_policy_node_iter_next((va_block), (node), (end)))
204 
205 #define uvm_for_each_va_policy_node_in_safe(node, next, va_block, start, end) \
206     for ((node) = uvm_va_policy_node_iter_first((va_block), (start), (end)),  \
207          (next) = uvm_va_policy_node_iter_next((va_block), (node), (end));    \
208          (node);                                                              \
209          (node) = (next),                                                     \
210          (next) = uvm_va_policy_node_iter_next((va_block), (node), (end)))
211 
212 // Returns the first policy in the range [start, end], if any.
213 // Locking: The va_block lock must be held.
214 const uvm_va_policy_t *uvm_va_policy_iter_first(uvm_va_block_t *va_block,
215                                                 NvU64 start,
216                                                 NvU64 end,
217                                                 uvm_va_policy_node_t **out_node,
218                                                 uvm_va_block_region_t *out_region);
219 
220 // Returns the next VA policy following the provided policy in address order,
221 // if that policy's start <= the provided end.
222 // Locking: The va_block lock must be held.
223 const uvm_va_policy_t *uvm_va_policy_iter_next(uvm_va_block_t *va_block,
224                                                const uvm_va_policy_t *policy,
225                                                NvU64 end,
226                                                uvm_va_policy_node_t **inout_node,
227                                                uvm_va_block_region_t *inout_region);
228 
229 // Note that policy and region are set and usable in the loop body.
230 // The 'node' variable is used to retain loop state and 'policy' doesn't
231 // necessarily match &node->policy.
232 #define uvm_for_each_va_policy_in(policy, va_block, start, end, node, region) \
233     for ((policy) = uvm_va_policy_iter_first((va_block), (start), (end), &(node), &(region)); \
234          (policy);                                                              \
235          (policy) = uvm_va_policy_iter_next((va_block), (policy), (end), &(node), &(region)))
236 
237 #else // UVM_IS_CONFIG_HMM()
238 
239 static NV_STATUS uvm_va_policy_init(void)
240 {
241     return NV_OK;
242 }
243 
244 static void uvm_va_policy_exit(void)
245 {
246 }
247 
248 static uvm_va_policy_node_t *uvm_va_policy_node_find(uvm_va_block_t *va_block, NvU64 addr)
249 {
250     UVM_ASSERT(0);
251     return NULL;
252 }
253 
254 static NV_STATUS uvm_va_policy_node_split(uvm_va_block_t *va_block,
255                                           uvm_va_policy_node_t *old,
256                                           NvU64 new_end,
257                                           uvm_va_policy_node_t **new_ptr)
258 {
259     return NV_OK;
260 }
261 
262 static void uvm_va_policy_node_split_move(uvm_va_block_t *old_va_block,
263                                           uvm_va_block_t *new_va_block)
264 {
265 }
266 
267 static void uvm_va_policy_clear(uvm_va_block_t *va_block, NvU64 start, NvU64 end)
268 {
269 }
270 
271 static NV_STATUS uvm_va_policy_set_range(uvm_va_block_t *va_block,
272                                          NvU64 start,
273                                          NvU64 end,
274                                          uvm_va_policy_type_t which,
275                                          bool is_default,
276                                          uvm_processor_id_t processor_id,
277                                          uvm_read_duplication_policy_t new_policy)
278 {
279     return NV_OK;
280 }
281 
282 static uvm_va_policy_node_t *uvm_va_policy_node_iter_first(uvm_va_block_t *va_block, NvU64 start, NvU64 end)
283 {
284     return NULL;
285 }
286 
287 #endif // UVM_IS_CONFIG_HMM()
288 
289 #endif // __UVM_VA_POLICY_H__
290