libata-sff: rename ap->ops->drain_fifo() to sff_drain_fifo()
[safe/jmp/linux-2.6] / drivers / base / memory.c
index 5260e9e..933442f 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/mm.h>
 #include <linux/mutex.h>
 #include <linux/stat.h>
+#include <linux/slab.h>
 
 #include <asm/atomic.h>
 #include <asm/uaccess.h>
@@ -44,7 +45,7 @@ static int memory_uevent(struct kset *kset, struct kobject *obj, struct kobj_uev
        return retval;
 }
 
-static struct kset_uevent_ops memory_uevent_ops = {
+static const struct kset_uevent_ops memory_uevent_ops = {
        .name           = memory_uevent_name,
        .uevent         = memory_uevent,
 };
@@ -63,6 +64,20 @@ void unregister_memory_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL(unregister_memory_notifier);
 
+static ATOMIC_NOTIFIER_HEAD(memory_isolate_chain);
+
+int register_memory_isolate_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&memory_isolate_chain, nb);
+}
+EXPORT_SYMBOL(register_memory_isolate_notifier);
+
+void unregister_memory_isolate_notifier(struct notifier_block *nb)
+{
+       atomic_notifier_chain_unregister(&memory_isolate_chain, nb);
+}
+EXPORT_SYMBOL(unregister_memory_isolate_notifier);
+
 /*
  * register_memory - Setup a sysfs device for a memory block
  */
@@ -157,6 +172,11 @@ int memory_notify(unsigned long val, void *v)
        return blocking_notifier_call_chain(&memory_chain, val, v);
 }
 
+int memory_isolate_notify(unsigned long val, void *v)
+{
+       return atomic_notifier_call_chain(&memory_isolate_chain, val, v);
+}
+
 /*
  * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is
  * OK to have direct references to sparsemem variables in here.
@@ -290,17 +310,18 @@ static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL);
  * Block size attribute stuff
  */
 static ssize_t
-print_block_size(struct class *class, char *buf)
+print_block_size(struct sysdev_class *class, struct sysdev_class_attribute *attr,
+                char *buf)
 {
        return sprintf(buf, "%lx\n", (unsigned long)PAGES_PER_SECTION * PAGE_SIZE);
 }
 
-static CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL);
+static SYSDEV_CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL);
 
 static int block_size_init(void)
 {
        return sysfs_create_file(&memory_sysdev_class.kset.kobj,
-                               &class_attr_block_size_bytes.attr);
+                               &attr_block_size_bytes.attr);
 }
 
 /*
@@ -311,7 +332,8 @@ static int block_size_init(void)
  */
 #ifdef CONFIG_ARCH_MEMORY_PROBE
 static ssize_t
-memory_probe_store(struct class *class, const char *buf, size_t count)
+memory_probe_store(struct class *class, struct class_attribute *attr,
+                  const char *buf, size_t count)
 {
        u64 phys_addr;
        int nid;
@@ -341,16 +363,83 @@ static inline int memory_probe_init(void)
 }
 #endif
 
+#ifdef CONFIG_MEMORY_FAILURE
+/*
+ * Support for offlining pages of memory
+ */
+
+/* Soft offline a page */
+static ssize_t
+store_soft_offline_page(struct class *class,
+                       struct class_attribute *attr,
+                       const char *buf, size_t count)
+{
+       int ret;
+       u64 pfn;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       if (strict_strtoull(buf, 0, &pfn) < 0)
+               return -EINVAL;
+       pfn >>= PAGE_SHIFT;
+       if (!pfn_valid(pfn))
+               return -ENXIO;
+       ret = soft_offline_page(pfn_to_page(pfn), 0);
+       return ret == 0 ? count : ret;
+}
+
+/* Forcibly offline a page, including killing processes. */
+static ssize_t
+store_hard_offline_page(struct class *class,
+                       struct class_attribute *attr,
+                       const char *buf, size_t count)
+{
+       int ret;
+       u64 pfn;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       if (strict_strtoull(buf, 0, &pfn) < 0)
+               return -EINVAL;
+       pfn >>= PAGE_SHIFT;
+       ret = __memory_failure(pfn, 0, 0);
+       return ret ? ret : count;
+}
+
+static CLASS_ATTR(soft_offline_page, 0644, NULL, store_soft_offline_page);
+static CLASS_ATTR(hard_offline_page, 0644, NULL, store_hard_offline_page);
+
+static __init int memory_fail_init(void)
+{
+       int err;
+
+       err = sysfs_create_file(&memory_sysdev_class.kset.kobj,
+                               &class_attr_soft_offline_page.attr);
+       if (!err)
+               err = sysfs_create_file(&memory_sysdev_class.kset.kobj,
+                               &class_attr_hard_offline_page.attr);
+       return err;
+}
+#else
+static inline int memory_fail_init(void)
+{
+       return 0;
+}
+#endif
+
 /*
  * Note that phys_device is optional.  It is here to allow for
  * differentiation between which *physical* devices each
  * section belongs to...
  */
+int __weak arch_get_memory_phys_device(unsigned long start_pfn)
+{
+       return 0;
+}
 
-static int add_memory_block(unsigned long node_id, struct mem_section *section,
-                    unsigned long state, int phys_device)
+static int add_memory_block(int nid, struct mem_section *section,
+                       unsigned long state, enum mem_add_context context)
 {
        struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+       unsigned long start_pfn;
        int ret = 0;
 
        if (!mem)
@@ -359,7 +448,8 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section,
        mem->phys_index = __section_nr(section);
        mem->state = state;
        mutex_init(&mem->state_mutex);
-       mem->phys_device = phys_device;
+       start_pfn = section_nr_to_pfn(mem->phys_index);
+       mem->phys_device = arch_get_memory_phys_device(start_pfn);
 
        ret = register_memory(mem, section);
        if (!ret)
@@ -370,6 +460,10 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section,
                ret = mem_create_simple_file(mem, phys_device);
        if (!ret)
                ret = mem_create_simple_file(mem, removable);
+       if (!ret) {
+               if (context == HOTPLUG)
+                       ret = register_mem_sect_under_node(mem, nid);
+       }
 
        return ret;
 }
@@ -382,7 +476,7 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section,
  *
  * This could be made generic for all sysdev classes.
  */
-static struct memory_block *find_memory_block(struct mem_section *section)
+struct memory_block *find_memory_block(struct mem_section *section)
 {
        struct kobject *kobj;
        struct sys_device *sysdev;
@@ -411,6 +505,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section,
        struct memory_block *mem;
 
        mem = find_memory_block(section);
+       unregister_mem_sect_under_nodes(mem);
        mem_remove_simple_file(mem, phys_index);
        mem_remove_simple_file(mem, state);
        mem_remove_simple_file(mem, phys_device);
@@ -424,9 +519,9 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section,
  * need an interface for the VM to add new memory regions,
  * but without onlining it.
  */
-int register_new_memory(struct mem_section *section)
+int register_new_memory(int nid, struct mem_section *section)
 {
-       return add_memory_block(0, section, MEM_OFFLINE, 0);
+       return add_memory_block(nid, section, MEM_OFFLINE, HOTPLUG);
 }
 
 int unregister_memory_section(struct mem_section *section)
@@ -458,7 +553,8 @@ int __init memory_dev_init(void)
        for (i = 0; i < NR_MEM_SECTIONS; i++) {
                if (!present_section_nr(i))
                        continue;
-               err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, 0);
+               err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE,
+                                      BOOT);
                if (!ret)
                        ret = err;
        }
@@ -466,6 +562,9 @@ int __init memory_dev_init(void)
        err = memory_probe_init();
        if (!ret)
                ret = err;
+       err = memory_fail_init();
+       if (!ret)
+               ret = err;
        err = block_size_init();
        if (!ret)
                ret = err;