~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/platforms/pseries/hotplug-memory.c

Version: ~ [ linux-5.2 ] ~ [ linux-5.1.16 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.57 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.132 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.184 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.184 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.69 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 #define pr_fmt(fmt)     "pseries-hotplug-mem: " fmt
 13 
 14 #include <linux/of.h>
 15 #include <linux/of_address.h>
 16 #include <linux/memblock.h>
 17 #include <linux/memory.h>
 18 #include <linux/memory_hotplug.h>
 19 #include <linux/slab.h>
 20 
 21 #include <asm/firmware.h>
 22 #include <asm/machdep.h>
 23 #include <asm/prom.h>
 24 #include <asm/sparsemem.h>
 25 #include "pseries.h"
 26 
 27 static bool rtas_hp_event;
 28 
 29 unsigned long pseries_memory_block_size(void)
 30 {
 31         struct device_node *np;
 32         unsigned int memblock_size = MIN_MEMORY_BLOCK_SIZE;
 33         struct resource r;
 34 
 35         np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
 36         if (np) {
 37                 const __be64 *size;
 38 
 39                 size = of_get_property(np, "ibm,lmb-size", NULL);
 40                 if (size)
 41                         memblock_size = be64_to_cpup(size);
 42                 of_node_put(np);
 43         } else  if (machine_is(pseries)) {
 44                 /* This fallback really only applies to pseries */
 45                 unsigned int memzero_size = 0;
 46 
 47                 np = of_find_node_by_path("/memory@0");
 48                 if (np) {
 49                         if (!of_address_to_resource(np, 0, &r))
 50                                 memzero_size = resource_size(&r);
 51                         of_node_put(np);
 52                 }
 53 
 54                 if (memzero_size) {
 55                         /* We now know the size of memory@0, use this to find
 56                          * the first memoryblock and get its size.
 57                          */
 58                         char buf[64];
 59 
 60                         sprintf(buf, "/memory@%x", memzero_size);
 61                         np = of_find_node_by_path(buf);
 62                         if (np) {
 63                                 if (!of_address_to_resource(np, 0, &r))
 64                                         memblock_size = resource_size(&r);
 65                                 of_node_put(np);
 66                         }
 67                 }
 68         }
 69         return memblock_size;
 70 }
 71 
 72 static void dlpar_free_drconf_property(struct property *prop)
 73 {
 74         kfree(prop->name);
 75         kfree(prop->value);
 76         kfree(prop);
 77 }
 78 
 79 static struct property *dlpar_clone_drconf_property(struct device_node *dn)
 80 {
 81         struct property *prop, *new_prop;
 82         struct of_drconf_cell *lmbs;
 83         u32 num_lmbs, *p;
 84         int i;
 85 
 86         prop = of_find_property(dn, "ibm,dynamic-memory", NULL);
 87         if (!prop)
 88                 return NULL;
 89 
 90         new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
 91         if (!new_prop)
 92                 return NULL;
 93 
 94         new_prop->name = kstrdup(prop->name, GFP_KERNEL);
 95         new_prop->value = kmalloc(prop->length, GFP_KERNEL);
 96         if (!new_prop->name || !new_prop->value) {
 97                 dlpar_free_drconf_property(new_prop);
 98                 return NULL;
 99         }
100 
101         memcpy(new_prop->value, prop->value, prop->length);
102         new_prop->length = prop->length;
103 
104         /* Convert the property to cpu endian-ness */
105         p = new_prop->value;
106         *p = be32_to_cpu(*p);
107 
108         num_lmbs = *p++;
109         lmbs = (struct of_drconf_cell *)p;
110 
111         for (i = 0; i < num_lmbs; i++) {
112                 lmbs[i].base_addr = be64_to_cpu(lmbs[i].base_addr);
113                 lmbs[i].drc_index = be32_to_cpu(lmbs[i].drc_index);
114                 lmbs[i].aa_index = be32_to_cpu(lmbs[i].aa_index);
115                 lmbs[i].flags = be32_to_cpu(lmbs[i].flags);
116         }
117 
118         return new_prop;
119 }
120 
121 static struct memory_block *lmb_to_memblock(struct of_drconf_cell *lmb)
122 {
123         unsigned long section_nr;
124         struct mem_section *mem_sect;
125         struct memory_block *mem_block;
126 
127         section_nr = pfn_to_section_nr(PFN_DOWN(lmb->base_addr));
128         mem_sect = __nr_to_section(section_nr);
129 
130         mem_block = find_memory_block(mem_sect);
131         return mem_block;
132 }
133 
134 #ifdef CONFIG_MEMORY_HOTREMOVE
135 static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size)
136 {
137         unsigned long block_sz, start_pfn;
138         int sections_per_block;
139         int i, nid;
140 
141         start_pfn = base >> PAGE_SHIFT;
142 
143         lock_device_hotplug();
144 
145         if (!pfn_valid(start_pfn))
146                 goto out;
147 
148         block_sz = pseries_memory_block_size();
149         sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
150         nid = memory_add_physaddr_to_nid(base);
151 
152         for (i = 0; i < sections_per_block; i++) {
153                 remove_memory(nid, base, MIN_MEMORY_BLOCK_SIZE);
154                 base += MIN_MEMORY_BLOCK_SIZE;
155         }
156 
157 out:
158         /* Update memory regions for memory remove */
159         memblock_remove(base, memblock_size);
160         unlock_device_hotplug();
161         return 0;
162 }
163 
164 static int pseries_remove_mem_node(struct device_node *np)
165 {
166         const char *type;
167         const __be32 *regs;
168         unsigned long base;
169         unsigned int lmb_size;
170         int ret = -EINVAL;
171 
172         /*
173          * Check to see if we are actually removing memory
174          */
175         type = of_get_property(np, "device_type", NULL);
176         if (type == NULL || strcmp(type, "memory") != 0)
177                 return 0;
178 
179         /*
180          * Find the base address and size of the memblock
181          */
182         regs = of_get_property(np, "reg", NULL);
183         if (!regs)
184                 return ret;
185 
186         base = be64_to_cpu(*(unsigned long *)regs);
187         lmb_size = be32_to_cpu(regs[3]);
188 
189         pseries_remove_memblock(base, lmb_size);
190         return 0;
191 }
192 
193 static bool lmb_is_removable(struct of_drconf_cell *lmb)
194 {
195         int i, scns_per_block;
196         int rc = 1;
197         unsigned long pfn, block_sz;
198         u64 phys_addr;
199 
200         if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
201                 return false;
202 
203         block_sz = memory_block_size_bytes();
204         scns_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
205         phys_addr = lmb->base_addr;
206 
207         for (i = 0; i < scns_per_block; i++) {
208                 pfn = PFN_DOWN(phys_addr);
209                 if (!pfn_present(pfn))
210                         continue;
211 
212                 rc &= is_mem_section_removable(pfn, PAGES_PER_SECTION);
213                 phys_addr += MIN_MEMORY_BLOCK_SIZE;
214         }
215 
216         return rc ? true : false;
217 }
218 
219 static int dlpar_add_lmb(struct of_drconf_cell *);
220 
221 static int dlpar_remove_lmb(struct of_drconf_cell *lmb)
222 {
223         struct memory_block *mem_block;
224         unsigned long block_sz;
225         int nid, rc;
226 
227         if (!lmb_is_removable(lmb))
228                 return -EINVAL;
229 
230         mem_block = lmb_to_memblock(lmb);
231         if (!mem_block)
232                 return -EINVAL;
233 
234         rc = device_offline(&mem_block->dev);
235         put_device(&mem_block->dev);
236         if (rc)
237                 return rc;
238 
239         block_sz = pseries_memory_block_size();
240         nid = memory_add_physaddr_to_nid(lmb->base_addr);
241 
242         remove_memory(nid, lmb->base_addr, block_sz);
243 
244         /* Update memory regions for memory remove */
245         memblock_remove(lmb->base_addr, block_sz);
246 
247         dlpar_release_drc(lmb->drc_index);
248 
249         lmb->flags &= ~DRCONF_MEM_ASSIGNED;
250         return 0;
251 }
252 
253 static int dlpar_memory_remove_by_count(u32 lmbs_to_remove,
254                                         struct property *prop)
255 {
256         struct of_drconf_cell *lmbs;
257         int lmbs_removed = 0;
258         int lmbs_available = 0;
259         u32 num_lmbs, *p;
260         int i, rc;
261 
262         pr_info("Attempting to hot-remove %d LMB(s)\n", lmbs_to_remove);
263 
264         if (lmbs_to_remove == 0)
265                 return -EINVAL;
266 
267         p = prop->value;
268         num_lmbs = *p++;
269         lmbs = (struct of_drconf_cell *)p;
270 
271         /* Validate that there are enough LMBs to satisfy the request */
272         for (i = 0; i < num_lmbs; i++) {
273                 if (lmbs[i].flags & DRCONF_MEM_ASSIGNED)
274                         lmbs_available++;
275         }
276 
277         if (lmbs_available < lmbs_to_remove)
278                 return -EINVAL;
279 
280         for (i = 0; i < num_lmbs && lmbs_removed < lmbs_to_remove; i++) {
281                 rc = dlpar_remove_lmb(&lmbs[i]);
282                 if (rc)
283                         continue;
284 
285                 lmbs_removed++;
286 
287                 /* Mark this lmb so we can add it later if all of the
288                  * requested LMBs cannot be removed.
289                  */
290                 lmbs[i].reserved = 1;
291         }
292 
293         if (lmbs_removed != lmbs_to_remove) {
294                 pr_err("Memory hot-remove failed, adding LMB's back\n");
295 
296                 for (i = 0; i < num_lmbs; i++) {
297                         if (!lmbs[i].reserved)
298                                 continue;
299 
300                         rc = dlpar_add_lmb(&lmbs[i]);
301                         if (rc)
302                                 pr_err("Failed to add LMB back, drc index %x\n",
303                                        lmbs[i].drc_index);
304 
305                         lmbs[i].reserved = 0;
306                 }
307 
308                 rc = -EINVAL;
309         } else {
310                 for (i = 0; i < num_lmbs; i++) {
311                         if (!lmbs[i].reserved)
312                                 continue;
313 
314                         pr_info("Memory at %llx was hot-removed\n",
315                                 lmbs[i].base_addr);
316 
317                         lmbs[i].reserved = 0;
318                 }
319                 rc = 0;
320         }
321 
322         return rc;
323 }
324 
325 static int dlpar_memory_remove_by_index(u32 drc_index, struct property *prop)
326 {
327         struct of_drconf_cell *lmbs;
328         u32 num_lmbs, *p;
329         int lmb_found;
330         int i, rc;
331 
332         pr_info("Attempting to hot-remove LMB, drc index %x\n", drc_index);
333 
334         p = prop->value;
335         num_lmbs = *p++;
336         lmbs = (struct of_drconf_cell *)p;
337 
338         lmb_found = 0;
339         for (i = 0; i < num_lmbs; i++) {
340                 if (lmbs[i].drc_index == drc_index) {
341                         lmb_found = 1;
342                         rc = dlpar_remove_lmb(&lmbs[i]);
343                         break;
344                 }
345         }
346 
347         if (!lmb_found)
348                 rc = -EINVAL;
349 
350         if (rc)
351                 pr_info("Failed to hot-remove memory at %llx\n",
352                         lmbs[i].base_addr);
353         else
354                 pr_info("Memory at %llx was hot-removed\n", lmbs[i].base_addr);
355 
356         return rc;
357 }
358 
359 #else
360 static inline int pseries_remove_memblock(unsigned long base,
361                                           unsigned int memblock_size)
362 {
363         return -EOPNOTSUPP;
364 }
365 static inline int pseries_remove_mem_node(struct device_node *np)
366 {
367         return 0;
368 }
369 static inline int dlpar_memory_remove(struct pseries_hp_errorlog *hp_elog)
370 {
371         return -EOPNOTSUPP;
372 }
373 static int dlpar_remove_lmb(struct of_drconf_cell *lmb)
374 {
375         return -EOPNOTSUPP;
376 }
377 static int dlpar_memory_remove_by_count(u32 lmbs_to_remove,
378                                         struct property *prop)
379 {
380         return -EOPNOTSUPP;
381 }
382 static int dlpar_memory_remove_by_index(u32 drc_index, struct property *prop)
383 {
384         return -EOPNOTSUPP;
385 }
386 
387 #endif /* CONFIG_MEMORY_HOTREMOVE */
388 
389 static int dlpar_add_lmb(struct of_drconf_cell *lmb)
390 {
391         struct memory_block *mem_block;
392         unsigned long block_sz;
393         int nid, rc;
394 
395         if (lmb->flags & DRCONF_MEM_ASSIGNED)
396                 return -EINVAL;
397 
398         block_sz = memory_block_size_bytes();
399 
400         rc = dlpar_acquire_drc(lmb->drc_index);
401         if (rc)
402                 return rc;
403 
404         /* Find the node id for this address */
405         nid = memory_add_physaddr_to_nid(lmb->base_addr);
406 
407         /* Add the memory */
408         rc = add_memory(nid, lmb->base_addr, block_sz);
409         if (rc) {
410                 dlpar_release_drc(lmb->drc_index);
411                 return rc;
412         }
413 
414         /* Register this block of memory */
415         rc = memblock_add(lmb->base_addr, block_sz);
416         if (rc) {
417                 remove_memory(nid, lmb->base_addr, block_sz);
418                 dlpar_release_drc(lmb->drc_index);
419                 return rc;
420         }
421 
422         mem_block = lmb_to_memblock(lmb);
423         if (!mem_block) {
424                 remove_memory(nid, lmb->base_addr, block_sz);
425                 dlpar_release_drc(lmb->drc_index);
426                 return -EINVAL;
427         }
428 
429         rc = device_online(&mem_block->dev);
430         put_device(&mem_block->dev);
431         if (rc) {
432                 remove_memory(nid, lmb->base_addr, block_sz);
433                 dlpar_release_drc(lmb->drc_index);
434                 return rc;
435         }
436 
437         lmb->flags |= DRCONF_MEM_ASSIGNED;
438         return 0;
439 }
440 
441 static int dlpar_memory_add_by_count(u32 lmbs_to_add, struct property *prop)
442 {
443         struct of_drconf_cell *lmbs;
444         u32 num_lmbs, *p;
445         int lmbs_available = 0;
446         int lmbs_added = 0;
447         int i, rc;
448 
449         pr_info("Attempting to hot-add %d LMB(s)\n", lmbs_to_add);
450 
451         if (lmbs_to_add == 0)
452                 return -EINVAL;
453 
454         p = prop->value;
455         num_lmbs = *p++;
456         lmbs = (struct of_drconf_cell *)p;
457 
458         /* Validate that there are enough LMBs to satisfy the request */
459         for (i = 0; i < num_lmbs; i++) {
460                 if (!(lmbs[i].flags & DRCONF_MEM_ASSIGNED))
461                         lmbs_available++;
462         }
463 
464         if (lmbs_available < lmbs_to_add)
465                 return -EINVAL;
466 
467         for (i = 0; i < num_lmbs && lmbs_to_add != lmbs_added; i++) {
468                 rc = dlpar_add_lmb(&lmbs[i]);
469                 if (rc)
470                         continue;
471 
472                 lmbs_added++;
473 
474                 /* Mark this lmb so we can remove it later if all of the
475                  * requested LMBs cannot be added.
476                  */
477                 lmbs[i].reserved = 1;
478         }
479 
480         if (lmbs_added != lmbs_to_add) {
481                 pr_err("Memory hot-add failed, removing any added LMBs\n");
482 
483                 for (i = 0; i < num_lmbs; i++) {
484                         if (!lmbs[i].reserved)
485                                 continue;
486 
487                         rc = dlpar_remove_lmb(&lmbs[i]);
488                         if (rc)
489                                 pr_err("Failed to remove LMB, drc index %x\n",
490                                        be32_to_cpu(lmbs[i].drc_index));
491                 }
492                 rc = -EINVAL;
493         } else {
494                 for (i = 0; i < num_lmbs; i++) {
495                         if (!lmbs[i].reserved)
496                                 continue;
497 
498                         pr_info("Memory at %llx (drc index %x) was hot-added\n",
499                                 lmbs[i].base_addr, lmbs[i].drc_index);
500                         lmbs[i].reserved = 0;
501                 }
502         }
503 
504         return rc;
505 }
506 
507 static int dlpar_memory_add_by_index(u32 drc_index, struct property *prop)
508 {
509         struct of_drconf_cell *lmbs;
510         u32 num_lmbs, *p;
511         int i, lmb_found;
512         int rc;
513 
514         pr_info("Attempting to hot-add LMB, drc index %x\n", drc_index);
515 
516         p = prop->value;
517         num_lmbs = *p++;
518         lmbs = (struct of_drconf_cell *)p;
519 
520         lmb_found = 0;
521         for (i = 0; i < num_lmbs; i++) {
522                 if (lmbs[i].drc_index == drc_index) {
523                         lmb_found = 1;
524                         rc = dlpar_add_lmb(&lmbs[i]);
525                         break;
526                 }
527         }
528 
529         if (!lmb_found)
530                 rc = -EINVAL;
531 
532         if (rc)
533                 pr_info("Failed to hot-add memory, drc index %x\n", drc_index);
534         else
535                 pr_info("Memory at %llx (drc index %x) was hot-added\n",
536                         lmbs[i].base_addr, drc_index);
537 
538         return rc;
539 }
540 
541 static void dlpar_update_drconf_property(struct device_node *dn,
542                                          struct property *prop)
543 {
544         struct of_drconf_cell *lmbs;
545         u32 num_lmbs, *p;
546         int i;
547 
548         /* Convert the property back to BE */
549         p = prop->value;
550         num_lmbs = *p;
551         *p = cpu_to_be32(*p);
552         p++;
553 
554         lmbs = (struct of_drconf_cell *)p;
555         for (i = 0; i < num_lmbs; i++) {
556                 lmbs[i].base_addr = cpu_to_be64(lmbs[i].base_addr);
557                 lmbs[i].drc_index = cpu_to_be32(lmbs[i].drc_index);
558                 lmbs[i].aa_index = cpu_to_be32(lmbs[i].aa_index);
559                 lmbs[i].flags = cpu_to_be32(lmbs[i].flags);
560         }
561 
562         rtas_hp_event = true;
563         of_update_property(dn, prop);
564         rtas_hp_event = false;
565 }
566 
567 int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
568 {
569         struct device_node *dn;
570         struct property *prop;
571         u32 count, drc_index;
572         int rc;
573 
574         count = hp_elog->_drc_u.drc_count;
575         drc_index = hp_elog->_drc_u.drc_index;
576 
577         lock_device_hotplug();
578 
579         dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
580         if (!dn) {
581                 rc = -EINVAL;
582                 goto dlpar_memory_out;
583         }
584 
585         prop = dlpar_clone_drconf_property(dn);
586         if (!prop) {
587                 rc = -EINVAL;
588                 goto dlpar_memory_out;
589         }
590 
591         switch (hp_elog->action) {
592         case PSERIES_HP_ELOG_ACTION_ADD:
593                 if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
594                         rc = dlpar_memory_add_by_count(count, prop);
595                 else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
596                         rc = dlpar_memory_add_by_index(drc_index, prop);
597                 else
598                         rc = -EINVAL;
599                 break;
600         case PSERIES_HP_ELOG_ACTION_REMOVE:
601                 if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
602                         rc = dlpar_memory_remove_by_count(count, prop);
603                 else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
604                         rc = dlpar_memory_remove_by_index(drc_index, prop);
605                 else
606                         rc = -EINVAL;
607                 break;
608         default:
609                 pr_err("Invalid action (%d) specified\n", hp_elog->action);
610                 rc = -EINVAL;
611                 break;
612         }
613 
614         if (rc)
615                 dlpar_free_drconf_property(prop);
616         else
617                 dlpar_update_drconf_property(dn, prop);
618 
619 dlpar_memory_out:
620         of_node_put(dn);
621         unlock_device_hotplug();
622         return rc;
623 }
624 
625 static int pseries_add_mem_node(struct device_node *np)
626 {
627         const char *type;
628         const __be32 *regs;
629         unsigned long base;
630         unsigned int lmb_size;
631         int ret = -EINVAL;
632 
633         /*
634          * Check to see if we are actually adding memory
635          */
636         type = of_get_property(np, "device_type", NULL);
637         if (type == NULL || strcmp(type, "memory") != 0)
638                 return 0;
639 
640         /*
641          * Find the base and size of the memblock
642          */
643         regs = of_get_property(np, "reg", NULL);
644         if (!regs)
645                 return ret;
646 
647         base = be64_to_cpu(*(unsigned long *)regs);
648         lmb_size = be32_to_cpu(regs[3]);
649 
650         /*
651          * Update memory region to represent the memory add
652          */
653         ret = memblock_add(base, lmb_size);
654         return (ret < 0) ? -EINVAL : 0;
655 }
656 
657 static int pseries_update_drconf_memory(struct of_reconfig_data *pr)
658 {
659         struct of_drconf_cell *new_drmem, *old_drmem;
660         unsigned long memblock_size;
661         u32 entries;
662         __be32 *p;
663         int i, rc = -EINVAL;
664 
665         if (rtas_hp_event)
666                 return 0;
667 
668         memblock_size = pseries_memory_block_size();
669         if (!memblock_size)
670                 return -EINVAL;
671 
672         p = (__be32 *) pr->old_prop->value;
673         if (!p)
674                 return -EINVAL;
675 
676         /* The first int of the property is the number of lmb's described
677          * by the property. This is followed by an array of of_drconf_cell
678          * entries. Get the number of entries and skip to the array of
679          * of_drconf_cell's.
680          */
681         entries = be32_to_cpu(*p++);
682         old_drmem = (struct of_drconf_cell *)p;
683 
684         p = (__be32 *)pr->prop->value;
685         p++;
686         new_drmem = (struct of_drconf_cell *)p;
687 
688         for (i = 0; i < entries; i++) {
689                 if ((be32_to_cpu(old_drmem[i].flags) & DRCONF_MEM_ASSIGNED) &&
690                     (!(be32_to_cpu(new_drmem[i].flags) & DRCONF_MEM_ASSIGNED))) {
691                         rc = pseries_remove_memblock(
692                                 be64_to_cpu(old_drmem[i].base_addr),
693                                                      memblock_size);
694                         break;
695                 } else if ((!(be32_to_cpu(old_drmem[i].flags) &
696                             DRCONF_MEM_ASSIGNED)) &&
697                             (be32_to_cpu(new_drmem[i].flags) &
698                             DRCONF_MEM_ASSIGNED)) {
699                         rc = memblock_add(be64_to_cpu(old_drmem[i].base_addr),
700                                           memblock_size);
701                         rc = (rc < 0) ? -EINVAL : 0;
702                         break;
703                 }
704         }
705         return rc;
706 }
707 
708 static int pseries_memory_notifier(struct notifier_block *nb,
709                                    unsigned long action, void *data)
710 {
711         struct of_reconfig_data *rd = data;
712         int err = 0;
713 
714         switch (action) {
715         case OF_RECONFIG_ATTACH_NODE:
716                 err = pseries_add_mem_node(rd->dn);
717                 break;
718         case OF_RECONFIG_DETACH_NODE:
719                 err = pseries_remove_mem_node(rd->dn);
720                 break;
721         case OF_RECONFIG_UPDATE_PROPERTY:
722                 if (!strcmp(rd->prop->name, "ibm,dynamic-memory"))
723                         err = pseries_update_drconf_memory(rd);
724                 break;
725         }
726         return notifier_from_errno(err);
727 }
728 
729 static struct notifier_block pseries_mem_nb = {
730         .notifier_call = pseries_memory_notifier,
731 };
732 
733 static int __init pseries_memory_hotplug_init(void)
734 {
735         if (firmware_has_feature(FW_FEATURE_LPAR))
736                 of_reconfig_notifier_register(&pseries_mem_nb);
737 
738         return 0;
739 }
740 machine_device_initcall(pseries, pseries_memory_hotplug_init);
741 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp