diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/memory.c | 155 |
1 files changed, 108 insertions, 47 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index cafeaaf0428f..0b7040042587 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
@@ -30,6 +30,14 @@ | |||
30 | static DEFINE_MUTEX(mem_sysfs_mutex); | 30 | static DEFINE_MUTEX(mem_sysfs_mutex); |
31 | 31 | ||
32 | #define MEMORY_CLASS_NAME "memory" | 32 | #define MEMORY_CLASS_NAME "memory" |
33 | #define MIN_MEMORY_BLOCK_SIZE (1 << SECTION_SIZE_BITS) | ||
34 | |||
35 | static int sections_per_block; | ||
36 | |||
37 | static inline int base_memory_block_id(int section_nr) | ||
38 | { | ||
39 | return section_nr / sections_per_block; | ||
40 | } | ||
33 | 41 | ||
34 | static struct sysdev_class memory_sysdev_class = { | 42 | static struct sysdev_class memory_sysdev_class = { |
35 | .name = MEMORY_CLASS_NAME, | 43 | .name = MEMORY_CLASS_NAME, |
@@ -84,28 +92,47 @@ EXPORT_SYMBOL(unregister_memory_isolate_notifier); | |||
84 | * register_memory - Setup a sysfs device for a memory block | 92 | * register_memory - Setup a sysfs device for a memory block |
85 | */ | 93 | */ |
86 | static | 94 | static |
87 | int register_memory(struct memory_block *memory, struct mem_section *section) | 95 | int register_memory(struct memory_block *memory) |
88 | { | 96 | { |
89 | int error; | 97 | int error; |
90 | 98 | ||
91 | memory->sysdev.cls = &memory_sysdev_class; | 99 | memory->sysdev.cls = &memory_sysdev_class; |
92 | memory->sysdev.id = __section_nr(section); | 100 | memory->sysdev.id = memory->phys_index / sections_per_block; |
93 | 101 | ||
94 | error = sysdev_register(&memory->sysdev); | 102 | error = sysdev_register(&memory->sysdev); |
95 | return error; | 103 | return error; |
96 | } | 104 | } |
97 | 105 | ||
98 | static void | 106 | static void |
99 | unregister_memory(struct memory_block *memory, struct mem_section *section) | 107 | unregister_memory(struct memory_block *memory) |
100 | { | 108 | { |
101 | BUG_ON(memory->sysdev.cls != &memory_sysdev_class); | 109 | BUG_ON(memory->sysdev.cls != &memory_sysdev_class); |
102 | BUG_ON(memory->sysdev.id != __section_nr(section)); | ||
103 | 110 | ||
104 | /* drop the ref. we got in remove_memory_block() */ | 111 | /* drop the ref. we got in remove_memory_block() */ |
105 | kobject_put(&memory->sysdev.kobj); | 112 | kobject_put(&memory->sysdev.kobj); |
106 | sysdev_unregister(&memory->sysdev); | 113 | sysdev_unregister(&memory->sysdev); |
107 | } | 114 | } |
108 | 115 | ||
116 | unsigned long __weak memory_block_size_bytes(void) | ||
117 | { | ||
118 | return MIN_MEMORY_BLOCK_SIZE; | ||
119 | } | ||
120 | |||
121 | static unsigned long get_memory_block_size(void) | ||
122 | { | ||
123 | unsigned long block_sz; | ||
124 | |||
125 | block_sz = memory_block_size_bytes(); | ||
126 | |||
127 | /* Validate blk_sz is a power of 2 and not less than section size */ | ||
128 | if ((block_sz & (block_sz - 1)) || (block_sz < MIN_MEMORY_BLOCK_SIZE)) { | ||
129 | WARN_ON(1); | ||
130 | block_sz = MIN_MEMORY_BLOCK_SIZE; | ||
131 | } | ||
132 | |||
133 | return block_sz; | ||
134 | } | ||
135 | |||
109 | /* | 136 | /* |
110 | * use this as the physical section index that this memsection | 137 | * use this as the physical section index that this memsection |
111 | * uses. | 138 | * uses. |
@@ -116,7 +143,7 @@ static ssize_t show_mem_phys_index(struct sys_device *dev, | |||
116 | { | 143 | { |
117 | struct memory_block *mem = | 144 | struct memory_block *mem = |
118 | container_of(dev, struct memory_block, sysdev); | 145 | container_of(dev, struct memory_block, sysdev); |
119 | return sprintf(buf, "%08lx\n", mem->phys_index); | 146 | return sprintf(buf, "%08lx\n", mem->phys_index / sections_per_block); |
120 | } | 147 | } |
121 | 148 | ||
122 | /* | 149 | /* |
@@ -125,13 +152,16 @@ static ssize_t show_mem_phys_index(struct sys_device *dev, | |||
125 | static ssize_t show_mem_removable(struct sys_device *dev, | 152 | static ssize_t show_mem_removable(struct sys_device *dev, |
126 | struct sysdev_attribute *attr, char *buf) | 153 | struct sysdev_attribute *attr, char *buf) |
127 | { | 154 | { |
128 | unsigned long start_pfn; | 155 | unsigned long i, pfn; |
129 | int ret; | 156 | int ret = 1; |
130 | struct memory_block *mem = | 157 | struct memory_block *mem = |
131 | container_of(dev, struct memory_block, sysdev); | 158 | container_of(dev, struct memory_block, sysdev); |
132 | 159 | ||
133 | start_pfn = section_nr_to_pfn(mem->phys_index); | 160 | for (i = 0; i < sections_per_block; i++) { |
134 | ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION); | 161 | pfn = section_nr_to_pfn(mem->phys_index + i); |
162 | ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION); | ||
163 | } | ||
164 | |||
135 | return sprintf(buf, "%d\n", ret); | 165 | return sprintf(buf, "%d\n", ret); |
136 | } | 166 | } |
137 | 167 | ||
@@ -184,17 +214,14 @@ int memory_isolate_notify(unsigned long val, void *v) | |||
184 | * OK to have direct references to sparsemem variables in here. | 214 | * OK to have direct references to sparsemem variables in here. |
185 | */ | 215 | */ |
186 | static int | 216 | static int |
187 | memory_block_action(struct memory_block *mem, unsigned long action) | 217 | memory_section_action(unsigned long phys_index, unsigned long action) |
188 | { | 218 | { |
189 | int i; | 219 | int i; |
190 | unsigned long psection; | ||
191 | unsigned long start_pfn, start_paddr; | 220 | unsigned long start_pfn, start_paddr; |
192 | struct page *first_page; | 221 | struct page *first_page; |
193 | int ret; | 222 | int ret; |
194 | int old_state = mem->state; | ||
195 | 223 | ||
196 | psection = mem->phys_index; | 224 | first_page = pfn_to_page(phys_index << PFN_SECTION_SHIFT); |
197 | first_page = pfn_to_page(psection << PFN_SECTION_SHIFT); | ||
198 | 225 | ||
199 | /* | 226 | /* |
200 | * The probe routines leave the pages reserved, just | 227 | * The probe routines leave the pages reserved, just |
@@ -207,8 +234,8 @@ memory_block_action(struct memory_block *mem, unsigned long action) | |||
207 | continue; | 234 | continue; |
208 | 235 | ||
209 | printk(KERN_WARNING "section number %ld page number %d " | 236 | printk(KERN_WARNING "section number %ld page number %d " |
210 | "not reserved, was it already online? \n", | 237 | "not reserved, was it already online?\n", |
211 | psection, i); | 238 | phys_index, i); |
212 | return -EBUSY; | 239 | return -EBUSY; |
213 | } | 240 | } |
214 | } | 241 | } |
@@ -219,18 +246,13 @@ memory_block_action(struct memory_block *mem, unsigned long action) | |||
219 | ret = online_pages(start_pfn, PAGES_PER_SECTION); | 246 | ret = online_pages(start_pfn, PAGES_PER_SECTION); |
220 | break; | 247 | break; |
221 | case MEM_OFFLINE: | 248 | case MEM_OFFLINE: |
222 | mem->state = MEM_GOING_OFFLINE; | ||
223 | start_paddr = page_to_pfn(first_page) << PAGE_SHIFT; | 249 | start_paddr = page_to_pfn(first_page) << PAGE_SHIFT; |
224 | ret = remove_memory(start_paddr, | 250 | ret = remove_memory(start_paddr, |
225 | PAGES_PER_SECTION << PAGE_SHIFT); | 251 | PAGES_PER_SECTION << PAGE_SHIFT); |
226 | if (ret) { | ||
227 | mem->state = old_state; | ||
228 | break; | ||
229 | } | ||
230 | break; | 252 | break; |
231 | default: | 253 | default: |
232 | WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n", | 254 | WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: " |
233 | __func__, mem, action, action); | 255 | "%ld\n", __func__, phys_index, action, action); |
234 | ret = -EINVAL; | 256 | ret = -EINVAL; |
235 | } | 257 | } |
236 | 258 | ||
@@ -240,7 +262,8 @@ memory_block_action(struct memory_block *mem, unsigned long action) | |||
240 | static int memory_block_change_state(struct memory_block *mem, | 262 | static int memory_block_change_state(struct memory_block *mem, |
241 | unsigned long to_state, unsigned long from_state_req) | 263 | unsigned long to_state, unsigned long from_state_req) |
242 | { | 264 | { |
243 | int ret = 0; | 265 | int i, ret = 0; |
266 | |||
244 | mutex_lock(&mem->state_mutex); | 267 | mutex_lock(&mem->state_mutex); |
245 | 268 | ||
246 | if (mem->state != from_state_req) { | 269 | if (mem->state != from_state_req) { |
@@ -248,8 +271,22 @@ static int memory_block_change_state(struct memory_block *mem, | |||
248 | goto out; | 271 | goto out; |
249 | } | 272 | } |
250 | 273 | ||
251 | ret = memory_block_action(mem, to_state); | 274 | if (to_state == MEM_OFFLINE) |
252 | if (!ret) | 275 | mem->state = MEM_GOING_OFFLINE; |
276 | |||
277 | for (i = 0; i < sections_per_block; i++) { | ||
278 | ret = memory_section_action(mem->phys_index + i, to_state); | ||
279 | if (ret) | ||
280 | break; | ||
281 | } | ||
282 | |||
283 | if (ret) { | ||
284 | for (i = 0; i < sections_per_block; i++) | ||
285 | memory_section_action(mem->phys_index + i, | ||
286 | from_state_req); | ||
287 | |||
288 | mem->state = from_state_req; | ||
289 | } else | ||
253 | mem->state = to_state; | 290 | mem->state = to_state; |
254 | 291 | ||
255 | out: | 292 | out: |
@@ -262,20 +299,15 @@ store_mem_state(struct sys_device *dev, | |||
262 | struct sysdev_attribute *attr, const char *buf, size_t count) | 299 | struct sysdev_attribute *attr, const char *buf, size_t count) |
263 | { | 300 | { |
264 | struct memory_block *mem; | 301 | struct memory_block *mem; |
265 | unsigned int phys_section_nr; | ||
266 | int ret = -EINVAL; | 302 | int ret = -EINVAL; |
267 | 303 | ||
268 | mem = container_of(dev, struct memory_block, sysdev); | 304 | mem = container_of(dev, struct memory_block, sysdev); |
269 | phys_section_nr = mem->phys_index; | ||
270 | |||
271 | if (!present_section_nr(phys_section_nr)) | ||
272 | goto out; | ||
273 | 305 | ||
274 | if (!strncmp(buf, "online", min((int)count, 6))) | 306 | if (!strncmp(buf, "online", min((int)count, 6))) |
275 | ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); | 307 | ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); |
276 | else if(!strncmp(buf, "offline", min((int)count, 7))) | 308 | else if(!strncmp(buf, "offline", min((int)count, 7))) |
277 | ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); | 309 | ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); |
278 | out: | 310 | |
279 | if (ret) | 311 | if (ret) |
280 | return ret; | 312 | return ret; |
281 | return count; | 313 | return count; |
@@ -315,7 +347,7 @@ static ssize_t | |||
315 | print_block_size(struct sysdev_class *class, struct sysdev_class_attribute *attr, | 347 | print_block_size(struct sysdev_class *class, struct sysdev_class_attribute *attr, |
316 | char *buf) | 348 | char *buf) |
317 | { | 349 | { |
318 | return sprintf(buf, "%lx\n", (unsigned long)PAGES_PER_SECTION * PAGE_SIZE); | 350 | return sprintf(buf, "%lx\n", get_memory_block_size()); |
319 | } | 351 | } |
320 | 352 | ||
321 | static SYSDEV_CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL); | 353 | static SYSDEV_CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL); |
@@ -444,6 +476,7 @@ struct memory_block *find_memory_block_hinted(struct mem_section *section, | |||
444 | struct sys_device *sysdev; | 476 | struct sys_device *sysdev; |
445 | struct memory_block *mem; | 477 | struct memory_block *mem; |
446 | char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1]; | 478 | char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1]; |
479 | int block_id = base_memory_block_id(__section_nr(section)); | ||
447 | 480 | ||
448 | kobj = hint ? &hint->sysdev.kobj : NULL; | 481 | kobj = hint ? &hint->sysdev.kobj : NULL; |
449 | 482 | ||
@@ -451,7 +484,7 @@ struct memory_block *find_memory_block_hinted(struct mem_section *section, | |||
451 | * This only works because we know that section == sysdev->id | 484 | * This only works because we know that section == sysdev->id |
452 | * slightly redundant with sysdev_register() | 485 | * slightly redundant with sysdev_register() |
453 | */ | 486 | */ |
454 | sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section)); | 487 | sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, block_id); |
455 | 488 | ||
456 | kobj = kset_find_obj_hinted(&memory_sysdev_class.kset, name, kobj); | 489 | kobj = kset_find_obj_hinted(&memory_sysdev_class.kset, name, kobj); |
457 | if (!kobj) | 490 | if (!kobj) |
@@ -476,26 +509,27 @@ struct memory_block *find_memory_block(struct mem_section *section) | |||
476 | return find_memory_block_hinted(section, NULL); | 509 | return find_memory_block_hinted(section, NULL); |
477 | } | 510 | } |
478 | 511 | ||
479 | static int add_memory_block(int nid, struct mem_section *section, | 512 | static int init_memory_block(struct memory_block **memory, |
480 | unsigned long state, enum mem_add_context context) | 513 | struct mem_section *section, unsigned long state) |
481 | { | 514 | { |
482 | struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL); | 515 | struct memory_block *mem; |
483 | unsigned long start_pfn; | 516 | unsigned long start_pfn; |
517 | int scn_nr; | ||
484 | int ret = 0; | 518 | int ret = 0; |
485 | 519 | ||
520 | mem = kzalloc(sizeof(*mem), GFP_KERNEL); | ||
486 | if (!mem) | 521 | if (!mem) |
487 | return -ENOMEM; | 522 | return -ENOMEM; |
488 | 523 | ||
489 | mutex_lock(&mem_sysfs_mutex); | 524 | scn_nr = __section_nr(section); |
490 | 525 | mem->phys_index = base_memory_block_id(scn_nr) * sections_per_block; | |
491 | mem->phys_index = __section_nr(section); | ||
492 | mem->state = state; | 526 | mem->state = state; |
493 | mem->section_count++; | 527 | mem->section_count++; |
494 | mutex_init(&mem->state_mutex); | 528 | mutex_init(&mem->state_mutex); |
495 | start_pfn = section_nr_to_pfn(mem->phys_index); | 529 | start_pfn = section_nr_to_pfn(mem->phys_index); |
496 | mem->phys_device = arch_get_memory_phys_device(start_pfn); | 530 | mem->phys_device = arch_get_memory_phys_device(start_pfn); |
497 | 531 | ||
498 | ret = register_memory(mem, section); | 532 | ret = register_memory(mem); |
499 | if (!ret) | 533 | if (!ret) |
500 | ret = mem_create_simple_file(mem, phys_index); | 534 | ret = mem_create_simple_file(mem, phys_index); |
501 | if (!ret) | 535 | if (!ret) |
@@ -504,8 +538,29 @@ static int add_memory_block(int nid, struct mem_section *section, | |||
504 | ret = mem_create_simple_file(mem, phys_device); | 538 | ret = mem_create_simple_file(mem, phys_device); |
505 | if (!ret) | 539 | if (!ret) |
506 | ret = mem_create_simple_file(mem, removable); | 540 | ret = mem_create_simple_file(mem, removable); |
541 | |||
542 | *memory = mem; | ||
543 | return ret; | ||
544 | } | ||
545 | |||
546 | static int add_memory_section(int nid, struct mem_section *section, | ||
547 | unsigned long state, enum mem_add_context context) | ||
548 | { | ||
549 | struct memory_block *mem; | ||
550 | int ret = 0; | ||
551 | |||
552 | mutex_lock(&mem_sysfs_mutex); | ||
553 | |||
554 | mem = find_memory_block(section); | ||
555 | if (mem) { | ||
556 | mem->section_count++; | ||
557 | kobject_put(&mem->sysdev.kobj); | ||
558 | } else | ||
559 | ret = init_memory_block(&mem, section, state); | ||
560 | |||
507 | if (!ret) { | 561 | if (!ret) { |
508 | if (context == HOTPLUG) | 562 | if (context == HOTPLUG && |
563 | mem->section_count == sections_per_block) | ||
509 | ret = register_mem_sect_under_node(mem, nid); | 564 | ret = register_mem_sect_under_node(mem, nid); |
510 | } | 565 | } |
511 | 566 | ||
@@ -528,8 +583,10 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, | |||
528 | mem_remove_simple_file(mem, state); | 583 | mem_remove_simple_file(mem, state); |
529 | mem_remove_simple_file(mem, phys_device); | 584 | mem_remove_simple_file(mem, phys_device); |
530 | mem_remove_simple_file(mem, removable); | 585 | mem_remove_simple_file(mem, removable); |
531 | unregister_memory(mem, section); | 586 | unregister_memory(mem); |
532 | } | 587 | kfree(mem); |
588 | } else | ||
589 | kobject_put(&mem->sysdev.kobj); | ||
533 | 590 | ||
534 | mutex_unlock(&mem_sysfs_mutex); | 591 | mutex_unlock(&mem_sysfs_mutex); |
535 | return 0; | 592 | return 0; |
@@ -541,7 +598,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, | |||
541 | */ | 598 | */ |
542 | int register_new_memory(int nid, struct mem_section *section) | 599 | int register_new_memory(int nid, struct mem_section *section) |
543 | { | 600 | { |
544 | return add_memory_block(nid, section, MEM_OFFLINE, HOTPLUG); | 601 | return add_memory_section(nid, section, MEM_OFFLINE, HOTPLUG); |
545 | } | 602 | } |
546 | 603 | ||
547 | int unregister_memory_section(struct mem_section *section) | 604 | int unregister_memory_section(struct mem_section *section) |
@@ -560,12 +617,16 @@ int __init memory_dev_init(void) | |||
560 | unsigned int i; | 617 | unsigned int i; |
561 | int ret; | 618 | int ret; |
562 | int err; | 619 | int err; |
620 | unsigned long block_sz; | ||
563 | 621 | ||
564 | memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops; | 622 | memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops; |
565 | ret = sysdev_class_register(&memory_sysdev_class); | 623 | ret = sysdev_class_register(&memory_sysdev_class); |
566 | if (ret) | 624 | if (ret) |
567 | goto out; | 625 | goto out; |
568 | 626 | ||
627 | block_sz = get_memory_block_size(); | ||
628 | sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE; | ||
629 | |||
569 | /* | 630 | /* |
570 | * Create entries for memory sections that were found | 631 | * Create entries for memory sections that were found |
571 | * during boot and have been initialized | 632 | * during boot and have been initialized |
@@ -573,8 +634,8 @@ int __init memory_dev_init(void) | |||
573 | for (i = 0; i < NR_MEM_SECTIONS; i++) { | 634 | for (i = 0; i < NR_MEM_SECTIONS; i++) { |
574 | if (!present_section_nr(i)) | 635 | if (!present_section_nr(i)) |
575 | continue; | 636 | continue; |
576 | err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, | 637 | err = add_memory_section(0, __nr_to_section(i), MEM_ONLINE, |
577 | BOOT); | 638 | BOOT); |
578 | if (!ret) | 639 | if (!ret) |
579 | ret = err; | 640 | ret = err; |
580 | } | 641 | } |