Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
[pandora-kernel.git] / drivers / base / memory.c
index 933442f..cafeaaf 100644 (file)
@@ -27,6 +27,8 @@
 #include <asm/atomic.h>
 #include <asm/uaccess.h>
 
+static DEFINE_MUTEX(mem_sysfs_mutex);
+
 #define MEMORY_CLASS_NAME      "memory"
 
 static struct sysdev_class memory_sysdev_class = {
@@ -435,6 +437,45 @@ int __weak arch_get_memory_phys_device(unsigned long start_pfn)
        return 0;
 }
 
+struct memory_block *find_memory_block_hinted(struct mem_section *section,
+                                             struct memory_block *hint)
+{
+       struct kobject *kobj;
+       struct sys_device *sysdev;
+       struct memory_block *mem;
+       char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1];
+
+       kobj = hint ? &hint->sysdev.kobj : NULL;
+
+       /*
+        * This only works because we know that section == sysdev->id
+        * slightly redundant with sysdev_register()
+        */
+       sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section));
+
+       kobj = kset_find_obj_hinted(&memory_sysdev_class.kset, name, kobj);
+       if (!kobj)
+               return NULL;
+
+       sysdev = container_of(kobj, struct sys_device, kobj);
+       mem = container_of(sysdev, struct memory_block, sysdev);
+
+       return mem;
+}
+
+/*
+ * For now, we have a linear search to go find the appropriate
+ * memory_block corresponding to a particular phys_index. If
+ * this gets to be a real problem, we can always use a radix
+ * tree or something here.
+ *
+ * This could be made generic for all sysdev classes.
+ */
+struct memory_block *find_memory_block(struct mem_section *section)
+{
+       return find_memory_block_hinted(section, NULL);
+}
+
 static int add_memory_block(int nid, struct mem_section *section,
                        unsigned long state, enum mem_add_context context)
 {
@@ -445,8 +486,11 @@ static int add_memory_block(int nid, struct mem_section *section,
        if (!mem)
                return -ENOMEM;
 
+       mutex_lock(&mem_sysfs_mutex);
+
        mem->phys_index = __section_nr(section);
        mem->state = state;
+       mem->section_count++;
        mutex_init(&mem->state_mutex);
        start_pfn = section_nr_to_pfn(mem->phys_index);
        mem->phys_device = arch_get_memory_phys_device(start_pfn);
@@ -465,53 +509,29 @@ static int add_memory_block(int nid, struct mem_section *section,
                        ret = register_mem_sect_under_node(mem, nid);
        }
 
+       mutex_unlock(&mem_sysfs_mutex);
        return ret;
 }
 
-/*
- * For now, we have a linear search to go find the appropriate
- * memory_block corresponding to a particular phys_index. If
- * this gets to be a real problem, we can always use a radix
- * tree or something here.
- *
- * This could be made generic for all sysdev classes.
- */
-struct memory_block *find_memory_block(struct mem_section *section)
-{
-       struct kobject *kobj;
-       struct sys_device *sysdev;
-       struct memory_block *mem;
-       char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1];
-
-       /*
-        * This only works because we know that section == sysdev->id
-        * slightly redundant with sysdev_register()
-        */
-       sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section));
-
-       kobj = kset_find_obj(&memory_sysdev_class.kset, name);
-       if (!kobj)
-               return NULL;
-
-       sysdev = container_of(kobj, struct sys_device, kobj);
-       mem = container_of(sysdev, struct memory_block, sysdev);
-
-       return mem;
-}
-
 int remove_memory_block(unsigned long node_id, struct mem_section *section,
                int phys_device)
 {
        struct memory_block *mem;
 
+       mutex_lock(&mem_sysfs_mutex);
        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);
-       mem_remove_simple_file(mem, removable);
-       unregister_memory(mem, section);
 
+       mem->section_count--;
+       if (mem->section_count == 0) {
+               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);
+               mem_remove_simple_file(mem, removable);
+               unregister_memory(mem, section);
+       }
+
+       mutex_unlock(&mem_sysfs_mutex);
        return 0;
 }