1 /*
2  * Copyright (c) 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_INCLUDE_KERNEL_MM_DEMAND_PAGING_H
8 #define ZEPHYR_INCLUDE_KERNEL_MM_DEMAND_PAGING_H
9 
10 #include <zephyr/kernel/mm.h>
11 
12 #include <zephyr/sys/util.h>
13 #include <zephyr/toolchain.h>
14 
15 /**
16  * @defgroup demand_paging Demand Paging
17  * @ingroup kernel_memory_management
18  */
19 
20 /**
21  * @defgroup mem-demand-paging Demand Paging APIs
22  * @ingroup demand_paging
23  * @{
24  */
25 
26 #ifndef _ASMLANGUAGE
27 #include <stdint.h>
28 #include <stddef.h>
29 #include <inttypes.h>
30 #include <zephyr/sys/__assert.h>
31 
32 struct k_mem_page_frame;
33 
34 /**
35  * Paging Statistics.
36  */
37 struct k_mem_paging_stats_t {
38 #if defined(CONFIG_DEMAND_PAGING_STATS) || defined(__DOXYGEN__)
39 	struct {
40 		/** Number of page faults */
41 		unsigned long			cnt;
42 
43 		/** Number of page faults with IRQ locked */
44 		unsigned long			irq_locked;
45 
46 		/** Number of page faults with IRQ unlocked */
47 		unsigned long			irq_unlocked;
48 
49 #if !defined(CONFIG_DEMAND_PAGING_ALLOW_IRQ) || defined(__DOXYGEN__)
50 		/** Number of page faults while in ISR */
51 		unsigned long			in_isr;
52 #endif /* !CONFIG_DEMAND_PAGING_ALLOW_IRQ */
53 	} pagefaults;
54 
55 	struct {
56 		/** Number of clean pages selected for eviction */
57 		unsigned long			clean;
58 
59 		/** Number of dirty pages selected for eviction */
60 		unsigned long			dirty;
61 	} eviction;
62 #endif /* CONFIG_DEMAND_PAGING_STATS */
63 };
64 
65 /**
66  * Paging Statistics Histograms.
67  */
68 struct k_mem_paging_histogram_t {
69 #if defined(CONFIG_DEMAND_PAGING_TIMING_HISTOGRAM) || defined(__DOXYGEN__)
70 	/* Counts for each bin in timing histogram */
71 	unsigned long	counts[CONFIG_DEMAND_PAGING_TIMING_HISTOGRAM_NUM_BINS];
72 
73 	/* Bounds for the bins in timing histogram,
74 	 * excluding the first and last (hence, NUM_SLOTS - 1).
75 	 */
76 	unsigned long	bounds[CONFIG_DEMAND_PAGING_TIMING_HISTOGRAM_NUM_BINS];
77 #endif /* CONFIG_DEMAND_PAGING_TIMING_HISTOGRAM */
78 };
79 
80 #ifdef __cplusplus
81 extern "C" {
82 #endif
83 
84 /**
85  * Evict a page-aligned virtual memory region to the backing store
86  *
87  * Useful if it is known that a memory region will not be used for some time.
88  * All the data pages within the specified region will be evicted to the
89  * backing store if they weren't already, with their associated page frames
90  * marked as available for mappings or page-ins.
91  *
92  * None of the associated page frames mapped to the provided region should
93  * be pinned.
94  *
95  * Note that there are no guarantees how long these pages will be evicted,
96  * they could take page faults immediately.
97  *
98  * If CONFIG_DEMAND_PAGING_ALLOW_IRQ is enabled, this function may not be
99  * called by ISRs as the backing store may be in-use.
100  *
101  * @param addr Base page-aligned virtual address
102  * @param size Page-aligned data region size
103  * @retval 0 Success
104  * @retval -ENOMEM Insufficient space in backing store to satisfy request.
105  *         The region may be partially paged out.
106  */
107 int k_mem_page_out(void *addr, size_t size);
108 
109 /**
110  * Load a virtual data region into memory
111  *
112  * After the function completes, all the page frames associated with this
113  * function will be paged in. However, they are not guaranteed to stay there.
114  * This is useful if the region is known to be used soon.
115  *
116  * If CONFIG_DEMAND_PAGING_ALLOW_IRQ is enabled, this function may not be
117  * called by ISRs as the backing store may be in-use.
118  *
119  * @param addr Base page-aligned virtual address
120  * @param size Page-aligned data region size
121  */
122 void k_mem_page_in(void *addr, size_t size);
123 
124 /**
125  * Pin an aligned virtual data region, paging in as necessary
126  *
127  * After the function completes, all the page frames associated with this
128  * region will be resident in memory and pinned such that they stay that way.
129  * This is a stronger version of z_mem_page_in().
130  *
131  * If CONFIG_DEMAND_PAGING_ALLOW_IRQ is enabled, this function may not be
132  * called by ISRs as the backing store may be in-use.
133  *
134  * @param addr Base page-aligned virtual address
135  * @param size Page-aligned data region size
136  */
137 void k_mem_pin(void *addr, size_t size);
138 
139 /**
140  * Un-pin an aligned virtual data region
141  *
142  * After the function completes, all the page frames associated with this
143  * region will be no longer marked as pinned. This does not evict the region,
144  * follow this with z_mem_page_out() if you need that.
145  *
146  * @param addr Base page-aligned virtual address
147  * @param size Page-aligned data region size
148  */
149 void k_mem_unpin(void *addr, size_t size);
150 
151 /**
152  * Get the paging statistics since system startup
153  *
154  * This populates the paging statistics struct being passed in
155  * as argument.
156  *
157  * @param[in,out] stats Paging statistics struct to be filled.
158  */
159 __syscall void k_mem_paging_stats_get(struct k_mem_paging_stats_t *stats);
160 
161 struct k_thread;
162 /**
163  * Get the paging statistics since system startup for a thread
164  *
165  * This populates the paging statistics struct being passed in
166  * as argument for a particular thread.
167  *
168  * @param[in] thread Thread
169  * @param[in,out] stats Paging statistics struct to be filled.
170  */
171 __syscall
172 void k_mem_paging_thread_stats_get(struct k_thread *thread,
173 				   struct k_mem_paging_stats_t *stats);
174 
175 /**
176  * Get the eviction timing histogram
177  *
178  * This populates the timing histogram struct being passed in
179  * as argument.
180  *
181  * @param[in,out] hist Timing histogram struct to be filled.
182  */
183 __syscall void k_mem_paging_histogram_eviction_get(
184 	struct k_mem_paging_histogram_t *hist);
185 
186 /**
187  * Get the backing store page-in timing histogram
188  *
189  * This populates the timing histogram struct being passed in
190  * as argument.
191  *
192  * @param[in,out] hist Timing histogram struct to be filled.
193  */
194 __syscall void k_mem_paging_histogram_backing_store_page_in_get(
195 	struct k_mem_paging_histogram_t *hist);
196 
197 /**
198  * Get the backing store page-out timing histogram
199  *
200  * This populates the timing histogram struct being passed in
201  * as argument.
202  *
203  * @param[in,out] hist Timing histogram struct to be filled.
204  */
205 __syscall void k_mem_paging_histogram_backing_store_page_out_get(
206 	struct k_mem_paging_histogram_t *hist);
207 
208 #include <zephyr/syscalls/demand_paging.h>
209 
210 /** @} */
211 
212 /**
213  * Eviction algorithm APIs
214  *
215  * @defgroup mem-demand-paging-eviction Eviction Algorithm APIs
216  * @ingroup demand_paging
217  * @{
218  */
219 
220 #if defined(CONFIG_EVICTION_TRACKING) || defined(__DOXYGEN__)
221 
222 /**
223  * Submit a page frame for eviction candidate tracking
224  *
225  * The kernel will invoke this to tell the eviction algorithm the provided
226  * page frame may be considered as a potential eviction candidate.
227  *
228  * This function will never be called before the initial
229  * k_mem_paging_eviction_init().
230  *
231  * This function is invoked with interrupts locked.
232  *
233  * @param [in] pf The page frame to add
234  */
235 void k_mem_paging_eviction_add(struct k_mem_page_frame *pf);
236 
237 /**
238  * Remove a page frame from potential eviction candidates
239  *
240  * The kernel will invoke this to tell the eviction algorithm the provided
241  * page frame may no longer be considered as a potential eviction candidate.
242  *
243  * This function will only be called with page frames that were submitted
244  * using k_mem_paging_eviction_add() beforehand.
245  *
246  * This function is invoked with interrupts locked.
247  *
248  * @param [in] pf The page frame to remove
249  */
250 void k_mem_paging_eviction_remove(struct k_mem_page_frame *pf);
251 
252 /**
253  * Process a page frame as being newly accessed
254  *
255  * The architecture-specific memory fault handler will invoke this to tell the
256  * eviction algorithm the provided physical address belongs to a page frame
257  * being accessed and such page frame should become unlikely to be
258  * considered as the next eviction candidate.
259  *
260  * This function is invoked with interrupts locked.
261  *
262  * @param [in] phys The physical address being accessed
263  */
264 void k_mem_paging_eviction_accessed(uintptr_t phys);
265 
266 #else /* CONFIG_EVICTION_TRACKING || __DOXYGEN__ */
267 
k_mem_paging_eviction_add(struct k_mem_page_frame * pf)268 static inline void k_mem_paging_eviction_add(struct k_mem_page_frame *pf)
269 {
270 	ARG_UNUSED(pf);
271 }
272 
k_mem_paging_eviction_remove(struct k_mem_page_frame * pf)273 static inline void k_mem_paging_eviction_remove(struct k_mem_page_frame *pf)
274 {
275 	ARG_UNUSED(pf);
276 }
277 
k_mem_paging_eviction_accessed(uintptr_t phys)278 static inline void k_mem_paging_eviction_accessed(uintptr_t phys)
279 {
280 	ARG_UNUSED(phys);
281 }
282 
283 #endif /* CONFIG_EVICTION_TRACKING || __DOXYGEN__ */
284 
285 /**
286  * Select a page frame for eviction
287  *
288  * The kernel will invoke this to choose a page frame to evict if there
289  * are no free page frames. It is not guaranteed that the returned page
290  * frame will actually be evicted. If it is then the kernel will call
291  * k_mem_paging_eviction_remove() with it.
292  *
293  * This function will never be called before the initial
294  * k_mem_paging_eviction_init().
295  *
296  * This function is invoked with interrupts locked.
297  *
298  * @param [out] dirty Whether the page to evict is dirty
299  * @return The page frame to evict
300  */
301 struct k_mem_page_frame *k_mem_paging_eviction_select(bool *dirty);
302 
303 /**
304  * Initialization function
305  *
306  * Called at POST_KERNEL to perform any necessary initialization tasks for the
307  * eviction algorithm. k_mem_paging_eviction_select() is guaranteed to never be
308  * called until this has returned, and this will only be called once.
309  */
310 void k_mem_paging_eviction_init(void);
311 
312 /** @} */
313 
314 /**
315  * Backing store APIs
316  *
317  * @defgroup mem-demand-paging-backing-store Backing Store APIs
318  * @ingroup demand_paging
319  * @{
320  */
321 
322 /**
323  * Reserve or fetch a storage location for a data page loaded into a page frame
324  *
325  * The returned location token must be unique to the mapped virtual address.
326  * This location will be used in the backing store to page out data page
327  * contents for later retrieval. The location value must be page-aligned.
328  *
329  * This function may be called multiple times on the same data page. If its
330  * page frame has its K_MEM_PAGE_FRAME_BACKED bit set, it is expected to return
331  * the previous backing store location for the data page containing a cached
332  * clean copy. This clean copy may be updated on page-out, or used to
333  * discard clean pages without needing to write out their contents.
334  *
335  * If the backing store is full, some other backing store location which caches
336  * a loaded data page may be selected, in which case its associated page frame
337  * will have the K_MEM_PAGE_FRAME_BACKED bit cleared (as it is no longer cached).
338  *
339  * k_mem_page_frame_to_virt(pf) will indicate the virtual address the page is
340  * currently mapped to. Large, sparse backing stores which can contain the
341  * entire address space may simply generate location tokens purely as a
342  * function of that virtual address with no other management necessary.
343  *
344  * This function distinguishes whether it was called on behalf of a page
345  * fault. A free backing store location must always be reserved in order for
346  * page faults to succeed. If the page_fault parameter is not set, this
347  * function should return -ENOMEM even if one location is available.
348  *
349  * This function is invoked with interrupts locked.
350  *
351  * @param pf Virtual address to obtain a storage location
352  * @param [out] location storage location token
353  * @param page_fault Whether this request was for a page fault
354  * @return 0 Success
355  * @return -ENOMEM Backing store is full
356  */
357 int k_mem_paging_backing_store_location_get(struct k_mem_page_frame *pf,
358 					    uintptr_t *location,
359 					    bool page_fault);
360 
361 /**
362  * Free a backing store location
363  *
364  * Any stored data may be discarded, and the location token associated with
365  * this address may be re-used for some other data page.
366  *
367  * This function is invoked with interrupts locked.
368  *
369  * @param location Location token to free
370  */
371 void k_mem_paging_backing_store_location_free(uintptr_t location);
372 
373 /**
374  * Obtain persistent location token for on-demand content
375  *
376  * Unlike k_mem_paging_backing_store_location_get() this does not allocate
377  * any backing store space. Instead, it returns a location token corresponding
378  * to some fixed storage content to be paged in on demand. This is expected
379  * to be used in conjonction with CONFIG_LINKER_USE_ONDEMAND_SECTION and the
380  * K_MEM_MAP_UNPAGED flag to create demand mappings at boot time. This may
381  * also be used e.g. to implement file-based mmap().
382  *
383  * @param addr Virtual address to obtain a location token for
384  * @param [out] location storage location token
385  * @return 0 for success or negative error code
386  */
387 int k_mem_paging_backing_store_location_query(void *addr, uintptr_t *location);
388 
389 /**
390  * Copy a data page from K_MEM_SCRATCH_PAGE to the specified location
391  *
392  * Immediately before this is called, K_MEM_SCRATCH_PAGE will be mapped read-write
393  * to the intended source page frame for the calling context.
394  *
395  * Calls to this and k_mem_paging_backing_store_page_in() will always be
396  * serialized, but interrupts may be enabled.
397  *
398  * @param location Location token for the data page, for later retrieval
399  */
400 void k_mem_paging_backing_store_page_out(uintptr_t location);
401 
402 /**
403  * Copy a data page from the provided location to K_MEM_SCRATCH_PAGE.
404  *
405  * Immediately before this is called, K_MEM_SCRATCH_PAGE will be mapped read-write
406  * to the intended destination page frame for the calling context.
407  *
408  * Calls to this and k_mem_paging_backing_store_page_out() will always be
409  * serialized, but interrupts may be enabled.
410  *
411  * @param location Location token for the data page
412  */
413 void k_mem_paging_backing_store_page_in(uintptr_t location);
414 
415 /**
416  * Update internal accounting after a page-in
417  *
418  * This is invoked after k_mem_paging_backing_store_page_in() and interrupts
419  * have been* re-locked, making it safe to access the k_mem_page_frame data.
420  * The location value will be the same passed to
421  * k_mem_paging_backing_store_page_in().
422  *
423  * The primary use-case for this is to update custom fields for the backing
424  * store in the page frame, to reflect where the data should be evicted to
425  * if it is paged out again. This may be a no-op in some implementations.
426  *
427  * If the backing store caches paged-in data pages, this is the appropriate
428  * time to set the K_MEM_PAGE_FRAME_BACKED bit. The kernel only skips paging
429  * out clean data pages if they are noted as clean in the page tables and the
430  * K_MEM_PAGE_FRAME_BACKED bit is set in their associated page frame.
431  *
432  * @param pf Page frame that was loaded in
433  * @param location Location of where the loaded data page was retrieved
434  */
435 void k_mem_paging_backing_store_page_finalize(struct k_mem_page_frame *pf,
436 					      uintptr_t location);
437 
438 /**
439  * Backing store initialization function.
440  *
441  * The implementation may expect to receive page in/out calls as soon as this
442  * returns, but not before that. Called at POST_KERNEL.
443  *
444  * This function is expected to do two things:
445  * - Initialize any internal data structures and accounting for the backing
446  *   store.
447  * - If the backing store already contains all or some loaded kernel data pages
448  *   at boot time, K_MEM_PAGE_FRAME_BACKED should be appropriately set for their
449  *   associated page frames, and any internal accounting set up appropriately.
450  */
451 void k_mem_paging_backing_store_init(void);
452 
453 /** @} */
454 
455 #ifdef __cplusplus
456 }
457 #endif
458 
459 #endif /* !_ASMLANGUAGE */
460 #endif /* ZEPHYR_INCLUDE_KERNEL_MM_DEMAND_PAGING_H */
461