/* * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is * OK to have direct references to sparsemem variables in here. */ static int memory_block_action(unsigned long phys_index, unsigned long action) { unsigned long start_pfn, start_paddr; unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; struct page *first_page; int ret; first_page = pfn_to_page(phys_index << PFN_SECTION_SHIFT); switch (action) { case MEM_ONLINE: start_pfn = page_to_pfn(first_page); if (!pages_correctly_reserved(start_pfn, nr_pages)) return -EBUSY; ret = online_pages(start_pfn, nr_pages); break; case MEM_OFFLINE: start_paddr = page_to_pfn(first_page) << PAGE_SHIFT; ret = remove_memory(start_paddr, nr_pages << PAGE_SHIFT); break; default: WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: " "%ld\n", __func__, phys_index, action, action); ret = -EINVAL; } return ret; }
static int acpi_memory_disable_device(struct acpi_memory_device *mem_device) { int result; u64 start = mem_device->start_addr; u64 len = mem_device->end_addr - start + 1; unsigned long attr = mem_device->read_write_attribute; ACPI_FUNCTION_TRACE("acpi_memory_disable_device"); /* * Ask the VM to offline this memory range. * Note: Assume that this function returns zero on success */ result = remove_memory(start, len, attr); if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Hot-Remove failed.\n")); return_VALUE(result); } /* Power-off and eject the device */ result = acpi_memory_powerdown_device(mem_device); if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Device Power Down failed.\n")); /* Set the status of the device to invalid */ mem_device->state = MEMORY_INVALID_STATE; return result; } mem_device->state = MEMORY_POWER_OFF_STATE; return result; }
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; start_pfn = base >> PAGE_SHIFT; lock_device_hotplug(); if (!pfn_valid(start_pfn)) goto out; block_sz = pseries_memory_block_size(); sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE; nid = memory_add_physaddr_to_nid(base); for (i = 0; i < sections_per_block; i++) { remove_memory(nid, base, MIN_MEMORY_BLOCK_SIZE); base += MIN_MEMORY_BLOCK_SIZE; } out: /* Update memory regions for memory remove */ memblock_remove(base, memblock_size); unlock_device_hotplug(); return 0; }
static int dlpar_remove_lmb(struct of_drconf_cell *lmb) { struct memory_block *mem_block; unsigned long block_sz; int nid, rc; if (!lmb_is_removable(lmb)) return -EINVAL; mem_block = lmb_to_memblock(lmb); if (!mem_block) return -EINVAL; rc = device_offline(&mem_block->dev); put_device(&mem_block->dev); 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); dlpar_remove_device_tree_lmb(lmb); return 0; }
/* * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is * OK to have direct references to sparsemem variables in here. */ static int memory_block_action(struct memory_block *mem, unsigned long action) { int i; unsigned long psection; unsigned long start_pfn, start_paddr; struct page *first_page; int ret; int old_state = mem->state; psection = mem->phys_index; first_page = pfn_to_page(psection << PFN_SECTION_SHIFT); /* * The probe routines leave the pages reserved, just * as the bootmem code does. Make sure they're still * that way. */ if (action == MEM_ONLINE) { for (i = 0; i < PAGES_PER_SECTION; i++) { if (PageReserved(first_page+i)) continue; printk(KERN_WARNING "section number %ld page number %d " "not reserved, was it already online? \n", psection, i); return -EBUSY; } } switch (action) { case MEM_ONLINE: start_pfn = page_to_pfn(first_page); ret = online_pages(start_pfn, PAGES_PER_SECTION); break; case MEM_OFFLINE: mem->state = MEM_GOING_OFFLINE; start_paddr = page_to_pfn(first_page) << PAGE_SHIFT; ret = remove_memory(start_paddr, PAGES_PER_SECTION << PAGE_SHIFT); if (ret) { mem->state = old_state; break; } break; default: WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n", __func__, mem, action, action); ret = -EINVAL; } return ret; }
static int dlpar_add_lmb_memory(struct of_drconf_cell *lmb) { struct memory_block *mem_block; unsigned long block_sz; int nid, rc; 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); if (rc) return rc; /* Register this block of memory */ rc = memblock_add(lmb->base_addr, block_sz); if (rc) { remove_memory(nid, lmb->base_addr, block_sz); return rc; } mem_block = lmb_to_memblock(lmb); if (!mem_block) { remove_memory(nid, lmb->base_addr, block_sz); return -EINVAL; } rc = device_online(&mem_block->dev); put_device(&mem_block->dev); if (rc) { remove_memory(nid, lmb->base_addr, block_sz); return rc; } lmb->flags |= DRCONF_MEM_ASSIGNED; return 0; }
/* * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is * OK to have direct references to sparsemem variables in here. */ static int memory_block_action(unsigned long phys_index, unsigned long action) { int i; unsigned long start_pfn, start_paddr; unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; struct page *first_page; int ret; first_page = pfn_to_page(phys_index << PFN_SECTION_SHIFT); /* * The probe routines leave the pages reserved, just * as the bootmem code does. Make sure they're still * that way. */ if (action == MEM_ONLINE) { for (i = 0; i < nr_pages; i++) { if (page_is_ram(page_to_pfn(first_page+i))) { if (PageReserved(first_page+i)) continue; printk(KERN_WARNING "section number %ld page number" " %d not reserved, was it " " already online?\n", phys_index, i); return -EBUSY; } } } switch (action) { case MEM_ONLINE: start_pfn = page_to_pfn(first_page); ret = online_pages(start_pfn, nr_pages); break; case MEM_OFFLINE: start_paddr = page_to_pfn(first_page) << PAGE_SHIFT; ret = remove_memory(start_paddr, nr_pages << PAGE_SHIFT); break; default: WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: " "%ld\n", __func__, phys_index, action, action); ret = -EINVAL; } return ret; }
static int dlpar_add_lmb(struct of_drconf_cell *lmb) { unsigned long block_sz; int nid, rc; if (lmb->flags & DRCONF_MEM_ASSIGNED) return -EINVAL; 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; } 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); if (rc) { dlpar_remove_device_tree_lmb(lmb); return rc; } rc = dlpar_online_lmb(lmb); if (rc) { remove_memory(nid, lmb->base_addr, block_sz); dlpar_remove_device_tree_lmb(lmb); } else { lmb->flags |= DRCONF_MEM_ASSIGNED; } return rc; }