mm/demotion: move memory demotion related code
[linux-block.git] / mm / memory-tiers.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/types.h>
3 #include <linux/nodemask.h>
4 #include <linux/slab.h>
5 #include <linux/lockdep.h>
6 #include <linux/sysfs.h>
7 #include <linux/kobject.h>
8 #include <linux/memory-tiers.h>
9
10 struct memory_tier {
11         /* hierarchy of memory tiers */
12         struct list_head list;
13         /* list of all memory types part of this tier */
14         struct list_head memory_types;
15         /*
16          * start value of abstract distance. memory tier maps
17          * an abstract distance  range,
18          * adistance_start .. adistance_start + MEMTIER_CHUNK_SIZE
19          */
20         int adistance_start;
21 };
22
23 struct memory_dev_type {
24         /* list of memory types that are part of same tier as this type */
25         struct list_head tier_sibiling;
26         /* abstract distance for this specific memory type */
27         int adistance;
28         /* Nodes of same abstract distance */
29         nodemask_t nodes;
30         struct memory_tier *memtier;
31 };
32
33 static DEFINE_MUTEX(memory_tier_lock);
34 static LIST_HEAD(memory_tiers);
35 static struct memory_dev_type *node_memory_types[MAX_NUMNODES];
36 /*
37  * For now we can have 4 faster memory tiers with smaller adistance
38  * than default DRAM tier.
39  */
40 static struct memory_dev_type default_dram_type  = {
41         .adistance = MEMTIER_ADISTANCE_DRAM,
42         .tier_sibiling = LIST_HEAD_INIT(default_dram_type.tier_sibiling),
43 };
44
45 static struct memory_tier *find_create_memory_tier(struct memory_dev_type *memtype)
46 {
47         bool found_slot = false;
48         struct memory_tier *memtier, *new_memtier;
49         int adistance = memtype->adistance;
50         unsigned int memtier_adistance_chunk_size = MEMTIER_CHUNK_SIZE;
51
52         lockdep_assert_held_once(&memory_tier_lock);
53
54         /*
55          * If the memtype is already part of a memory tier,
56          * just return that.
57          */
58         if (memtype->memtier)
59                 return memtype->memtier;
60
61         adistance = round_down(adistance, memtier_adistance_chunk_size);
62         list_for_each_entry(memtier, &memory_tiers, list) {
63                 if (adistance == memtier->adistance_start) {
64                         memtype->memtier = memtier;
65                         list_add(&memtype->tier_sibiling, &memtier->memory_types);
66                         return memtier;
67                 } else if (adistance < memtier->adistance_start) {
68                         found_slot = true;
69                         break;
70                 }
71         }
72
73         new_memtier = kmalloc(sizeof(struct memory_tier), GFP_KERNEL);
74         if (!new_memtier)
75                 return ERR_PTR(-ENOMEM);
76
77         new_memtier->adistance_start = adistance;
78         INIT_LIST_HEAD(&new_memtier->list);
79         INIT_LIST_HEAD(&new_memtier->memory_types);
80         if (found_slot)
81                 list_add_tail(&new_memtier->list, &memtier->list);
82         else
83                 list_add_tail(&new_memtier->list, &memory_tiers);
84         memtype->memtier = new_memtier;
85         list_add(&memtype->tier_sibiling, &new_memtier->memory_types);
86         return new_memtier;
87 }
88
89 static struct memory_tier *set_node_memory_tier(int node)
90 {
91         struct memory_tier *memtier;
92         struct memory_dev_type *memtype;
93
94         lockdep_assert_held_once(&memory_tier_lock);
95
96         if (!node_state(node, N_MEMORY))
97                 return ERR_PTR(-EINVAL);
98
99         if (!node_memory_types[node])
100                 node_memory_types[node] = &default_dram_type;
101
102         memtype = node_memory_types[node];
103         node_set(node, memtype->nodes);
104         memtier = find_create_memory_tier(memtype);
105         return memtier;
106 }
107
108 static int __init memory_tier_init(void)
109 {
110         int node;
111         struct memory_tier *memtier;
112
113         mutex_lock(&memory_tier_lock);
114         /*
115          * Look at all the existing N_MEMORY nodes and add them to
116          * default memory tier or to a tier if we already have memory
117          * types assigned.
118          */
119         for_each_node_state(node, N_MEMORY) {
120                 memtier = set_node_memory_tier(node);
121                 if (IS_ERR(memtier))
122                         /*
123                          * Continue with memtiers we are able to setup
124                          */
125                         break;
126         }
127         mutex_unlock(&memory_tier_lock);
128
129         return 0;
130 }
131 subsys_initcall(memory_tier_init);
132
133 bool numa_demotion_enabled = false;
134
135 #ifdef CONFIG_MIGRATION
136 #ifdef CONFIG_SYSFS
137 static ssize_t numa_demotion_enabled_show(struct kobject *kobj,
138                                           struct kobj_attribute *attr, char *buf)
139 {
140         return sysfs_emit(buf, "%s\n",
141                           numa_demotion_enabled ? "true" : "false");
142 }
143
144 static ssize_t numa_demotion_enabled_store(struct kobject *kobj,
145                                            struct kobj_attribute *attr,
146                                            const char *buf, size_t count)
147 {
148         ssize_t ret;
149
150         ret = kstrtobool(buf, &numa_demotion_enabled);
151         if (ret)
152                 return ret;
153
154         return count;
155 }
156
157 static struct kobj_attribute numa_demotion_enabled_attr =
158         __ATTR(demotion_enabled, 0644, numa_demotion_enabled_show,
159                numa_demotion_enabled_store);
160
161 static struct attribute *numa_attrs[] = {
162         &numa_demotion_enabled_attr.attr,
163         NULL,
164 };
165
166 static const struct attribute_group numa_attr_group = {
167         .attrs = numa_attrs,
168 };
169
170 static int __init numa_init_sysfs(void)
171 {
172         int err;
173         struct kobject *numa_kobj;
174
175         numa_kobj = kobject_create_and_add("numa", mm_kobj);
176         if (!numa_kobj) {
177                 pr_err("failed to create numa kobject\n");
178                 return -ENOMEM;
179         }
180         err = sysfs_create_group(numa_kobj, &numa_attr_group);
181         if (err) {
182                 pr_err("failed to register numa group\n");
183                 goto delete_obj;
184         }
185         return 0;
186
187 delete_obj:
188         kobject_put(numa_kobj);
189         return err;
190 }
191 subsys_initcall(numa_init_sysfs);
192 #endif /* CONFIG_SYSFS */
193 #endif