Blame view
drivers/base/memory.c
19.8 KB
3947be196 [PATCH] memory ho... |
1 |
/* |
10fbcf4c6 convert 'memory' ... |
2 |
* Memory subsystem support |
3947be196 [PATCH] memory ho... |
3 4 5 6 7 8 9 10 11 |
* * Written by Matt Tolentino <matthew.e.tolentino@intel.com> * Dave Hansen <haveblue@us.ibm.com> * * This file provides the necessary infrastructure to represent * a SPARSEMEM-memory-model system's physical memory in /sysfs. * All arch-independent code that assumes MEMORY_HOTPLUG requires * SPARSEMEM should be contained here, or in mm/memory_hotplug.c. */ |
3947be196 [PATCH] memory ho... |
12 13 |
#include <linux/module.h> #include <linux/init.h> |
3947be196 [PATCH] memory ho... |
14 |
#include <linux/topology.h> |
c59ede7b7 [PATCH] move capa... |
15 |
#include <linux/capability.h> |
3947be196 [PATCH] memory ho... |
16 17 |
#include <linux/device.h> #include <linux/memory.h> |
3947be196 [PATCH] memory ho... |
18 19 |
#include <linux/memory_hotplug.h> #include <linux/mm.h> |
da19cbcf7 driver core: memo... |
20 |
#include <linux/mutex.h> |
9f1b16a51 memory_probe: fix... |
21 |
#include <linux/stat.h> |
5a0e3ad6a include cleanup: ... |
22 |
#include <linux/slab.h> |
9f1b16a51 memory_probe: fix... |
23 |
|
60063497a atomic: use <linu... |
24 |
#include <linux/atomic.h> |
3947be196 [PATCH] memory ho... |
25 |
#include <asm/uaccess.h> |
2938ffbd4 Driver core: Add ... |
26 |
static DEFINE_MUTEX(mem_sysfs_mutex); |
3947be196 [PATCH] memory ho... |
27 |
#define MEMORY_CLASS_NAME "memory" |
0c2c99b1b memory hotplug: A... |
28 |
|
7315f0ccf drivers/base/memo... |
29 |
#define to_memory_block(dev) container_of(dev, struct memory_block, dev) |
0c2c99b1b memory hotplug: A... |
30 31 32 33 34 35 |
static int sections_per_block; static inline int base_memory_block_id(int section_nr) { return section_nr / sections_per_block; } |
3947be196 [PATCH] memory ho... |
36 |
|
4960e05e2 Driver core: Intr... |
37 38 |
static int memory_subsys_online(struct device *dev); static int memory_subsys_offline(struct device *dev); |
10fbcf4c6 convert 'memory' ... |
39 |
static struct bus_type memory_subsys = { |
af5ca3f4e Driver core: chan... |
40 |
.name = MEMORY_CLASS_NAME, |
10fbcf4c6 convert 'memory' ... |
41 |
.dev_name = MEMORY_CLASS_NAME, |
4960e05e2 Driver core: Intr... |
42 43 |
.online = memory_subsys_online, .offline = memory_subsys_offline, |
3947be196 [PATCH] memory ho... |
44 |
}; |
e041c6834 [PATCH] Notifier ... |
45 |
static BLOCKING_NOTIFIER_HEAD(memory_chain); |
3947be196 [PATCH] memory ho... |
46 |
|
98a38ebdd [PATCH] memhotplu... |
47 |
int register_memory_notifier(struct notifier_block *nb) |
3947be196 [PATCH] memory ho... |
48 |
{ |
2aeebca2f drivers: base: me... |
49 |
return blocking_notifier_chain_register(&memory_chain, nb); |
3947be196 [PATCH] memory ho... |
50 |
} |
3c82c30cd memory: Introduce... |
51 |
EXPORT_SYMBOL(register_memory_notifier); |
3947be196 [PATCH] memory ho... |
52 |
|
98a38ebdd [PATCH] memhotplu... |
53 |
void unregister_memory_notifier(struct notifier_block *nb) |
3947be196 [PATCH] memory ho... |
54 |
{ |
2aeebca2f drivers: base: me... |
55 |
blocking_notifier_chain_unregister(&memory_chain, nb); |
3947be196 [PATCH] memory ho... |
56 |
} |
3c82c30cd memory: Introduce... |
57 |
EXPORT_SYMBOL(unregister_memory_notifier); |
3947be196 [PATCH] memory ho... |
58 |
|
925cc71e5 mm: Add notifier ... |
59 60 61 62 63 64 65 66 67 68 69 70 71 |
static ATOMIC_NOTIFIER_HEAD(memory_isolate_chain); int register_memory_isolate_notifier(struct notifier_block *nb) { return atomic_notifier_chain_register(&memory_isolate_chain, nb); } EXPORT_SYMBOL(register_memory_isolate_notifier); void unregister_memory_isolate_notifier(struct notifier_block *nb) { atomic_notifier_chain_unregister(&memory_isolate_chain, nb); } EXPORT_SYMBOL(unregister_memory_isolate_notifier); |
fa7194eb9 memory hotplug: s... |
72 73 |
static void memory_block_release(struct device *dev) { |
7315f0ccf drivers/base/memo... |
74 |
struct memory_block *mem = to_memory_block(dev); |
fa7194eb9 memory hotplug: s... |
75 76 77 |
kfree(mem); } |
0c2c99b1b memory hotplug: A... |
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
unsigned long __weak memory_block_size_bytes(void) { return MIN_MEMORY_BLOCK_SIZE; } static unsigned long get_memory_block_size(void) { unsigned long block_sz; block_sz = memory_block_size_bytes(); /* Validate blk_sz is a power of 2 and not less than section size */ if ((block_sz & (block_sz - 1)) || (block_sz < MIN_MEMORY_BLOCK_SIZE)) { WARN_ON(1); block_sz = MIN_MEMORY_BLOCK_SIZE; } return block_sz; } |
3947be196 [PATCH] memory ho... |
97 98 99 100 |
/* * use this as the physical section index that this memsection * uses. */ |
10fbcf4c6 convert 'memory' ... |
101 102 |
static ssize_t show_mem_start_phys_index(struct device *dev, struct device_attribute *attr, char *buf) |
3947be196 [PATCH] memory ho... |
103 |
{ |
7315f0ccf drivers/base/memo... |
104 |
struct memory_block *mem = to_memory_block(dev); |
d33601644 memory hotplug: U... |
105 106 107 108 109 110 |
unsigned long phys_index; phys_index = mem->start_section_nr / sections_per_block; return sprintf(buf, "%08lx ", phys_index); } |
3947be196 [PATCH] memory ho... |
111 |
/* |
5c755e9fd memory-hotplug: a... |
112 113 |
* Show whether the section of memory is likely to be hot-removable */ |
10fbcf4c6 convert 'memory' ... |
114 115 |
static ssize_t show_mem_removable(struct device *dev, struct device_attribute *attr, char *buf) |
5c755e9fd memory-hotplug: a... |
116 |
{ |
0c2c99b1b memory hotplug: A... |
117 118 |
unsigned long i, pfn; int ret = 1; |
7315f0ccf drivers/base/memo... |
119 |
struct memory_block *mem = to_memory_block(dev); |
5c755e9fd memory-hotplug: a... |
120 |
|
0c2c99b1b memory hotplug: A... |
121 |
for (i = 0; i < sections_per_block; i++) { |
21ea9f5ac drivers/base/memo... |
122 123 |
if (!present_section_nr(mem->start_section_nr + i)) continue; |
d33601644 memory hotplug: U... |
124 |
pfn = section_nr_to_pfn(mem->start_section_nr + i); |
0c2c99b1b memory hotplug: A... |
125 126 |
ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION); } |
5c755e9fd memory-hotplug: a... |
127 128 129 130 131 |
return sprintf(buf, "%d ", ret); } /* |
3947be196 [PATCH] memory ho... |
132 133 |
* online, offline, going offline, etc. */ |
10fbcf4c6 convert 'memory' ... |
134 135 |
static ssize_t show_mem_state(struct device *dev, struct device_attribute *attr, char *buf) |
3947be196 [PATCH] memory ho... |
136 |
{ |
7315f0ccf drivers/base/memo... |
137 |
struct memory_block *mem = to_memory_block(dev); |
3947be196 [PATCH] memory ho... |
138 139 140 141 142 143 144 |
ssize_t len = 0; /* * We can probably put these states in a nice little array * so that they're not open-coded */ switch (mem->state) { |
3d3af6afa drivers: base: me... |
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
case MEM_ONLINE: len = sprintf(buf, "online "); break; case MEM_OFFLINE: len = sprintf(buf, "offline "); break; case MEM_GOING_OFFLINE: len = sprintf(buf, "going-offline "); break; default: len = sprintf(buf, "ERROR-UNKNOWN-%ld ", mem->state); WARN_ON(1); break; |
3947be196 [PATCH] memory ho... |
163 164 165 166 |
} return len; } |
7b78d335a memory hotplug: r... |
167 |
int memory_notify(unsigned long val, void *v) |
3947be196 [PATCH] memory ho... |
168 |
{ |
e041c6834 [PATCH] Notifier ... |
169 |
return blocking_notifier_call_chain(&memory_chain, val, v); |
3947be196 [PATCH] memory ho... |
170 |
} |
925cc71e5 mm: Add notifier ... |
171 172 173 174 |
int memory_isolate_notify(unsigned long val, void *v) { return atomic_notifier_call_chain(&memory_isolate_chain, val, v); } |
3947be196 [PATCH] memory ho... |
175 |
/* |
2bbcb8788 mm: memory hotplu... |
176 177 178 |
* The probe routines leave the pages reserved, just as the bootmem code does. * Make sure they're still that way. */ |
6056d619a mm: Remove unused... |
179 |
static bool pages_correctly_reserved(unsigned long start_pfn) |
2bbcb8788 mm: memory hotplu... |
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
{ int i, j; struct page *page; unsigned long pfn = start_pfn; /* * memmap between sections is not contiguous except with * SPARSEMEM_VMEMMAP. We lookup the page once per section * and assume memmap is contiguous within each section */ for (i = 0; i < sections_per_block; i++, pfn += PAGES_PER_SECTION) { if (WARN_ON_ONCE(!pfn_valid(pfn))) return false; page = pfn_to_page(pfn); for (j = 0; j < PAGES_PER_SECTION; j++) { if (PageReserved(page + j)) continue; printk(KERN_WARNING "section number %ld page number %d " "not reserved, was it already online? ", pfn_to_section_nr(pfn), j); return false; } } return true; } /* |
3947be196 [PATCH] memory ho... |
212 213 |
* MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is * OK to have direct references to sparsemem variables in here. |
30467e0b3 mm, hotplug: fix ... |
214 |
* Must already be protected by mem_hotplug_begin(). |
3947be196 [PATCH] memory ho... |
215 216 |
*/ static int |
511c2aba8 mm, memory-hotplu... |
217 |
memory_block_action(unsigned long phys_index, unsigned long action, int online_type) |
3947be196 [PATCH] memory ho... |
218 |
{ |
a16cee10c memory-hotplug: p... |
219 |
unsigned long start_pfn; |
5409d2cd8 memory hotplug: S... |
220 |
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; |
de0ed36a3 Revert "memory ho... |
221 |
struct page *first_page; |
3947be196 [PATCH] memory ho... |
222 |
int ret; |
3947be196 [PATCH] memory ho... |
223 |
|
19c07d5e0 memory hotplug: u... |
224 |
start_pfn = section_nr_to_pfn(phys_index); |
71fbd556a memory-hotplug: r... |
225 |
first_page = pfn_to_page(start_pfn); |
de0ed36a3 Revert "memory ho... |
226 |
|
3947be196 [PATCH] memory ho... |
227 |
switch (action) { |
3d3af6afa drivers: base: me... |
228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
case MEM_ONLINE: if (!pages_correctly_reserved(start_pfn)) return -EBUSY; ret = online_pages(start_pfn, nr_pages, online_type); break; case MEM_OFFLINE: ret = offline_pages(start_pfn, nr_pages); break; default: WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: " "%ld ", __func__, phys_index, action, action); ret = -EINVAL; |
3947be196 [PATCH] memory ho... |
242 |
} |
3947be196 [PATCH] memory ho... |
243 244 245 |
return ret; } |
31bc3858e memory-hotplug: a... |
246 |
int memory_block_change_state(struct memory_block *mem, |
fa2be40fe drivers: base: us... |
247 |
unsigned long to_state, unsigned long from_state_req) |
3947be196 [PATCH] memory ho... |
248 |
{ |
de0ed36a3 Revert "memory ho... |
249 |
int ret = 0; |
0c2c99b1b memory hotplug: A... |
250 |
|
4960e05e2 Driver core: Intr... |
251 252 |
if (mem->state != from_state_req) return -EINVAL; |
3947be196 [PATCH] memory ho... |
253 |
|
0c2c99b1b memory hotplug: A... |
254 255 |
if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; |
fa2be40fe drivers: base: us... |
256 257 |
ret = memory_block_action(mem->start_section_nr, to_state, mem->online_type); |
b2c064b25 Driver core / mem... |
258 |
mem->state = ret ? from_state_req : to_state; |
fa2be40fe drivers: base: us... |
259 |
|
4960e05e2 Driver core: Intr... |
260 261 |
return ret; } |
0c2c99b1b memory hotplug: A... |
262 |
|
fa2be40fe drivers: base: us... |
263 |
/* The device lock serializes operations on memory_subsys_[online|offline] */ |
4960e05e2 Driver core: Intr... |
264 265 |
static int memory_subsys_online(struct device *dev) { |
7315f0ccf drivers/base/memo... |
266 |
struct memory_block *mem = to_memory_block(dev); |
4960e05e2 Driver core: Intr... |
267 |
int ret; |
3947be196 [PATCH] memory ho... |
268 |
|
fa2be40fe drivers: base: us... |
269 270 |
if (mem->state == MEM_ONLINE) return 0; |
4960e05e2 Driver core: Intr... |
271 |
|
fa2be40fe drivers: base: us... |
272 273 274 275 276 277 |
/* * If we are called from store_mem_state(), online_type will be * set >= 0 Otherwise we were called from the device online * attribute and need to set the online_type. */ if (mem->online_type < 0) |
4f7c6b49c mem-hotplug: intr... |
278 |
mem->online_type = MMOP_ONLINE_KEEP; |
4960e05e2 Driver core: Intr... |
279 |
|
30467e0b3 mm, hotplug: fix ... |
280 |
/* Already under protection of mem_hotplug_begin() */ |
fa2be40fe drivers: base: us... |
281 |
ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); |
4960e05e2 Driver core: Intr... |
282 |
|
fa2be40fe drivers: base: us... |
283 284 |
/* clear online_type */ mem->online_type = -1; |
4960e05e2 Driver core: Intr... |
285 |
|
4960e05e2 Driver core: Intr... |
286 287 |
return ret; } |
4960e05e2 Driver core: Intr... |
288 |
static int memory_subsys_offline(struct device *dev) |
e90bdb7f5 memory-hotplug: u... |
289 |
{ |
7315f0ccf drivers/base/memo... |
290 |
struct memory_block *mem = to_memory_block(dev); |
e90bdb7f5 memory-hotplug: u... |
291 |
|
fa2be40fe drivers: base: us... |
292 293 |
if (mem->state == MEM_OFFLINE) return 0; |
e90bdb7f5 memory-hotplug: u... |
294 |
|
26bbe7ef6 drivers/base/memo... |
295 296 297 |
/* Can't offline block with non-present sections */ if (mem->section_count != sections_per_block) return -EINVAL; |
fa2be40fe drivers: base: us... |
298 |
return memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); |
e90bdb7f5 memory-hotplug: u... |
299 |
} |
4960e05e2 Driver core: Intr... |
300 |
|
3947be196 [PATCH] memory ho... |
301 |
static ssize_t |
10fbcf4c6 convert 'memory' ... |
302 303 |
store_mem_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
3947be196 [PATCH] memory ho... |
304 |
{ |
7315f0ccf drivers/base/memo... |
305 |
struct memory_block *mem = to_memory_block(dev); |
fa2be40fe drivers: base: us... |
306 |
int ret, online_type; |
3947be196 [PATCH] memory ho... |
307 |
|
5e33bc416 driver core / ACP... |
308 309 310 |
ret = lock_device_hotplug_sysfs(); if (ret) return ret; |
4960e05e2 Driver core: Intr... |
311 |
|
1f6a6cc82 mem-hotplug: avoi... |
312 |
if (sysfs_streq(buf, "online_kernel")) |
4f7c6b49c mem-hotplug: intr... |
313 |
online_type = MMOP_ONLINE_KERNEL; |
1f6a6cc82 mem-hotplug: avoi... |
314 |
else if (sysfs_streq(buf, "online_movable")) |
4f7c6b49c mem-hotplug: intr... |
315 |
online_type = MMOP_ONLINE_MOVABLE; |
1f6a6cc82 mem-hotplug: avoi... |
316 |
else if (sysfs_streq(buf, "online")) |
4f7c6b49c mem-hotplug: intr... |
317 |
online_type = MMOP_ONLINE_KEEP; |
1f6a6cc82 mem-hotplug: avoi... |
318 |
else if (sysfs_streq(buf, "offline")) |
4f7c6b49c mem-hotplug: intr... |
319 |
online_type = MMOP_OFFLINE; |
a37f86305 driver core: Rele... |
320 321 322 323 |
else { ret = -EINVAL; goto err; } |
fa2be40fe drivers: base: us... |
324 |
|
30467e0b3 mm, hotplug: fix ... |
325 326 327 328 329 330 331 332 |
/* * Memory hotplug needs to hold mem_hotplug_begin() for probe to find * the correct memory block to online before doing device_online(dev), * which will take dev->mutex. Take the lock early to prevent an * inversion, memory_subsys_online() callbacks will be implemented by * assuming it's already protected. */ mem_hotplug_begin(); |
fa2be40fe drivers: base: us... |
333 |
switch (online_type) { |
4f7c6b49c mem-hotplug: intr... |
334 335 336 |
case MMOP_ONLINE_KERNEL: case MMOP_ONLINE_MOVABLE: case MMOP_ONLINE_KEEP: |
fa2be40fe drivers: base: us... |
337 338 339 |
mem->online_type = online_type; ret = device_online(&mem->dev); break; |
4f7c6b49c mem-hotplug: intr... |
340 |
case MMOP_OFFLINE: |
fa2be40fe drivers: base: us... |
341 342 343 344 |
ret = device_offline(&mem->dev); break; default: ret = -EINVAL; /* should never happen */ |
4960e05e2 Driver core: Intr... |
345 |
} |
4960e05e2 Driver core: Intr... |
346 |
|
30467e0b3 mm, hotplug: fix ... |
347 |
mem_hotplug_done(); |
a37f86305 driver core: Rele... |
348 |
err: |
4960e05e2 Driver core: Intr... |
349 |
unlock_device_hotplug(); |
0c2c99b1b memory hotplug: A... |
350 |
|
d66ba15bd memory-hotplug: f... |
351 |
if (ret < 0) |
3947be196 [PATCH] memory ho... |
352 |
return ret; |
d66ba15bd memory-hotplug: f... |
353 354 |
if (ret) return -EINVAL; |
3947be196 [PATCH] memory ho... |
355 356 357 358 359 360 361 362 363 364 365 366 |
return count; } /* * phys_device is a bad name for this. What I really want * is a way to differentiate between memory ranges that * are part of physical devices that constitute * a complete removable unit or fru. * i.e. do these ranges belong to the same physical device, * s.t. if I offline all of these sections I can then * remove the physical device? */ |
10fbcf4c6 convert 'memory' ... |
367 368 |
static ssize_t show_phys_device(struct device *dev, struct device_attribute *attr, char *buf) |
3947be196 [PATCH] memory ho... |
369 |
{ |
7315f0ccf drivers/base/memo... |
370 |
struct memory_block *mem = to_memory_block(dev); |
3947be196 [PATCH] memory ho... |
371 372 373 |
return sprintf(buf, "%d ", mem->phys_device); } |
ed2f24009 memory-hotplug: a... |
374 375 376 377 378 379 |
#ifdef CONFIG_MEMORY_HOTREMOVE static ssize_t show_valid_zones(struct device *dev, struct device_attribute *attr, char *buf) { struct memory_block *mem = to_memory_block(dev); unsigned long start_pfn, end_pfn; |
6cb0497ae base/memory, hotp... |
380 |
unsigned long valid_start, valid_end, valid_pages; |
ed2f24009 memory-hotplug: a... |
381 |
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; |
ed2f24009 memory-hotplug: a... |
382 |
struct zone *zone; |
a371d9f1c memory-hotplug: u... |
383 |
int zone_shift = 0; |
ed2f24009 memory-hotplug: a... |
384 385 386 |
start_pfn = section_nr_to_pfn(mem->start_section_nr); end_pfn = start_pfn + nr_pages; |
ed2f24009 memory-hotplug: a... |
387 388 |
/* The block contains more than one zone can not be offlined. */ |
6cb0497ae base/memory, hotp... |
389 |
if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end)) |
ed2f24009 memory-hotplug: a... |
390 391 |
return sprintf(buf, "none "); |
6cb0497ae base/memory, hotp... |
392 393 |
zone = page_zone(pfn_to_page(valid_start)); valid_pages = valid_end - valid_start; |
ed2f24009 memory-hotplug: a... |
394 |
|
a371d9f1c memory-hotplug: u... |
395 396 397 398 |
/* MMOP_ONLINE_KEEP */ sprintf(buf, "%s", zone->name); /* MMOP_ONLINE_KERNEL */ |
6cb0497ae base/memory, hotp... |
399 |
zone_can_shift(valid_start, valid_pages, ZONE_NORMAL, &zone_shift); |
a371d9f1c memory-hotplug: u... |
400 401 402 |
if (zone_shift) { strcat(buf, " "); strcat(buf, (zone + zone_shift)->name); |
ed2f24009 memory-hotplug: a... |
403 |
} |
a371d9f1c memory-hotplug: u... |
404 |
/* MMOP_ONLINE_MOVABLE */ |
6cb0497ae base/memory, hotp... |
405 |
zone_can_shift(valid_start, valid_pages, ZONE_MOVABLE, &zone_shift); |
a371d9f1c memory-hotplug: u... |
406 407 408 |
if (zone_shift) { strcat(buf, " "); strcat(buf, (zone + zone_shift)->name); |
ed2f24009 memory-hotplug: a... |
409 |
} |
a371d9f1c memory-hotplug: u... |
410 411 412 413 |
strcat(buf, " "); return strlen(buf); |
ed2f24009 memory-hotplug: a... |
414 415 416 |
} static DEVICE_ATTR(valid_zones, 0444, show_valid_zones, NULL); #endif |
10fbcf4c6 convert 'memory' ... |
417 |
static DEVICE_ATTR(phys_index, 0444, show_mem_start_phys_index, NULL); |
10fbcf4c6 convert 'memory' ... |
418 419 420 |
static DEVICE_ATTR(state, 0644, show_mem_state, store_mem_state); static DEVICE_ATTR(phys_device, 0444, show_phys_device, NULL); static DEVICE_ATTR(removable, 0444, show_mem_removable, NULL); |
3947be196 [PATCH] memory ho... |
421 |
|
3947be196 [PATCH] memory ho... |
422 423 424 425 |
/* * Block size attribute stuff */ static ssize_t |
10fbcf4c6 convert 'memory' ... |
426 |
print_block_size(struct device *dev, struct device_attribute *attr, |
8564a6c14 sysdev: Fix type ... |
427 |
char *buf) |
3947be196 [PATCH] memory ho... |
428 |
{ |
0c2c99b1b memory hotplug: A... |
429 430 |
return sprintf(buf, "%lx ", get_memory_block_size()); |
3947be196 [PATCH] memory ho... |
431 |
} |
10fbcf4c6 convert 'memory' ... |
432 |
static DEVICE_ATTR(block_size_bytes, 0444, print_block_size, NULL); |
3947be196 [PATCH] memory ho... |
433 |
|
3947be196 [PATCH] memory ho... |
434 |
/* |
31bc3858e memory-hotplug: a... |
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
* Memory auto online policy. */ static ssize_t show_auto_online_blocks(struct device *dev, struct device_attribute *attr, char *buf) { if (memhp_auto_online) return sprintf(buf, "online "); else return sprintf(buf, "offline "); } static ssize_t store_auto_online_blocks(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { if (sysfs_streq(buf, "online")) memhp_auto_online = true; else if (sysfs_streq(buf, "offline")) memhp_auto_online = false; else return -EINVAL; return count; } static DEVICE_ATTR(auto_online_blocks, 0644, show_auto_online_blocks, store_auto_online_blocks); /* |
3947be196 [PATCH] memory ho... |
468 469 470 471 472 473 474 |
* Some architectures will have custom drivers to do this, and * will not need to do it from userspace. The fake hot-add code * as well as ppc64 will do all of their discovery in userspace * and will require this interface. */ #ifdef CONFIG_ARCH_MEMORY_PROBE static ssize_t |
10fbcf4c6 convert 'memory' ... |
475 |
memory_probe_store(struct device *dev, struct device_attribute *attr, |
28812fe11 driver-core: Add ... |
476 |
const char *buf, size_t count) |
3947be196 [PATCH] memory ho... |
477 478 |
{ u64 phys_addr; |
cb5490a5e drivers/base/memo... |
479 |
int nid, ret; |
61b94feaf memory hotplug: R... |
480 |
unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block; |
3947be196 [PATCH] memory ho... |
481 |
|
b69deb2b7 mm/mem-hotplug: r... |
482 483 484 |
ret = kstrtoull(buf, 0, &phys_addr); if (ret) return ret; |
3947be196 [PATCH] memory ho... |
485 |
|
61b94feaf memory hotplug: R... |
486 487 |
if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1)) return -EINVAL; |
cb5490a5e drivers/base/memo... |
488 489 490 |
nid = memory_add_physaddr_to_nid(phys_addr); ret = add_memory(nid, phys_addr, MIN_MEMORY_BLOCK_SIZE * sections_per_block); |
6add7cd61 memory hotplug: s... |
491 |
|
cb5490a5e drivers/base/memo... |
492 493 |
if (ret) goto out; |
3947be196 [PATCH] memory ho... |
494 |
|
9f0af69b2 sysfs-memory: fix... |
495 496 497 |
ret = count; out: return ret; |
3947be196 [PATCH] memory ho... |
498 |
} |
3947be196 [PATCH] memory ho... |
499 |
|
96b2c0fc8 drivers/base: Use... |
500 |
static DEVICE_ATTR(probe, S_IWUSR, NULL, memory_probe_store); |
3947be196 [PATCH] memory ho... |
501 |
#endif |
facb6011f HWPOISON: Add sof... |
502 503 504 505 506 507 508 |
#ifdef CONFIG_MEMORY_FAILURE /* * Support for offlining pages of memory */ /* Soft offline a page */ static ssize_t |
10fbcf4c6 convert 'memory' ... |
509 510 |
store_soft_offline_page(struct device *dev, struct device_attribute *attr, |
28812fe11 driver-core: Add ... |
511 |
const char *buf, size_t count) |
facb6011f HWPOISON: Add sof... |
512 513 514 515 516 |
{ int ret; u64 pfn; if (!capable(CAP_SYS_ADMIN)) return -EPERM; |
34da5e677 driver core: repl... |
517 |
if (kstrtoull(buf, 0, &pfn) < 0) |
facb6011f HWPOISON: Add sof... |
518 519 520 521 522 523 524 525 526 527 |
return -EINVAL; pfn >>= PAGE_SHIFT; if (!pfn_valid(pfn)) return -ENXIO; ret = soft_offline_page(pfn_to_page(pfn), 0); return ret == 0 ? count : ret; } /* Forcibly offline a page, including killing processes. */ static ssize_t |
10fbcf4c6 convert 'memory' ... |
528 529 |
store_hard_offline_page(struct device *dev, struct device_attribute *attr, |
28812fe11 driver-core: Add ... |
530 |
const char *buf, size_t count) |
facb6011f HWPOISON: Add sof... |
531 532 533 534 535 |
{ int ret; u64 pfn; if (!capable(CAP_SYS_ADMIN)) return -EPERM; |
34da5e677 driver core: repl... |
536 |
if (kstrtoull(buf, 0, &pfn) < 0) |
facb6011f HWPOISON: Add sof... |
537 538 |
return -EINVAL; pfn >>= PAGE_SHIFT; |
cd42f4a3b HWPOISON: Clean u... |
539 |
ret = memory_failure(pfn, 0, 0); |
facb6011f HWPOISON: Add sof... |
540 541 |
return ret ? ret : count; } |
74fef7a8f base: memory: fix... |
542 543 |
static DEVICE_ATTR(soft_offline_page, S_IWUSR, NULL, store_soft_offline_page); static DEVICE_ATTR(hard_offline_page, S_IWUSR, NULL, store_hard_offline_page); |
facb6011f HWPOISON: Add sof... |
544 |
#endif |
3947be196 [PATCH] memory ho... |
545 546 547 548 549 |
/* * Note that phys_device is optional. It is here to allow for * differentiation between which *physical* devices each * section belongs to... */ |
bc32df008 memory hotplug: a... |
550 551 552 553 |
int __weak arch_get_memory_phys_device(unsigned long start_pfn) { return 0; } |
3947be196 [PATCH] memory ho... |
554 |
|
10fbcf4c6 convert 'memory' ... |
555 556 557 558 |
/* * A reference for the returned object is held and the reference for the * hinted object is released. */ |
98383031e driver core: Intr... |
559 560 |
struct memory_block *find_memory_block_hinted(struct mem_section *section, struct memory_block *hint) |
3947be196 [PATCH] memory ho... |
561 |
{ |
0c2c99b1b memory hotplug: A... |
562 |
int block_id = base_memory_block_id(__section_nr(section)); |
10fbcf4c6 convert 'memory' ... |
563 564 |
struct device *hintdev = hint ? &hint->dev : NULL; struct device *dev; |
3947be196 [PATCH] memory ho... |
565 |
|
10fbcf4c6 convert 'memory' ... |
566 567 568 569 |
dev = subsys_find_device_by_id(&memory_subsys, block_id, hintdev); if (hint) put_device(&hint->dev); if (!dev) |
3947be196 [PATCH] memory ho... |
570 |
return NULL; |
7315f0ccf drivers/base/memo... |
571 |
return to_memory_block(dev); |
3947be196 [PATCH] memory ho... |
572 |
} |
98383031e driver core: Intr... |
573 574 575 576 577 578 |
/* * For now, we have a linear search to go find the appropriate * memory_block corresponding to a particular phys_index. If * this gets to be a real problem, we can always use a radix * tree or something here. * |
10fbcf4c6 convert 'memory' ... |
579 |
* This could be made generic for all device subsystems. |
98383031e driver core: Intr... |
580 581 582 583 584 |
*/ struct memory_block *find_memory_block(struct mem_section *section) { return find_memory_block_hinted(section, NULL); } |
96b2c0fc8 drivers/base: Use... |
585 586 |
static struct attribute *memory_memblk_attrs[] = { &dev_attr_phys_index.attr, |
96b2c0fc8 drivers/base: Use... |
587 588 589 |
&dev_attr_state.attr, &dev_attr_phys_device.attr, &dev_attr_removable.attr, |
ed2f24009 memory-hotplug: a... |
590 591 592 |
#ifdef CONFIG_MEMORY_HOTREMOVE &dev_attr_valid_zones.attr, #endif |
96b2c0fc8 drivers/base: Use... |
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
NULL }; static struct attribute_group memory_memblk_attr_group = { .attrs = memory_memblk_attrs, }; static const struct attribute_group *memory_memblk_attr_groups[] = { &memory_memblk_attr_group, NULL, }; /* * register_memory - Setup a sysfs device for a memory block */ static int register_memory(struct memory_block *memory) { |
96b2c0fc8 drivers/base: Use... |
611 612 613 614 |
memory->dev.bus = &memory_subsys; memory->dev.id = memory->start_section_nr / sections_per_block; memory->dev.release = memory_block_release; memory->dev.groups = memory_memblk_attr_groups; |
f991fae5c Merge tag 'pm+acp... |
615 |
memory->dev.offline = memory->state == MEM_OFFLINE; |
96b2c0fc8 drivers/base: Use... |
616 |
|
879f1bec8 drivers: base: re... |
617 |
return device_register(&memory->dev); |
96b2c0fc8 drivers/base: Use... |
618 |
} |
0c2c99b1b memory hotplug: A... |
619 620 |
static int init_memory_block(struct memory_block **memory, struct mem_section *section, unsigned long state) |
e4619c857 Driver core: Move... |
621 |
{ |
0c2c99b1b memory hotplug: A... |
622 |
struct memory_block *mem; |
e4619c857 Driver core: Move... |
623 |
unsigned long start_pfn; |
0c2c99b1b memory hotplug: A... |
624 |
int scn_nr; |
e4619c857 Driver core: Move... |
625 |
int ret = 0; |
0c2c99b1b memory hotplug: A... |
626 |
mem = kzalloc(sizeof(*mem), GFP_KERNEL); |
e4619c857 Driver core: Move... |
627 628 |
if (!mem) return -ENOMEM; |
0c2c99b1b memory hotplug: A... |
629 |
scn_nr = __section_nr(section); |
d33601644 memory hotplug: U... |
630 631 632 |
mem->start_section_nr = base_memory_block_id(scn_nr) * sections_per_block; mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; |
e4619c857 Driver core: Move... |
633 |
mem->state = state; |
d33601644 memory hotplug: U... |
634 |
start_pfn = section_nr_to_pfn(mem->start_section_nr); |
e4619c857 Driver core: Move... |
635 |
mem->phys_device = arch_get_memory_phys_device(start_pfn); |
0c2c99b1b memory hotplug: A... |
636 |
ret = register_memory(mem); |
0c2c99b1b memory hotplug: A... |
637 638 639 640 |
*memory = mem; return ret; } |
cb5e39b80 drivers: base: re... |
641 |
static int add_memory_block(int base_section_nr) |
0c2c99b1b memory hotplug: A... |
642 |
{ |
cb5e39b80 drivers: base: re... |
643 644 |
struct memory_block *mem; int i, ret, section_count = 0, section_nr; |
0c2c99b1b memory hotplug: A... |
645 |
|
cb5e39b80 drivers: base: re... |
646 647 648 649 650 651 652 653 |
for (i = base_section_nr; (i < base_section_nr + sections_per_block) && i < NR_MEM_SECTIONS; i++) { if (!present_section_nr(i)) continue; if (section_count == 0) section_nr = i; section_count++; |
e4619c857 Driver core: Move... |
654 |
} |
cb5e39b80 drivers: base: re... |
655 656 657 658 659 660 661 |
if (section_count == 0) return 0; ret = init_memory_block(&mem, __nr_to_section(section_nr), MEM_ONLINE); if (ret) return ret; mem->section_count = section_count; return 0; |
e4619c857 Driver core: Move... |
662 |
} |
260ae3f7d mm: skip memory b... |
663 664 665 666 667 668 669 |
static bool is_zone_device_section(struct mem_section *ms) { struct page *page; page = sparse_decode_mem_map(ms->section_mem_map, __section_nr(ms)); return is_zone_device_page(page); } |
cb5e39b80 drivers: base: re... |
670 |
|
4edd7ceff mm, hotplug: avoi... |
671 672 673 674 675 676 |
/* * need an interface for the VM to add new memory regions, * but without onlining it. */ int register_new_memory(int nid, struct mem_section *section) { |
d7f80530a drivers: base: un... |
677 678 |
int ret = 0; struct memory_block *mem; |
b1eaef3da drivers: base: mo... |
679 |
|
260ae3f7d mm: skip memory b... |
680 681 |
if (is_zone_device_section(section)) return 0; |
b1eaef3da drivers: base: mo... |
682 |
mutex_lock(&mem_sysfs_mutex); |
b1eaef3da drivers: base: mo... |
683 |
|
d7f80530a drivers: base: un... |
684 685 686 687 688 689 690 691 |
mem = find_memory_block(section); if (mem) { mem->section_count++; put_device(&mem->dev); } else { ret = init_memory_block(&mem, section, MEM_OFFLINE); if (ret) goto out; |
56c6b5d3a drivers/base/memo... |
692 |
mem->section_count++; |
d7f80530a drivers: base: un... |
693 694 695 696 697 698 |
} if (mem->section_count == sections_per_block) ret = register_mem_sect_under_node(mem, nid); out: mutex_unlock(&mem_sysfs_mutex); |
b1eaef3da drivers: base: mo... |
699 |
return ret; |
4edd7ceff mm, hotplug: avoi... |
700 701 702 703 704 705 706 707 708 |
} #ifdef CONFIG_MEMORY_HOTREMOVE static void unregister_memory(struct memory_block *memory) { BUG_ON(memory->dev.bus != &memory_subsys); /* drop the ref. we got in remove_memory_block() */ |
df2b717c6 drivers: base: us... |
709 |
put_device(&memory->dev); |
4edd7ceff mm, hotplug: avoi... |
710 711 |
device_unregister(&memory->dev); } |
cc292b0b4 drivers/base/memo... |
712 |
static int remove_memory_section(unsigned long node_id, |
4edd7ceff mm, hotplug: avoi... |
713 |
struct mem_section *section, int phys_device) |
3947be196 [PATCH] memory ho... |
714 715 |
{ struct memory_block *mem; |
260ae3f7d mm: skip memory b... |
716 717 |
if (is_zone_device_section(section)) return 0; |
2938ffbd4 Driver core: Add ... |
718 |
mutex_lock(&mem_sysfs_mutex); |
3947be196 [PATCH] memory ho... |
719 |
mem = find_memory_block(section); |
d33601644 memory hotplug: U... |
720 |
unregister_mem_sect_under_nodes(mem, __section_nr(section)); |
076812159 Driver core: Add ... |
721 722 |
mem->section_count--; |
96b2c0fc8 drivers/base: Use... |
723 |
if (mem->section_count == 0) |
0c2c99b1b memory hotplug: A... |
724 |
unregister_memory(mem); |
96b2c0fc8 drivers/base: Use... |
725 |
else |
df2b717c6 drivers: base: us... |
726 |
put_device(&mem->dev); |
3947be196 [PATCH] memory ho... |
727 |
|
2938ffbd4 Driver core: Add ... |
728 |
mutex_unlock(&mem_sysfs_mutex); |
3947be196 [PATCH] memory ho... |
729 730 |
return 0; } |
3947be196 [PATCH] memory ho... |
731 732 |
int unregister_memory_section(struct mem_section *section) { |
540557b94 sparsemem: record... |
733 |
if (!present_section(section)) |
3947be196 [PATCH] memory ho... |
734 |
return -EINVAL; |
cc292b0b4 drivers/base/memo... |
735 |
return remove_memory_section(0, section, 0); |
3947be196 [PATCH] memory ho... |
736 |
} |
4edd7ceff mm, hotplug: avoi... |
737 |
#endif /* CONFIG_MEMORY_HOTREMOVE */ |
3947be196 [PATCH] memory ho... |
738 |
|
6677e3eaf memory-hotplug: c... |
739 740 741 742 743 |
/* return true if the memory block is offlined, otherwise, return false */ bool is_memblock_offlined(struct memory_block *mem) { return mem->state == MEM_OFFLINE; } |
96b2c0fc8 drivers/base: Use... |
744 745 746 747 748 749 750 751 752 753 754 |
static struct attribute *memory_root_attrs[] = { #ifdef CONFIG_ARCH_MEMORY_PROBE &dev_attr_probe.attr, #endif #ifdef CONFIG_MEMORY_FAILURE &dev_attr_soft_offline_page.attr, &dev_attr_hard_offline_page.attr, #endif &dev_attr_block_size_bytes.attr, |
31bc3858e memory-hotplug: a... |
755 |
&dev_attr_auto_online_blocks.attr, |
96b2c0fc8 drivers/base: Use... |
756 757 758 759 760 761 762 763 764 765 766 |
NULL }; static struct attribute_group memory_root_attr_group = { .attrs = memory_root_attrs, }; static const struct attribute_group *memory_root_attr_groups[] = { &memory_root_attr_group, NULL, }; |
e90bdb7f5 memory-hotplug: u... |
767 |
/* |
3947be196 [PATCH] memory ho... |
768 769 770 771 772 773 |
* Initialize the sysfs support for memory devices... */ int __init memory_dev_init(void) { unsigned int i; int ret; |
28ec24e23 [PATCH] driver/ba... |
774 |
int err; |
0c2c99b1b memory hotplug: A... |
775 |
unsigned long block_sz; |
3947be196 [PATCH] memory ho... |
776 |
|
96b2c0fc8 drivers/base: Use... |
777 |
ret = subsys_system_register(&memory_subsys, memory_root_attr_groups); |
28ec24e23 [PATCH] driver/ba... |
778 779 |
if (ret) goto out; |
3947be196 [PATCH] memory ho... |
780 |
|
0c2c99b1b memory hotplug: A... |
781 782 |
block_sz = get_memory_block_size(); sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE; |
3947be196 [PATCH] memory ho... |
783 784 785 786 |
/* * Create entries for memory sections that were found * during boot and have been initialized */ |
b1eaef3da drivers: base: mo... |
787 |
mutex_lock(&mem_sysfs_mutex); |
cb5e39b80 drivers: base: re... |
788 789 |
for (i = 0; i < NR_MEM_SECTIONS; i += sections_per_block) { err = add_memory_block(i); |
28ec24e23 [PATCH] driver/ba... |
790 791 |
if (!ret) ret = err; |
3947be196 [PATCH] memory ho... |
792 |
} |
b1eaef3da drivers: base: mo... |
793 |
mutex_unlock(&mem_sysfs_mutex); |
3947be196 [PATCH] memory ho... |
794 |
|
28ec24e23 [PATCH] driver/ba... |
795 796 |
out: if (ret) |
2b3a302a0 driver core: repl... |
797 798 |
printk(KERN_ERR "%s() failed: %d ", __func__, ret); |
3947be196 [PATCH] memory ho... |
799 800 |
return ret; } |