return len;
 }
 
-static inline int memory_notify(unsigned long val, void *v)
+int memory_notify(unsigned long val, void *v)
 {
        return blocking_notifier_call_chain(&memory_chain, val, v);
 }
                        break;
                case MEM_OFFLINE:
                        mem->state = MEM_GOING_OFFLINE;
-                       memory_notify(MEM_GOING_OFFLINE, NULL);
                        start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
                        ret = remove_memory(start_paddr,
                                            PAGES_PER_SECTION << PAGE_SHIFT);
                                mem->state = old_state;
                                break;
                        }
-                       memory_notify(MEM_MAPPING_INVALID, NULL);
                        break;
                default:
                        printk(KERN_WARNING "%s(%p, %ld) unknown action: %ld\n",
                        WARN_ON(1);
                        ret = -EINVAL;
        }
-       /*
-        * For now, only notify on successful memory operations
-        */
-       if (!ret)
-               memory_notify(action, NULL);
 
        return ret;
 }
 
 #define        MEM_ONLINE              (1<<0) /* exposed to userspace */
 #define        MEM_GOING_OFFLINE       (1<<1) /* exposed to userspace */
 #define        MEM_OFFLINE             (1<<2) /* exposed to userspace */
+#define        MEM_GOING_ONLINE        (1<<3)
+#define        MEM_CANCEL_ONLINE       (1<<4)
+#define        MEM_CANCEL_OFFLINE      (1<<5)
 
-/*
- * All of these states are currently kernel-internal for notifying
- * kernel components and architectures.
- *
- * For MEM_MAPPING_INVALID, all notifier chains with priority >0
- * are called before pfn_to_page() becomes invalid.  The priority=0
- * entry is reserved for the function that actually makes
- * pfn_to_page() stop working.  Any notifiers that want to be called
- * after that should have priority <0.
- */
-#define        MEM_MAPPING_INVALID     (1<<3)
+struct memory_notify {
+       unsigned long start_pfn;
+       unsigned long nr_pages;
+       int status_change_nid;
+};
 
 struct notifier_block;
 struct mem_section;
 static inline void unregister_memory_notifier(struct notifier_block *nb)
 {
 }
+static inline int memory_notify(unsigned long val, void *v)
+{
+       return 0;
+}
 #else
+extern int register_memory_notifier(struct notifier_block *nb);
+extern void unregister_memory_notifier(struct notifier_block *nb);
 extern int register_new_memory(struct mem_section *);
 extern int unregister_memory_section(struct mem_section *);
 extern int memory_dev_init(void);
 extern int remove_memory_block(unsigned long, struct mem_section *, int);
-
+extern int memory_notify(unsigned long val, void *v);
 #define CONFIG_MEM_BLOCK_SIZE  (PAGES_PER_SECTION<<PAGE_SHIFT)
 
 
 
        unsigned long onlined_pages = 0;
        struct zone *zone;
        int need_zonelists_rebuild = 0;
+       int nid;
+       int ret;
+       struct memory_notify arg;
+
+       arg.start_pfn = pfn;
+       arg.nr_pages = nr_pages;
+       arg.status_change_nid = -1;
+
+       nid = page_to_nid(pfn_to_page(pfn));
+       if (node_present_pages(nid) == 0)
+               arg.status_change_nid = nid;
 
+       ret = memory_notify(MEM_GOING_ONLINE, &arg);
+       ret = notifier_to_errno(ret);
+       if (ret) {
+               memory_notify(MEM_CANCEL_ONLINE, &arg);
+               return ret;
+       }
        /*
         * This doesn't need a lock to do pfn_to_page().
         * The section can't be removed here because of the
                build_all_zonelists();
        vm_total_pages = nr_free_pagecache_pages();
        writeback_set_ratelimit();
+
+       if (onlined_pages)
+               memory_notify(MEM_ONLINE, &arg);
+
        return 0;
 }
 #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
 {
        unsigned long pfn, nr_pages, expire;
        long offlined_pages;
-       int ret, drain, retry_max;
+       int ret, drain, retry_max, node;
        struct zone *zone;
+       struct memory_notify arg;
 
        BUG_ON(start_pfn >= end_pfn);
        /* at least, alignment against pageblock is necessary */
           we assume this for now. .*/
        if (!test_pages_in_a_zone(start_pfn, end_pfn))
                return -EINVAL;
+
+       zone = page_zone(pfn_to_page(start_pfn));
+       node = zone_to_nid(zone);
+       nr_pages = end_pfn - start_pfn;
+
        /* set above range as isolated */
        ret = start_isolate_page_range(start_pfn, end_pfn);
        if (ret)
                return ret;
-       nr_pages = end_pfn - start_pfn;
+
+       arg.start_pfn = start_pfn;
+       arg.nr_pages = nr_pages;
+       arg.status_change_nid = -1;
+       if (nr_pages >= node_present_pages(node))
+               arg.status_change_nid = node;
+
+       ret = memory_notify(MEM_GOING_OFFLINE, &arg);
+       ret = notifier_to_errno(ret);
+       if (ret)
+               goto failed_removal;
+
        pfn = start_pfn;
        expire = jiffies + timeout;
        drain = 0;
        /* reset pagetype flags */
        start_isolate_page_range(start_pfn, end_pfn);
        /* removal success */
-       zone = page_zone(pfn_to_page(start_pfn));
        zone->present_pages -= offlined_pages;
        zone->zone_pgdat->node_present_pages -= offlined_pages;
        totalram_pages -= offlined_pages;
        num_physpages -= offlined_pages;
+
        vm_total_pages = nr_free_pagecache_pages();
        writeback_set_ratelimit();
+
+       memory_notify(MEM_OFFLINE, &arg);
        return 0;
 
 failed_removal:
        printk(KERN_INFO "memory offlining %lx to %lx failed\n",
                start_pfn, end_pfn);
+       memory_notify(MEM_CANCEL_OFFLINE, &arg);
        /* pushback to free area */
        undo_isolate_page_range(start_pfn, end_pfn);
+
        return ret;
 }
 #else