Skip to content

Commit

Permalink
ACPI / memhotplug: Bind removable memory blocks to ACPI device nodes
Browse files Browse the repository at this point in the history
During ACPI memory hotplug configuration bind memory blocks residing
in modules removable through the standard ACPI mechanism to struct
acpi_device objects associated with ACPI namespace objects
representing those modules.  Accordingly, unbind those memory blocks
from the struct acpi_device objects when the memory modules in
question are being removed.

When "offline" operation for devices representing memory blocks is
introduced, this will allow the ACPI core's device hot-remove code to
use it to carry out remove_memory() for those memory blocks and check
the results of that before it actually removes the modules holding
them from the system.

Since walk_memory_range() is used for accessing all memory blocks
corresponding to a given ACPI namespace object, it is exported from
memory_hotplug.c so that the code in acpi_memhotplug.c can use it.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Vasilis Liaskovitis <vasilis.liaskovitis@profitbricks.com>
Reviewed-by: Toshi Kani <toshi.kani@hp.com>
  • Loading branch information
Rafael J. Wysocki committed May 12, 2013
1 parent ac212b6 commit e2ff394
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 4 deletions.
53 changes: 50 additions & 3 deletions drivers/acpi/acpi_memhotplug.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
*/

#include <linux/acpi.h>
#include <linux/memory.h>
#include <linux/memory_hotplug.h>

#include "internal.h"
Expand Down Expand Up @@ -166,13 +167,50 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
return 0;
}

static unsigned long acpi_meminfo_start_pfn(struct acpi_memory_info *info)
{
return PFN_DOWN(info->start_addr);
}

static unsigned long acpi_meminfo_end_pfn(struct acpi_memory_info *info)
{
return PFN_UP(info->start_addr + info->length-1);
}

static int acpi_bind_memblk(struct memory_block *mem, void *arg)
{
return acpi_bind_one(&mem->dev, (acpi_handle)arg);
}

static int acpi_bind_memory_blocks(struct acpi_memory_info *info,
acpi_handle handle)
{
return walk_memory_range(acpi_meminfo_start_pfn(info),
acpi_meminfo_end_pfn(info), (void *)handle,
acpi_bind_memblk);
}

static int acpi_unbind_memblk(struct memory_block *mem, void *arg)
{
acpi_unbind_one(&mem->dev);
return 0;
}

static void acpi_unbind_memory_blocks(struct acpi_memory_info *info,
acpi_handle handle)
{
walk_memory_range(acpi_meminfo_start_pfn(info),
acpi_meminfo_end_pfn(info), NULL, acpi_unbind_memblk);
}

static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
{
acpi_handle handle = mem_device->device->handle;
int result, num_enabled = 0;
struct acpi_memory_info *info;
int node;

node = acpi_get_node(mem_device->device->handle);
node = acpi_get_node(handle);
/*
* Tell the VM there is more memory here...
* Note: Assume that this function returns zero on success
Expand Down Expand Up @@ -203,6 +241,12 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
if (result && result != -EEXIST)
continue;

result = acpi_bind_memory_blocks(info, handle);
if (result) {
acpi_unbind_memory_blocks(info, handle);
return -ENODEV;
}

info->enabled = 1;

/*
Expand All @@ -229,17 +273,20 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)

static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
{
acpi_handle handle = mem_device->device->handle;
int result = 0, nid;
struct acpi_memory_info *info, *n;

nid = acpi_get_node(mem_device->device->handle);
nid = acpi_get_node(handle);

list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
if (!info->enabled)
continue;

if (nid < 0)
nid = memory_add_physaddr_to_nid(info->start_addr);

acpi_unbind_memory_blocks(info, handle);
result = remove_memory(nid, info->start_addr, info->length);
if (result)
return result;
Expand Down Expand Up @@ -300,7 +347,7 @@ static int acpi_memory_device_add(struct acpi_device *device,
if (result) {
dev_err(&device->dev, "acpi_memory_enable_device() error\n");
acpi_memory_device_free(mem_device);
return -ENODEV;
return result;
}

dev_dbg(&device->dev, "Memory device configured by ACPI\n");
Expand Down
2 changes: 2 additions & 0 deletions include/linux/memory_hotplug.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ static inline int is_mem_section_removable(unsigned long pfn,
static inline void try_offline_node(int nid) {}
#endif /* CONFIG_MEMORY_HOTREMOVE */

extern int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn,
void *arg, int (*func)(struct memory_block *, void *));
extern int mem_online_node(int nid);
extern int add_memory(int nid, u64 start, u64 size);
extern int arch_add_memory(int nid, u64 start, u64 size);
Expand Down
4 changes: 3 additions & 1 deletion mm/memory_hotplug.c
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
{
return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ);
}
#endif /* CONFIG_MEMORY_HOTREMOVE */

/**
* walk_memory_range - walks through all mem sections in [start_pfn, end_pfn)
Expand All @@ -1631,7 +1632,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
*
* Returns the return value of func.
*/
static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn,
int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn,
void *arg, int (*func)(struct memory_block *, void *))
{
struct memory_block *mem = NULL;
Expand Down Expand Up @@ -1668,6 +1669,7 @@ static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn,
return 0;
}

#ifdef CONFIG_MEMORY_HOTREMOVE
/**
* offline_memory_block_cb - callback function for offlining memory block
* @mem: the memory block to be offlined
Expand Down

0 comments on commit e2ff394

Please sign in to comment.