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