ACPI / memhotplug: Bind removable memory blocks to ACPI device nodes
[linux-2.6-block.git] / drivers / acpi / acpi_memhotplug.c
index 5e6301e949206adb9f9b512a4b44b9d093e588df..5590db12028efbf979d248a8ed0d3bb6333c2191 100644 (file)
@@ -28,6 +28,7 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/memory.h>
 #include <linux/memory_hotplug.h>
 
 #include "internal.h"
@@ -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
@@ -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;
 
                /*
@@ -229,10 +273,11 @@ 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)
@@ -240,6 +285,8 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
 
                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;
@@ -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");