hotplug-memory.c 23.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * pseries Memory Hotplug infrastructure.
 *
 * Copyright (C) 2008 Badari Pulavarty, IBM Corporation
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */

12 13
#define pr_fmt(fmt)	"pseries-hotplug-mem: " fmt

14
#include <linux/of.h>
R
Rob Herring 已提交
15
#include <linux/of_address.h>
Y
Yinghai Lu 已提交
16
#include <linux/memblock.h>
17
#include <linux/memory.h>
18
#include <linux/memory_hotplug.h>
19
#include <linux/slab.h>
20

21 22
#include <asm/firmware.h>
#include <asm/machdep.h>
R
Rob Herring 已提交
23
#include <asm/prom.h>
24
#include <asm/sparsemem.h>
25
#include <asm/fadump.h>
26
#include <asm/drmem.h>
27
#include "pseries.h"
28

29 30
static bool rtas_hp_event;

31
unsigned long pseries_memory_block_size(void)
32 33
{
	struct device_node *np;
34 35
	unsigned int memblock_size = MIN_MEMORY_BLOCK_SIZE;
	struct resource r;
36 37 38

	np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
	if (np) {
39
		const __be64 *size;
40 41

		size = of_get_property(np, "ibm,lmb-size", NULL);
42 43
		if (size)
			memblock_size = be64_to_cpup(size);
44
		of_node_put(np);
45 46
	} else  if (machine_is(pseries)) {
		/* This fallback really only applies to pseries */
47 48 49 50
		unsigned int memzero_size = 0;

		np = of_find_node_by_path("/memory@0");
		if (np) {
51 52
			if (!of_address_to_resource(np, 0, &r))
				memzero_size = resource_size(&r);
53 54 55 56 57 58 59 60 61 62 63 64
			of_node_put(np);
		}

		if (memzero_size) {
			/* We now know the size of memory@0, use this to find
			 * the first memoryblock and get its size.
			 */
			char buf[64];

			sprintf(buf, "/memory@%x", memzero_size);
			np = of_find_node_by_path(buf);
			if (np) {
65 66
				if (!of_address_to_resource(np, 0, &r))
					memblock_size = resource_size(&r);
67 68 69 70 71 72 73
				of_node_put(np);
			}
		}
	}
	return memblock_size;
}

74
static void dlpar_free_property(struct property *prop)
75 76 77 78 79 80
{
	kfree(prop->name);
	kfree(prop->value);
	kfree(prop);
}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
static struct property *dlpar_clone_property(struct property *prop,
					     u32 prop_size)
{
	struct property *new_prop;

	new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
	if (!new_prop)
		return NULL;

	new_prop->name = kstrdup(prop->name, GFP_KERNEL);
	new_prop->value = kzalloc(prop_size, GFP_KERNEL);
	if (!new_prop->name || !new_prop->value) {
		dlpar_free_property(new_prop);
		return NULL;
	}

	memcpy(new_prop->value, prop->value, prop->length);
	new_prop->length = prop_size;

	of_property_set_flag(new_prop, OF_DYNAMIC);
	return new_prop;
}

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
static u32 find_aa_index(struct device_node *dr_node,
			 struct property *ala_prop, const u32 *lmb_assoc)
{
	u32 *assoc_arrays;
	u32 aa_index;
	int aa_arrays, aa_array_entries, aa_array_sz;
	int i, index;

	/*
	 * The ibm,associativity-lookup-arrays property is defined to be
	 * a 32-bit value specifying the number of associativity arrays
	 * followed by a 32-bitvalue specifying the number of entries per
	 * array, followed by the associativity arrays.
	 */
	assoc_arrays = ala_prop->value;

	aa_arrays = be32_to_cpu(assoc_arrays[0]);
	aa_array_entries = be32_to_cpu(assoc_arrays[1]);
	aa_array_sz = aa_array_entries * sizeof(u32);

	aa_index = -1;
	for (i = 0; i < aa_arrays; i++) {
		index = (i * aa_array_entries) + 2;

		if (memcmp(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz))
			continue;

		aa_index = i;
		break;
	}

	if (aa_index == -1) {
		struct property *new_prop;
		u32 new_prop_size;

		new_prop_size = ala_prop->length + aa_array_sz;
		new_prop = dlpar_clone_property(ala_prop, new_prop_size);
		if (!new_prop)
			return -1;

		assoc_arrays = new_prop->value;

		/* increment the number of entries in the lookup array */
		assoc_arrays[0] = cpu_to_be32(aa_arrays + 1);

		/* copy the new associativity into the lookup array */
		index = aa_arrays * aa_array_entries + 2;
		memcpy(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz);

		of_update_property(dr_node, new_prop);

		/*
		 * The associativity lookup array index for this lmb is
		 * number of entries - 1 since we added its associativity
		 * to the end of the lookup array.
		 */
		aa_index = be32_to_cpu(assoc_arrays[0]) - 1;
	}

	return aa_index;
}

166
static u32 lookup_lmb_associativity_index(struct drmem_lmb *lmb)
167 168
{
	struct device_node *parent, *lmb_node, *dr_node;
169
	struct property *ala_prop;
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
	const u32 *lmb_assoc;
	u32 aa_index;

	parent = of_find_node_by_path("/");
	if (!parent)
		return -ENODEV;

	lmb_node = dlpar_configure_connector(cpu_to_be32(lmb->drc_index),
					     parent);
	of_node_put(parent);
	if (!lmb_node)
		return -EINVAL;

	lmb_assoc = of_get_property(lmb_node, "ibm,associativity", NULL);
	if (!lmb_assoc) {
		dlpar_free_cc_nodes(lmb_node);
		return -ENODEV;
	}

	dr_node = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
	if (!dr_node) {
		dlpar_free_cc_nodes(lmb_node);
		return -ENODEV;
	}

195 196 197 198
	ala_prop = of_find_property(dr_node, "ibm,associativity-lookup-arrays",
				    NULL);
	if (!ala_prop) {
		of_node_put(dr_node);
199 200 201 202
		dlpar_free_cc_nodes(lmb_node);
		return -ENODEV;
	}

203
	aa_index = find_aa_index(dr_node, ala_prop, lmb_assoc);
204 205 206 207 208

	dlpar_free_cc_nodes(lmb_node);
	return aa_index;
}

209
static int dlpar_add_device_tree_lmb(struct drmem_lmb *lmb)
210
{
211
	int rc, aa_index;
212 213 214 215 216 217 218 219 220 221 222

	lmb->flags |= DRCONF_MEM_ASSIGNED;

	aa_index = lookup_lmb_associativity_index(lmb);
	if (aa_index < 0) {
		pr_err("Couldn't find associativity index for drc index %x\n",
		       lmb->drc_index);
		return aa_index;
	}

	lmb->aa_index = aa_index;
223 224 225 226 227 228

	rtas_hp_event = true;
	rc = drmem_update_dt();
	rtas_hp_event = false;

	return rc;
229 230
}

231
static int dlpar_remove_device_tree_lmb(struct drmem_lmb *lmb)
232
{
233 234
	int rc;

235 236
	lmb->flags &= ~DRCONF_MEM_ASSIGNED;
	lmb->aa_index = 0xffffffff;
237 238 239 240 241 242

	rtas_hp_event = true;
	rc = drmem_update_dt();
	rtas_hp_event = false;

	return rc;
243 244
}

245
static struct memory_block *lmb_to_memblock(struct drmem_lmb *lmb)
246 247 248 249 250 251 252 253 254 255 256 257
{
	unsigned long section_nr;
	struct mem_section *mem_sect;
	struct memory_block *mem_block;

	section_nr = pfn_to_section_nr(PFN_DOWN(lmb->base_addr));
	mem_sect = __nr_to_section(section_nr);

	mem_block = find_memory_block(mem_sect);
	return mem_block;
}

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
static int get_lmb_range(u32 drc_index, int n_lmbs,
			 struct drmem_lmb **start_lmb,
			 struct drmem_lmb **end_lmb)
{
	struct drmem_lmb *lmb, *start, *end;
	struct drmem_lmb *last_lmb;

	start = NULL;
	for_each_drmem_lmb(lmb) {
		if (lmb->drc_index == drc_index) {
			start = lmb;
			break;
		}
	}

	if (!start)
		return -EINVAL;

	end = &start[n_lmbs - 1];

	last_lmb = &drmem_info->lmbs[drmem_info->n_lmbs - 1];
	if (end > last_lmb)
		return -EINVAL;

	*start_lmb = start;
	*end_lmb = end;
	return 0;
}

static int dlpar_change_lmb_state(struct drmem_lmb *lmb, bool online)
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
{
	struct memory_block *mem_block;
	int rc;

	mem_block = lmb_to_memblock(lmb);
	if (!mem_block)
		return -EINVAL;

	if (online && mem_block->dev.offline)
		rc = device_online(&mem_block->dev);
	else if (!online && !mem_block->dev.offline)
		rc = device_offline(&mem_block->dev);
	else
		rc = 0;

	put_device(&mem_block->dev);

	return rc;
}

308
static int dlpar_online_lmb(struct drmem_lmb *lmb)
309 310 311 312
{
	return dlpar_change_lmb_state(lmb, true);
}

313
#ifdef CONFIG_MEMORY_HOTREMOVE
314
static int dlpar_offline_lmb(struct drmem_lmb *lmb)
315 316 317 318
{
	return dlpar_change_lmb_state(lmb, false);
}

319 320 321 322 323
static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size)
{
	unsigned long block_sz, start_pfn;
	int sections_per_block;
	int i, nid;
324

325
	start_pfn = base >> PAGE_SHIFT;
326

327 328 329 330
	lock_device_hotplug();

	if (!pfn_valid(start_pfn))
		goto out;
331

332
	block_sz = pseries_memory_block_size();
333 334
	sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
	nid = memory_add_physaddr_to_nid(base);
335

336 337 338
	for (i = 0; i < sections_per_block; i++) {
		remove_memory(nid, base, MIN_MEMORY_BLOCK_SIZE);
		base += MIN_MEMORY_BLOCK_SIZE;
339
	}
340

341
out:
342
	/* Update memory regions for memory remove */
Y
Yinghai Lu 已提交
343
	memblock_remove(base, memblock_size);
344
	unlock_device_hotplug();
345
	return 0;
346 347
}

348
static int pseries_remove_mem_node(struct device_node *np)
349 350
{
	const char *type;
351
	const __be32 *regs;
352
	unsigned long base;
353
	unsigned int lmb_size;
354 355 356 357 358 359 360 361 362 363
	int ret = -EINVAL;

	/*
	 * Check to see if we are actually removing memory
	 */
	type = of_get_property(np, "device_type", NULL);
	if (type == NULL || strcmp(type, "memory") != 0)
		return 0;

	/*
364
	 * Find the base address and size of the memblock
365 366 367 368 369
	 */
	regs = of_get_property(np, "reg", NULL);
	if (!regs)
		return ret;

370 371
	base = be64_to_cpu(*(unsigned long *)regs);
	lmb_size = be32_to_cpu(regs[3]);
372

373 374
	pseries_remove_memblock(base, lmb_size);
	return 0;
375
}
376

377
static bool lmb_is_removable(struct drmem_lmb *lmb)
378 379 380 381 382 383 384 385 386 387 388 389 390
{
	int i, scns_per_block;
	int rc = 1;
	unsigned long pfn, block_sz;
	u64 phys_addr;

	if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
		return false;

	block_sz = memory_block_size_bytes();
	scns_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
	phys_addr = lmb->base_addr;

391 392 393 394 395 396
#ifdef CONFIG_FA_DUMP
	/* Don't hot-remove memory that falls in fadump boot memory area */
	if (is_fadump_boot_memory_area(phys_addr, block_sz))
		return false;
#endif

397 398 399 400 401 402 403 404 405 406 407 408
	for (i = 0; i < scns_per_block; i++) {
		pfn = PFN_DOWN(phys_addr);
		if (!pfn_present(pfn))
			continue;

		rc &= is_mem_section_removable(pfn, PAGES_PER_SECTION);
		phys_addr += MIN_MEMORY_BLOCK_SIZE;
	}

	return rc ? true : false;
}

409
static int dlpar_add_lmb(struct drmem_lmb *);
410

411
static int dlpar_remove_lmb(struct drmem_lmb *lmb)
412 413 414 415 416 417 418
{
	unsigned long block_sz;
	int nid, rc;

	if (!lmb_is_removable(lmb))
		return -EINVAL;

419
	rc = dlpar_offline_lmb(lmb);
420 421 422 423 424 425 426 427 428 429 430
	if (rc)
		return rc;

	block_sz = pseries_memory_block_size();
	nid = memory_add_physaddr_to_nid(lmb->base_addr);

	remove_memory(nid, lmb->base_addr, block_sz);

	/* Update memory regions for memory remove */
	memblock_remove(lmb->base_addr, block_sz);

431
	dlpar_remove_device_tree_lmb(lmb);
432 433 434
	return 0;
}

435
static int dlpar_memory_remove_by_count(u32 lmbs_to_remove)
436
{
437
	struct drmem_lmb *lmb;
438 439
	int lmbs_removed = 0;
	int lmbs_available = 0;
440
	int rc;
441 442 443 444 445 446 447

	pr_info("Attempting to hot-remove %d LMB(s)\n", lmbs_to_remove);

	if (lmbs_to_remove == 0)
		return -EINVAL;

	/* Validate that there are enough LMBs to satisfy the request */
448 449
	for_each_drmem_lmb(lmb) {
		if (lmb_is_removable(lmb))
450
			lmbs_available++;
451 452 453

		if (lmbs_available == lmbs_to_remove)
			break;
454 455
	}

456 457 458
	if (lmbs_available < lmbs_to_remove) {
		pr_info("Not enough LMBs available (%d of %d) to satisfy request\n",
			lmbs_available, lmbs_to_remove);
459
		return -EINVAL;
460
	}
461

462 463
	for_each_drmem_lmb(lmb) {
		rc = dlpar_remove_lmb(lmb);
464 465 466 467 468 469
		if (rc)
			continue;

		/* Mark this lmb so we can add it later if all of the
		 * requested LMBs cannot be removed.
		 */
470 471 472 473 474
		drmem_mark_lmb_reserved(lmb);

		lmbs_removed++;
		if (lmbs_removed == lmbs_to_remove)
			break;
475 476 477 478 479
	}

	if (lmbs_removed != lmbs_to_remove) {
		pr_err("Memory hot-remove failed, adding LMB's back\n");

480 481
		for_each_drmem_lmb(lmb) {
			if (!drmem_lmb_reserved(lmb))
482 483
				continue;

484
			rc = dlpar_add_lmb(lmb);
485 486
			if (rc)
				pr_err("Failed to add LMB back, drc index %x\n",
487
				       lmb->drc_index);
488

489
			drmem_remove_lmb_reservation(lmb);
490 491 492 493
		}

		rc = -EINVAL;
	} else {
494 495
		for_each_drmem_lmb(lmb) {
			if (!drmem_lmb_reserved(lmb))
496 497
				continue;

498
			dlpar_release_drc(lmb->drc_index);
499
			pr_info("Memory at %llx was hot-removed\n",
500
				lmb->base_addr);
501

502
			drmem_remove_lmb_reservation(lmb);
503 504 505 506 507 508 509
		}
		rc = 0;
	}

	return rc;
}

510
static int dlpar_memory_remove_by_index(u32 drc_index)
511
{
512
	struct drmem_lmb *lmb;
513
	int lmb_found;
514
	int rc;
515 516 517 518

	pr_info("Attempting to hot-remove LMB, drc index %x\n", drc_index);

	lmb_found = 0;
519 520
	for_each_drmem_lmb(lmb) {
		if (lmb->drc_index == drc_index) {
521
			lmb_found = 1;
522
			rc = dlpar_remove_lmb(lmb);
523
			if (!rc)
524
				dlpar_release_drc(lmb->drc_index);
525

526 527 528 529 530 531 532 533 534
			break;
		}
	}

	if (!lmb_found)
		rc = -EINVAL;

	if (rc)
		pr_info("Failed to hot-remove memory at %llx\n",
535
			lmb->base_addr);
536
	else
537
		pr_info("Memory at %llx was hot-removed\n", lmb->base_addr);
538 539 540 541

	return rc;
}

542
static int dlpar_memory_readd_by_index(u32 drc_index)
543
{
544
	struct drmem_lmb *lmb;
545
	int lmb_found;
546
	int rc;
547 548 549 550

	pr_info("Attempting to update LMB, drc index %x\n", drc_index);

	lmb_found = 0;
551 552
	for_each_drmem_lmb(lmb) {
		if (lmb->drc_index == drc_index) {
553
			lmb_found = 1;
554
			rc = dlpar_remove_lmb(lmb);
555
			if (!rc) {
556
				rc = dlpar_add_lmb(lmb);
557
				if (rc)
558
					dlpar_release_drc(lmb->drc_index);
559 560 561 562 563 564 565 566 567 568
			}
			break;
		}
	}

	if (!lmb_found)
		rc = -EINVAL;

	if (rc)
		pr_info("Failed to update memory at %llx\n",
569
			lmb->base_addr);
570
	else
571
		pr_info("Memory at %llx was updated\n", lmb->base_addr);
572 573 574

	return rc;
}
575

576
static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index)
577
{
578 579 580
	struct drmem_lmb *lmb, *start_lmb, *end_lmb;
	int lmbs_available = 0;
	int rc;
581 582 583 584 585 586 587

	pr_info("Attempting to hot-remove %u LMB(s) at %x\n",
		lmbs_to_remove, drc_index);

	if (lmbs_to_remove == 0)
		return -EINVAL;

588 589
	rc = get_lmb_range(drc_index, lmbs_to_remove, &start_lmb, &end_lmb);
	if (rc)
590 591 592
		return -EINVAL;

	/* Validate that there are enough LMBs to satisfy the request */
593 594
	for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
		if (lmb->flags & DRCONF_MEM_RESERVED)
595 596 597 598 599 600 601 602
			break;

		lmbs_available++;
	}

	if (lmbs_available < lmbs_to_remove)
		return -EINVAL;

603 604
	for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
		if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
605 606
			continue;

607
		rc = dlpar_remove_lmb(lmb);
608 609 610
		if (rc)
			break;

611
		drmem_mark_lmb_reserved(lmb);
612 613 614 615 616
	}

	if (rc) {
		pr_err("Memory indexed-count-remove failed, adding any removed LMBs\n");

617 618 619

		for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
			if (!drmem_lmb_reserved(lmb))
620 621
				continue;

622
			rc = dlpar_add_lmb(lmb);
623 624
			if (rc)
				pr_err("Failed to add LMB, drc index %x\n",
625
				       lmb->drc_index);
626

627
			drmem_remove_lmb_reservation(lmb);
628 629 630
		}
		rc = -EINVAL;
	} else {
631 632
		for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
			if (!drmem_lmb_reserved(lmb))
633 634
				continue;

635
			dlpar_release_drc(lmb->drc_index);
636
			pr_info("Memory at %llx (drc index %x) was hot-removed\n",
637
				lmb->base_addr, lmb->drc_index);
638

639
			drmem_remove_lmb_reservation(lmb);
640 641 642 643 644 645
		}
	}

	return rc;
}

646 647 648 649 650 651
#else
static inline int pseries_remove_memblock(unsigned long base,
					  unsigned int memblock_size)
{
	return -EOPNOTSUPP;
}
652
static inline int pseries_remove_mem_node(struct device_node *np)
653
{
654
	return 0;
655
}
656 657 658 659
static inline int dlpar_memory_remove(struct pseries_hp_errorlog *hp_elog)
{
	return -EOPNOTSUPP;
}
660
static int dlpar_remove_lmb(struct drmem_lmb *lmb)
661 662 663
{
	return -EOPNOTSUPP;
}
664
static int dlpar_memory_remove_by_count(u32 lmbs_to_remove)
665 666 667
{
	return -EOPNOTSUPP;
}
668
static int dlpar_memory_remove_by_index(u32 drc_index)
669 670 671
{
	return -EOPNOTSUPP;
}
672
static int dlpar_memory_readd_by_index(u32 drc_index)
673 674 675
{
	return -EOPNOTSUPP;
}
676

677
static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index)
678 679 680
{
	return -EOPNOTSUPP;
}
681
#endif /* CONFIG_MEMORY_HOTREMOVE */
682

683
static int dlpar_add_lmb(struct drmem_lmb *lmb)
684 685 686 687
{
	unsigned long block_sz;
	int nid, rc;

688 689 690
	if (lmb->flags & DRCONF_MEM_ASSIGNED)
		return -EINVAL;

691 692 693 694 695 696 697 698
	rc = dlpar_add_device_tree_lmb(lmb);
	if (rc) {
		pr_err("Couldn't update device tree for drc index %x\n",
		       lmb->drc_index);
		dlpar_release_drc(lmb->drc_index);
		return rc;
	}

699 700 701 702 703 704 705
	block_sz = memory_block_size_bytes();

	/* Find the node id for this address */
	nid = memory_add_physaddr_to_nid(lmb->base_addr);

	/* Add the memory */
	rc = add_memory(nid, lmb->base_addr, block_sz);
706
	if (rc) {
707
		dlpar_remove_device_tree_lmb(lmb);
708 709 710 711 712 713 714 715
		return rc;
	}

	rc = dlpar_online_lmb(lmb);
	if (rc) {
		remove_memory(nid, lmb->base_addr, block_sz);
		dlpar_remove_device_tree_lmb(lmb);
	} else {
716
		lmb->flags |= DRCONF_MEM_ASSIGNED;
717
	}
718 719 720 721

	return rc;
}

722
static int dlpar_memory_add_by_count(u32 lmbs_to_add)
723
{
724
	struct drmem_lmb *lmb;
725 726
	int lmbs_available = 0;
	int lmbs_added = 0;
727
	int rc;
728 729 730 731 732 733 734

	pr_info("Attempting to hot-add %d LMB(s)\n", lmbs_to_add);

	if (lmbs_to_add == 0)
		return -EINVAL;

	/* Validate that there are enough LMBs to satisfy the request */
735 736
	for_each_drmem_lmb(lmb) {
		if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
737
			lmbs_available++;
738 739 740

		if (lmbs_available == lmbs_to_add)
			break;
741 742 743 744 745
	}

	if (lmbs_available < lmbs_to_add)
		return -EINVAL;

746 747
	for_each_drmem_lmb(lmb) {
		if (lmb->flags & DRCONF_MEM_ASSIGNED)
748 749
			continue;

750
		rc = dlpar_acquire_drc(lmb->drc_index);
751 752 753
		if (rc)
			continue;

754
		rc = dlpar_add_lmb(lmb);
755
		if (rc) {
756
			dlpar_release_drc(lmb->drc_index);
757 758 759
			continue;
		}

760 761 762
		/* Mark this lmb so we can remove it later if all of the
		 * requested LMBs cannot be added.
		 */
763 764 765 766 767
		drmem_mark_lmb_reserved(lmb);

		lmbs_added++;
		if (lmbs_added == lmbs_to_add)
			break;
768 769 770
	}

	if (lmbs_added != lmbs_to_add) {
771 772
		pr_err("Memory hot-add failed, removing any added LMBs\n");

773 774
		for_each_drmem_lmb(lmb) {
			if (!drmem_lmb_reserved(lmb))
775 776
				continue;

777
			rc = dlpar_remove_lmb(lmb);
778 779
			if (rc)
				pr_err("Failed to remove LMB, drc index %x\n",
780
				       lmb->drc_index);
781
			else
782 783 784
				dlpar_release_drc(lmb->drc_index);

			drmem_remove_lmb_reservation(lmb);
785
		}
786 787
		rc = -EINVAL;
	} else {
788 789
		for_each_drmem_lmb(lmb) {
			if (!drmem_lmb_reserved(lmb))
790 791 792
				continue;

			pr_info("Memory at %llx (drc index %x) was hot-added\n",
793 794
				lmb->base_addr, lmb->drc_index);
			drmem_remove_lmb_reservation(lmb);
795
		}
796
		rc = 0;
797 798 799 800 801
	}

	return rc;
}

802
static int dlpar_memory_add_by_index(u32 drc_index)
803
{
804 805
	struct drmem_lmb *lmb;
	int rc, lmb_found;
806 807 808 809

	pr_info("Attempting to hot-add LMB, drc index %x\n", drc_index);

	lmb_found = 0;
810 811
	for_each_drmem_lmb(lmb) {
		if (lmb->drc_index == drc_index) {
812
			lmb_found = 1;
813
			rc = dlpar_acquire_drc(lmb->drc_index);
814
			if (!rc) {
815
				rc = dlpar_add_lmb(lmb);
816
				if (rc)
817
					dlpar_release_drc(lmb->drc_index);
818 819
			}

820 821 822 823 824 825 826 827 828 829 830
			break;
		}
	}

	if (!lmb_found)
		rc = -EINVAL;

	if (rc)
		pr_info("Failed to hot-add memory, drc index %x\n", drc_index);
	else
		pr_info("Memory at %llx (drc index %x) was hot-added\n",
831
			lmb->base_addr, drc_index);
832 833 834 835

	return rc;
}

836
static int dlpar_memory_add_by_ic(u32 lmbs_to_add, u32 drc_index)
837
{
838 839 840
	struct drmem_lmb *lmb, *start_lmb, *end_lmb;
	int lmbs_available = 0;
	int rc;
841 842 843 844 845 846 847

	pr_info("Attempting to hot-add %u LMB(s) at index %x\n",
		lmbs_to_add, drc_index);

	if (lmbs_to_add == 0)
		return -EINVAL;

848 849
	rc = get_lmb_range(drc_index, lmbs_to_add, &start_lmb, &end_lmb);
	if (rc)
850 851 852
		return -EINVAL;

	/* Validate that the LMBs in this range are not reserved */
853 854
	for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
		if (lmb->flags & DRCONF_MEM_RESERVED)
855 856 857 858 859 860 861 862
			break;

		lmbs_available++;
	}

	if (lmbs_available < lmbs_to_add)
		return -EINVAL;

863 864
	for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
		if (lmb->flags & DRCONF_MEM_ASSIGNED)
865 866
			continue;

867
		rc = dlpar_acquire_drc(lmb->drc_index);
868 869 870
		if (rc)
			break;

871
		rc = dlpar_add_lmb(lmb);
872
		if (rc) {
873
			dlpar_release_drc(lmb->drc_index);
874 875 876
			break;
		}

877
		drmem_mark_lmb_reserved(lmb);
878 879 880 881 882
	}

	if (rc) {
		pr_err("Memory indexed-count-add failed, removing any added LMBs\n");

883 884
		for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
			if (!drmem_lmb_reserved(lmb))
885 886
				continue;

887
			rc = dlpar_remove_lmb(lmb);
888 889
			if (rc)
				pr_err("Failed to remove LMB, drc index %x\n",
890
				       lmb->drc_index);
891
			else
892 893 894
				dlpar_release_drc(lmb->drc_index);

			drmem_remove_lmb_reservation(lmb);
895 896 897
		}
		rc = -EINVAL;
	} else {
898 899
		for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
			if (!drmem_lmb_reserved(lmb))
900 901 902
				continue;

			pr_info("Memory at %llx (drc index %x) was hot-added\n",
903 904
				lmb->base_addr, lmb->drc_index);
			drmem_remove_lmb_reservation(lmb);
905 906 907 908 909 910
		}
	}

	return rc;
}

911 912
int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
{
913 914 915
	u32 count, drc_index;
	int rc;

916 917 918
	lock_device_hotplug();

	switch (hp_elog->action) {
919
	case PSERIES_HP_ELOG_ACTION_ADD:
920 921
		if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) {
			count = hp_elog->_drc_u.drc_count;
922
			rc = dlpar_memory_add_by_count(count);
923 924
		} else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) {
			drc_index = hp_elog->_drc_u.drc_index;
925
			rc = dlpar_memory_add_by_index(drc_index);
926 927 928
		} else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) {
			count = hp_elog->_drc_u.ic.count;
			drc_index = hp_elog->_drc_u.ic.index;
929
			rc = dlpar_memory_add_by_ic(count, drc_index);
930
		} else {
931
			rc = -EINVAL;
932 933
		}

934
		break;
935
	case PSERIES_HP_ELOG_ACTION_REMOVE:
936 937
		if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) {
			count = hp_elog->_drc_u.drc_count;
938
			rc = dlpar_memory_remove_by_count(count);
939 940
		} else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) {
			drc_index = hp_elog->_drc_u.drc_index;
941
			rc = dlpar_memory_remove_by_index(drc_index);
942 943 944
		} else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) {
			count = hp_elog->_drc_u.ic.count;
			drc_index = hp_elog->_drc_u.ic.index;
945
			rc = dlpar_memory_remove_by_ic(count, drc_index);
946
		} else {
947
			rc = -EINVAL;
948 949
		}

950
		break;
951
	case PSERIES_HP_ELOG_ACTION_READD:
952
		drc_index = hp_elog->_drc_u.drc_index;
953
		rc = dlpar_memory_readd_by_index(drc_index);
954
		break;
955 956 957 958 959 960 961 962 963 964
	default:
		pr_err("Invalid action (%d) specified\n", hp_elog->action);
		rc = -EINVAL;
		break;
	}

	unlock_device_hotplug();
	return rc;
}

965
static int pseries_add_mem_node(struct device_node *np)
966 967
{
	const char *type;
968
	const __be32 *regs;
969
	unsigned long base;
970
	unsigned int lmb_size;
971 972 973 974 975 976 977 978 979 980
	int ret = -EINVAL;

	/*
	 * Check to see if we are actually adding memory
	 */
	type = of_get_property(np, "device_type", NULL);
	if (type == NULL || strcmp(type, "memory") != 0)
		return 0;

	/*
Y
Yinghai Lu 已提交
981
	 * Find the base and size of the memblock
982 983 984 985 986
	 */
	regs = of_get_property(np, "reg", NULL);
	if (!regs)
		return ret;

987 988
	base = be64_to_cpu(*(unsigned long *)regs);
	lmb_size = be32_to_cpu(regs[3]);
989 990 991 992

	/*
	 * Update memory region to represent the memory add
	 */
993
	ret = memblock_add(base, lmb_size);
994 995 996
	return (ret < 0) ? -EINVAL : 0;
}

997
static int pseries_update_drconf_memory(struct of_reconfig_data *pr)
998
{
999
	struct of_drconf_cell_v1 *new_drmem, *old_drmem;
1000
	unsigned long memblock_size;
1001
	u32 entries;
1002
	__be32 *p;
1003
	int i, rc = -EINVAL;
1004

1005 1006 1007
	if (rtas_hp_event)
		return 0;

1008
	memblock_size = pseries_memory_block_size();
1009
	if (!memblock_size)
1010 1011
		return -EINVAL;

1012
	p = (__be32 *) pr->old_prop->value;
1013 1014 1015 1016 1017
	if (!p)
		return -EINVAL;

	/* The first int of the property is the number of lmb's described
	 * by the property. This is followed by an array of of_drconf_cell
1018
	 * entries. Get the number of entries and skip to the array of
1019 1020
	 * of_drconf_cell's.
	 */
1021
	entries = be32_to_cpu(*p++);
1022
	old_drmem = (struct of_drconf_cell_v1 *)p;
1023

1024
	p = (__be32 *)pr->prop->value;
1025
	p++;
1026
	new_drmem = (struct of_drconf_cell_v1 *)p;
1027 1028

	for (i = 0; i < entries; i++) {
1029 1030 1031 1032
		if ((be32_to_cpu(old_drmem[i].flags) & DRCONF_MEM_ASSIGNED) &&
		    (!(be32_to_cpu(new_drmem[i].flags) & DRCONF_MEM_ASSIGNED))) {
			rc = pseries_remove_memblock(
				be64_to_cpu(old_drmem[i].base_addr),
1033 1034
						     memblock_size);
			break;
1035 1036 1037 1038 1039
		} else if ((!(be32_to_cpu(old_drmem[i].flags) &
			    DRCONF_MEM_ASSIGNED)) &&
			    (be32_to_cpu(new_drmem[i].flags) &
			    DRCONF_MEM_ASSIGNED)) {
			rc = memblock_add(be64_to_cpu(old_drmem[i].base_addr),
1040 1041 1042 1043
					  memblock_size);
			rc = (rc < 0) ? -EINVAL : 0;
			break;
		}
1044 1045
	}
	return rc;
1046 1047
}

1048
static int pseries_memory_notifier(struct notifier_block *nb,
1049
				   unsigned long action, void *data)
1050
{
1051
	struct of_reconfig_data *rd = data;
1052
	int err = 0;
1053 1054

	switch (action) {
1055
	case OF_RECONFIG_ATTACH_NODE:
1056
		err = pseries_add_mem_node(rd->dn);
1057
		break;
1058
	case OF_RECONFIG_DETACH_NODE:
1059
		err = pseries_remove_mem_node(rd->dn);
1060
		break;
1061
	case OF_RECONFIG_UPDATE_PROPERTY:
1062 1063
		if (!strcmp(rd->prop->name, "ibm,dynamic-memory"))
			err = pseries_update_drconf_memory(rd);
1064 1065
		break;
	}
1066
	return notifier_from_errno(err);
1067 1068 1069 1070 1071 1072 1073 1074 1075
}

static struct notifier_block pseries_mem_nb = {
	.notifier_call = pseries_memory_notifier,
};

static int __init pseries_memory_hotplug_init(void)
{
	if (firmware_has_feature(FW_FEATURE_LPAR))
1076
		of_reconfig_notifier_register(&pseries_mem_nb);
1077 1078 1079 1080

	return 0;
}
machine_device_initcall(pseries, pseries_memory_hotplug_init);