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

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

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

~ [ 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