]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/powerpc/platforms/pseries/hotplug-memory.c
[POWERPC] Hotplug memory remove notifications for powerpc
[karo-tx-linux.git] / arch / powerpc / platforms / pseries / hotplug-memory.c
1 /*
2  * pseries Memory Hotplug infrastructure.
3  *
4  * Copyright (C) 2008 Badari Pulavarty, IBM Corporation
5  *
6  *      This program is free software; you can redistribute it and/or
7  *      modify it under the terms of the GNU General Public License
8  *      as published by the Free Software Foundation; either version
9  *      2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/of.h>
13 #include <asm/firmware.h>
14 #include <asm/machdep.h>
15 #include <asm/pSeries_reconfig.h>
16
17 static int pseries_remove_memory(struct device_node *np)
18 {
19         const char *type;
20         const unsigned int *my_index;
21         const unsigned int *regs;
22         u64 start_pfn, start;
23         struct zone *zone;
24         int ret = -EINVAL;
25
26         /*
27          * Check to see if we are actually removing memory
28          */
29         type = of_get_property(np, "device_type", NULL);
30         if (type == NULL || strcmp(type, "memory") != 0)
31                 return 0;
32
33         /*
34          * Find the memory index and size of the removing section
35          */
36         my_index = of_get_property(np, "ibm,my-drc-index", NULL);
37         if (!my_index)
38                 return ret;
39
40         regs = of_get_property(np, "reg", NULL);
41         if (!regs)
42                 return ret;
43
44         start_pfn = section_nr_to_pfn(*my_index & 0xffff);
45         zone = page_zone(pfn_to_page(start_pfn));
46
47         /*
48          * Remove section mappings and sysfs entries for the
49          * section of the memory we are removing.
50          *
51          * NOTE: Ideally, this should be done in generic code like
52          * remove_memory(). But remove_memory() gets called by writing
53          * to sysfs "state" file and we can't remove sysfs entries
54          * while writing to it. So we have to defer it to here.
55          */
56         ret = __remove_pages(zone, start_pfn, regs[3] >> PAGE_SHIFT);
57         if (ret)
58                 return ret;
59
60         /*
61          * Remove htab bolted mappings for this section of memory
62          */
63         start = (unsigned long)__va(start_pfn << PAGE_SHIFT);
64         ret = remove_section_mapping(start, start + regs[3]);
65         return ret;
66 }
67
68 static int pseries_memory_notifier(struct notifier_block *nb,
69                                 unsigned long action, void *node)
70 {
71         int err = NOTIFY_OK;
72
73         switch (action) {
74         case PSERIES_RECONFIG_ADD:
75                 break;
76         case PSERIES_RECONFIG_REMOVE:
77                 if (pseries_remove_memory(node))
78                         err = NOTIFY_BAD;
79                 break;
80         default:
81                 err = NOTIFY_DONE;
82                 break;
83         }
84         return err;
85 }
86
87 static struct notifier_block pseries_mem_nb = {
88         .notifier_call = pseries_memory_notifier,
89 };
90
91 static int __init pseries_memory_hotplug_init(void)
92 {
93         if (firmware_has_feature(FW_FEATURE_LPAR))
94                 pSeries_reconfig_notifier_register(&pseries_mem_nb);
95
96         return 0;
97 }
98 machine_device_initcall(pseries, pseries_memory_hotplug_init);