1/* @title: Page Zeroer Subsystem */
2#pragma once
3#include <math/ewma.h>
4#include <math/fixed.h>
5#include <mem/buddy.h>
6#include <thread/daemon.h>
7#include <thread/workqueue.h>
8#include <types/types.h>
9
10/* Notes on the page zeroer architecture:
11 *
12 * We have a structure called `daemon`, in `daemon.h`, and our
13 * page zeroer mechanism is essentially built off of how that works.
14 *
15 * The basic idea of `daemon`: you have 1 background thread and N
16 * timesharing threads, and ONE background work and ONE timesharing work
17 * that you can wake the threads to go and perform
18 *
19 * For the more low level aspects: we make one page zeroer per domain,
20 * with one timesharing thread per 4 (TODO: subject to change) CPUs in
21 * that domain. The idea is that pages are marked as zeroed at the buddy_page
22 * level, and "zero pages push at front, non-zero push at tail" for buddy
23 * allocator freelists.
24 *
25 * During coalescing, the allocator will need to check the full block to
26 * determine if THE WHOLE THING is zeroed out, otherwise it will bail
27 *
28 * TODO: I'm thinking we could perhaps perform that part smarter? maybe
29 * we can have a system where the zero pages at order > 0 are sorted,
30 * but that would probably call for major expansion in struct page
31 *
32 * Anywho, we have a structure (TODO:) that manages the quotas
33 * for the page zeroer to meet. This likely won't be global,
34 * but I can try having some degree of a global structure on
35 * a "summary" of the per-domain page zeroers.
36 *
37 * The background thread performs aging on this global structure,
38 * as it runs whenever all other threads on that CPU are zzzzzz,
39 * and it can also kick awake the timesharing threads if needed
40 *
41 * But the idea is that upon interacting with the page zeroer
42 * interfaces, the quotas will be checked and the relevant
43 * timesharing threads will be booted awake to do some work
44 *
45 * There is also a path in which the page zeroer can be instructed
46 * to perform a specific task, and this involves things like the slowpath
47 * in a zero alloc'd hugepage, in which case the timesharing threads
48 * are instructed to zero out that hugepage
49 *
50 */
51
52struct page_zeroer_demand {
53 struct ewma rate; /* consumed blocks per epoch */
54 struct ewma mad; /* mean abs deviation of `rate` */
55 atomic_size_t consumed_epoch; /* fast-path inc, bg drain */
56 atomic_size_t stockout_epoch; /* sync-zero fallbacks */
57};
58
59struct page_zeroer_rate {
60 size_t batch_blocks; /* blocks TS thread zeros per wake */
61 fx32_32_t safety_factor; /* multiplier on demand for min_blocks */
62 fx32_32_t integral; /* PI integral state */
63 fx32_32_t idle_scale; /* [0, 1] until scheduler idle features come */
64};
65
66struct page_zeroer_watermark {
67 size_t min_min_blocks;
68 size_t max_max_blocks;
69
70 size_t min_blocks; /* These are dynamic */
71 size_t max_blocks;
72};
73
74/* TODO: once we get scheduler idleness integration, add it here */
75struct page_zeroer_quota {
76 struct page_zeroer_watermark wm;
77 struct page_zeroer_rate rate;
78 struct page_zeroer_demand demand;
79 time_t zero_block_time_ns;
80 atomic_size_t num_zero_blocks;
81};
82
83struct page_zeroer {
84 struct page_zeroer_quota quotas[BUDDY_MAX_ORDER];
85 struct daemon *daemon;
86};
87