
add/remove_pages() don't really even take pages as arguments.  
Change their names to be more generic than _pages to _memory.

Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
---

 memhotplug1-dave/arch/i386/mm/init.c            |    8 -
 memhotplug1-dave/arch/ppc64/Kconfig             |    4 
 memhotplug1-dave/drivers/base/memory.c          |  100 ++++++++++++++++++++----
 memhotplug1-dave/include/linux/memory.h         |    1 
 memhotplug1-dave/include/linux/memory_hotplug.h |   26 +++++-
 memhotplug1-dave/mm/memory_hotplug.c            |   84 +++++++++++++-------
 memhotplug1/include/asm-i386/memory_hotplug.h   |    8 -
 mm/page_alloc.c                                 |    0 
 8 files changed, 171 insertions(+), 60 deletions(-)

diff -puN drivers/base/memory.c~N-sysfs-memory-class-work drivers/base/memory.c
--- memhotplug1/drivers/base/memory.c~N-sysfs-memory-class-work	2004-11-04 16:45:54.000000000 -0800
+++ memhotplug1-dave/drivers/base/memory.c	2004-11-04 16:45:54.000000000 -0800
@@ -11,6 +11,7 @@
 #include <linux/memory.h>
 #include <linux/kobject.h>
 #include <asm/atomic.h>
+#include <asm/uaccess.h>
 
 struct sysdev_class memory_sysdev_class = {
 	set_kset_name("memory"),
@@ -29,7 +30,7 @@ static int memory_hotplug_filter(struct 
 
 static char *memory_hotplug_name(struct kset *kset, struct kobject *kobj)
 {
-        return "-no_name_implemented";
+        return "memory";
 }
 
 static int memory_hotplug(struct kset *kset, struct kobject *kobj, char **envp,
@@ -100,8 +101,77 @@ static ssize_t show_mem_state(struct sys
 {
 	struct memory_block *mem =
 		container_of(dev, struct memory_block, sysdev);
-	return sprintf(buf, "%d\n", mem->state);
+	ssize_t len = 0;
+
+	/*
+	 * We can probably put these states in a nice little array
+	 * so that they're not open-coded
+	 */
+	switch (mem->state) {
+		case MEM_ONLINE:
+			len = sprintf(buf, "online\n");
+			break;
+		case MEM_OFFLINE:
+			len = sprintf(buf, "offline\n");
+			break;
+		case MEM_GOING_OFFLINE:
+			len = sprintf(buf, "going-offline\n");
+			break;
+		case MEM_INVALID:
+			len = sprintf(buf, "invalid\n");
+			break;
+		default:
+			len = sprintf(buf, "ERROR\n");
+			break;
+	}
+
+	return len;
 }
+
+static int online_memory_block(struct memory_block *mem)
+{
+	int ret = 0;
+	down(&mem->state_sem);
+
+	if (mem->state != MEM_OFFLINE)
+		return -EINVAL;
+
+	ret = __online_memory_block(mem);
+	if (!ret)
+		mem->state = MEM_ONLINE;
+
+	up(&mem->state_sem);
+
+	return ret;
+}
+
+static ssize_t
+store_mem_state(struct sys_device *dev, const char *buf, size_t count)
+{
+	struct memory_block *mem =
+		container_of(dev, struct memory_block, sysdev);
+	unsigned int phys_section_nr = mem->phys_index;
+	unsigned int section = phys_section[phys_section_nr];
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (mem_section[section].phys_section == INVALID_SECTION) {
+		printk("%s: Ummm.. this section (%d) is not currently mapped!\n",
+			__func__, section);
+		return -EINVAL;
+	}
+
+	if (!strncmp(buf, "online", min((int)count,6))) {
+		online_memory_block(mem);
+	} else if(!strncmp(buf, "offline", min((int)count,7)))
+		return -ENOSYS;
+	else
+		return -EINVAL;
+
+	return count;
+}
+
 /*
  * phys_device is a bad name for this.  What I really want
  * is a way to differentiate between memory ranges that
@@ -119,7 +189,7 @@ static ssize_t show_phys_device(struct s
 }
 
 SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL);
-SYSDEV_ATTR(state, 0444, show_mem_state, NULL);
+SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state);
 SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL);
 
 #define mem_create_simple_file(mem, attr_name)	\
@@ -151,18 +221,26 @@ static int block_size_init(void)
 /* define this off in some header somewhere ... */
 #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, const char __user *buf, size_t count)
 {
+	u64 phys_addr;
+	int ret;
 	/*
 	 * Hmmm... what do we really want this to do?
 	 */
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
 
-	/* make call into arch code */
-	//static int num; /* hehe. no locking */
+	phys_addr = simple_strtoull(buf, NULL, 0);
 
-	//add_memory_block(num++, 0x100*num, MEM_OFFLINE, 4);
+	// a hardware check for the ram?
+	//if (!ram_present(phys_addr, PAGES_PER_SECTION))
+	//	return -EINVAL;
+
+	ret = add_pages(phys_addr, (PAGES_PER_SECTION << PAGE_SHIFT), 0);
+
+	if (ret)
+		count = ret;
 
 	return count;
 }
@@ -199,6 +277,7 @@ int add_memory_block(unsigned long node_
 
 	mem->phys_index = phys_index;
 	mem->state = state;
+	init_MUTEX(&mem->state_sem);
 	mem->phys_device = phys_device;
 
 #if 0
@@ -220,7 +299,6 @@ int add_memory_block(unsigned long node_
 	return ret;
 }
 
-#define update_memdevice(...) 	do {} while(0)
 #define online_section(...) 	do {} while(0)
 
 static ssize_t
@@ -243,12 +321,6 @@ online_store(struct class *class, const 
 	 */
 	online_section(section);
 
-	/*
-	 * Now that the memory has been truly onlined, update the
-	 * appropriate entry in sysfs
-	 */
-	update_memdevice();
-
 	return count;
 }
 static CLASS_ATTR(online, 0700, NULL, online_store);
diff -puN include/linux/memory.h~N-sysfs-memory-class-work include/linux/memory.h
--- memhotplug1/include/linux/memory.h~N-sysfs-memory-class-work	2004-11-04 16:45:54.000000000 -0800
+++ memhotplug1-dave/include/linux/memory.h	2004-11-04 16:45:54.000000000 -0800
@@ -54,6 +54,7 @@ enum memory_state {
 struct memory_block {
 	unsigned long phys_index;
 	enum memory_state state; 	/* just filler for now */
+	struct semaphore state_sem;
 	int phys_device;		/* to which fru does this belong? */
 	void *hw;			/* optional pointer to fw/hw data */
 	int (*phys_callback)(struct memory_block *);
diff -puN arch/ppc64/Kconfig~N-sysfs-memory-class-work arch/ppc64/Kconfig
--- memhotplug1/arch/ppc64/Kconfig~N-sysfs-memory-class-work	2004-11-04 16:45:54.000000000 -0800
+++ memhotplug1-dave/arch/ppc64/Kconfig	2004-11-04 16:45:54.000000000 -0800
@@ -207,6 +207,10 @@ config ARCH_HAS_BOOTPA
 config NONLINEAR
 	bool "Allow nonlinear physical memory"
 
+config MEMORY_HOTPLUG
+	bool "Allow for memory hotplug"
+	depends on NONLINEAR && HOTPLUG
+
 config DISCONTIGMEM
 	bool "Discontiguous Memory Support"
 	depends on SMP && PPC_PSERIES
diff -puN include/linux/memory_hotplug.h~N-sysfs-memory-class-work include/linux/memory_hotplug.h
--- memhotplug1/include/linux/memory_hotplug.h~N-sysfs-memory-class-work	2004-11-04 16:45:54.000000000 -0800
+++ memhotplug1-dave/include/linux/memory_hotplug.h	2004-11-04 16:45:54.000000000 -0800
@@ -1,21 +1,43 @@
 #ifndef __MEMORY_HOTPLUG_H
 #define __MEMORY_HOTPLUG_H
 
-#include <asm/memory_hotplug.h>
-
 extern int zone_grow_free_lists(struct zone *zone, unsigned long new_nr_pages);
 extern int zone_grow_waitqueues(struct zone *zone, unsigned long nr_pages);
 extern int add_one_highpage(struct page *page, int pfn, int bad_ppro);
 /* need some defines for these for archs that don't support it */
 extern void online_page(struct page *page);
+/* VM interface that may be used by firmware interface */
+extern int add_pages(u64 start, u64 size, unsigned long attr);
+extern int remove_pages(u64 start, u64 size, unsigned long attr);
 
 struct memory_block;
 extern int __online_memory_block(struct memory_block *);
 
+#ifdef CONFIG_MEMORY_HOTPLUG
 /* reasonably generic interface to expand the physical pages in a zone  */
 extern int __add_pages(struct zone *zone, unsigned long start_pfn,
 	unsigned long nr_pages, unsigned long attr);
 extern int __remove_pages(struct zone *zone, unsigned long start_pfn,
 	unsigned long nr_pages, unsigned long attr);
+#else
+static inline int mhp_notimplemented(char *func)
+{
+	printk("%s() called, with CONFIG_MEMORY_HOTPLUG disabled\n", __func__);
+	dump_stack();
+	return -ENOSYS;
+}
+
+static inline int __add_pages(struct zone *zone, unsigned long start_pfn,
+	unsigned long nr_pages, unsigned long attr)
+{
+	return mhp_notimplemented(__FUNCTION__);
+}
+static inline int __remove_pages(struct zone *zone, unsigned long start_pfn,
+	unsigned long nr_pages, unsigned long attr)
+{
+	return mhp_notimplemented(__FUNCTION__);
+}
+
+#endif /* CONFIG_MEMORY_HOTPLUG */
 
 #endif
diff -L include/asm-i386/memory_hotplug.h -puN include/asm-i386/memory_hotplug.h~N-sysfs-memory-class-work /dev/null
--- memhotplug1/include/asm-i386/memory_hotplug.h
+++ /dev/null	2004-08-06 10:20:23.000000000 -0700
@@ -1,8 +0,0 @@
-#ifndef __ASM_MEMORY_HOTPLUG_H
-#define __ASM_MEMORY_HOTPLUG_H
-
-/* VM interface that may be used by firmware interface */
-extern int add_pages(u64 start, u64 size, unsigned long attr);
-extern int remove_pages(u64 start, u64 size, unsigned long attr);
-
-#endif
diff -puN mm/memory_hotplug.c~N-sysfs-memory-class-work mm/memory_hotplug.c
--- memhotplug1/mm/memory_hotplug.c~N-sysfs-memory-class-work	2004-11-04 16:45:54.000000000 -0800
+++ memhotplug1-dave/mm/memory_hotplug.c	2004-11-04 16:45:54.000000000 -0800
@@ -30,9 +30,14 @@ static struct page *__kmalloc_section_me
 
 	page = alloc_pages(GFP_KERNEL, get_order(memmap_size));
 	if (!page) {
-		printk("Failed to allocate new memmap!\n");
+		printk(KERN_WARNING "Failed to allocate new memmap!\n");
 		return NULL;
 	}
+	SetPageReserved(page);
+
+	printk(KERN_DEBUG "%s() set page: %p set mapping to %p\n", __func__,
+		page, page->mapping);
+	printk(KERN_DEBUG "page_count(page): %d\n", page_count(page));
 
 	ret = (struct page *)pfn_to_kaddr(page_to_pfn(page));
 	memset(ret, 0, memmap_size);
@@ -40,58 +45,81 @@ static struct page *__kmalloc_section_me
 	return ret;
 }
 
-/*
- * Reasonably generic function for adding memory.  It is
- * expected that archs that support memory hotplug will
- * call this function after deciding the zone to which to
- * add the new pages.
- */
-int __add_pages(struct zone *zone, unsigned long phys_start_pfn,
-		unsigned long nr_pages, unsigned long attr)
+int __add_section(struct zone *zone, unsigned long phys_start_pfn,
+		  unsigned long attr)
 {
 	struct pglist_data *pgdat = zone->zone_pgdat;
 	struct page *memmap;
-	unsigned long base_pfn;
-	int i, err;
+	int i;
+	unsigned long nr_pages = PAGES_PER_SECTION;
+
+	printk(KERN_DEBUG "%s(%p, %08lx, %08lx)\n", __func__, zone,
+		phys_start_pfn, attr);
 
-	base_pfn = zone->zone_start_pfn + zone->present_pages;
+	down(&zone->resize_sem);
+
+	printk(KERN_DEBUG "%s() phys_start_pfn: %08lx\n", __func__, phys_start_pf);
+	if (phys_section[pfn_to_section(phys_start_pfn)] != INVALID_PHYS_SECTION) {
+		printk(KERN_DEBUG "%s(): phys_section[%d] already online\n",
+			__func__, pfn_to_section(phys_start_pfn));
+		up(&zone->resize_sem);
+		return -EEXIST;
+	}
+	alloc_memsections(phys_start_pfn, phys_start_pfn, nr_pages);
 
-	alloc_memsections(base_pfn, phys_start_pfn, nr_pages);
+	up(&zone->resize_sem);
 
 	memmap = __kmalloc_section_memmap(nr_pages);
 	if (!memmap)
 		return -ENOMEM;
 
-	err = zone_grow_free_lists(zone, zone->spanned_pages + nr_pages);
-	if (err) {
-		kfree(memmap);
-		return -1;
-	}
-
-	alloc_memmap(memmap, base_pfn, nr_pages);
+	alloc_memmap(memmap, phys_start_pfn,, nr_pages);
 	memmap_init_zone(nr_pages, 0, zone - pgdat->node_zones, phys_start_pfn);
 
-	/* Hmm, our use of zone->present_pages above either a) needs to change
-	 * or b) needs to mean that "pages that are present, BUT not necessarily
-	 * used right now (i.e. offline)."  For now, use this hack to mean b).
-	 */
-	zone->present_pages += nr_pages;
-
 	/*
 	 * Actually, we don't want to online the pages here at all.  We
 	 * will enable the new regions to be available via sysfs and thus
 	 * onlined from user space.
 	 */
 
-	for (i = 0; i < nr_pages; i += SECTION_SIZE, base_pfn += SECTION_SIZE) {
-		unsigned int section = pfn_to_section(base_pfn);
+	for (i = 0; i < nr_pages; i += SECTION_SIZE) {
+		unsigned int section = pfn_to_section(phys_start_pfn + i);
 		register_new_memory(section);
 	}
 
 	return 0;
 }
 
+/*
+ * Reasonably generic function for adding memory.  It is
+ * expected that archs that support memory hotplug will
+ * call this function after deciding the zone to which to
+ * add the new pages.
+ */
+int __add_pages(struct zone *zone, unsigned long phys_start_pfn,
+		 unsigned long nr_pages, unsigned long attr)
+{
+	unsigned long i;
+	int err = 0;
 
+	printk(KERN_DEBUG "%s(%p, %08lx, %ld, %08lx)\n", __func__,
+			zone, phys_start_pfn, nr_pages, attr);
+
+	for (i = 0; i < nr_pages; i += PAGES_PER_SECTION) {
+		 printk(KERN_DEBUG "\tfor: i: %ld\n", i);
+		 err = __add_section(zone, phys_start_pfn + i, attr);
+
+		 if (err)
+			break;
+	}
+
+	/*
+	 * Should we back the ones out that succeeded if any part of
+	 * the addition fails?
+	 */
+
+	return err;
+}
 
 static int
 online_pages(unsigned long pfn, unsigned long nr_pages)
diff -puN arch/i386/mm/init.c~N-sysfs-memory-class-work arch/i386/mm/init.c
--- memhotplug1/arch/i386/mm/init.c~N-sysfs-memory-class-work	2004-11-04 16:45:54.000000000 -0800
+++ memhotplug1-dave/arch/i386/mm/init.c	2004-11-04 16:45:54.000000000 -0800
@@ -327,16 +327,8 @@ int add_one_highpage(struct page *page, 
 
 void online_page(struct page *page)
 {
-#ifndef CONFIG_NUMA
-	struct pglist_data *pgdata = &contig_page_data;
-	struct zone *zone = pgdata->node_zones + MAX_NR_ZONES-1;
-#endif
 	ClearPageReserved(page);
 	add_one_highpage(page, page_to_pfn(page), 0);
-	/* see note in mm/memory_hotplug.c
-	 * this needs to be fixed properly
-	 */
-	/* zone->present_pages++; */
 }
 
 /*
diff -puN mm/page_alloc.c~N-sysfs-memory-class-work mm/page_alloc.c
_
