Merge tag 'stable/for-linus-3.7-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / drivers / base / memory.c
index 7dda4f790f00d58145cc3b58ce4607c42ea829b1..86c88216a503197a84ef22004bf4949adec98d92 100644 (file)
@@ -248,26 +248,23 @@ static bool pages_correctly_reserved(unsigned long start_pfn,
 static int
 memory_block_action(unsigned long phys_index, unsigned long action)
 {
-       unsigned long start_pfn, start_paddr;
+       unsigned long start_pfn;
        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);
+       start_pfn = page_to_pfn(first_page);
 
        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);
+                       ret = offline_pages(start_pfn, nr_pages);
                        break;
                default:
                        WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: "
@@ -278,13 +275,11 @@ memory_block_action(unsigned long phys_index, unsigned long action)
        return ret;
 }
 
-static int memory_block_change_state(struct memory_block *mem,
+static int __memory_block_change_state(struct memory_block *mem,
                unsigned long to_state, unsigned long from_state_req)
 {
        int ret = 0;
 
-       mutex_lock(&mem->state_mutex);
-
        if (mem->state != from_state_req) {
                ret = -EINVAL;
                goto out;
@@ -312,10 +307,20 @@ static int memory_block_change_state(struct memory_block *mem,
                break;
        }
 out:
-       mutex_unlock(&mem->state_mutex);
        return ret;
 }
 
+static int memory_block_change_state(struct memory_block *mem,
+               unsigned long to_state, unsigned long from_state_req)
+{
+       int ret;
+
+       mutex_lock(&mem->state_mutex);
+       ret = __memory_block_change_state(mem, to_state, from_state_req);
+       mutex_unlock(&mem->state_mutex);
+
+       return ret;
+}
 static ssize_t
 store_mem_state(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
@@ -655,6 +660,21 @@ int unregister_memory_section(struct mem_section *section)
        return remove_memory_block(0, section, 0);
 }
 
+/*
+ * offline one memory block. If the memory block has been offlined, do nothing.
+ */
+int offline_memory_block(struct memory_block *mem)
+{
+       int ret = 0;
+
+       mutex_lock(&mem->state_mutex);
+       if (mem->state != MEM_OFFLINE)
+               ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
+       mutex_unlock(&mem->state_mutex);
+
+       return ret;
+}
+
 /*
  * Initialize the sysfs support for memory devices...
  */