kcore: use registerd physmem information
authorKAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Tue, 22 Sep 2009 23:45:48 +0000 (16:45 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 Sep 2009 14:39:41 +0000 (07:39 -0700)
For /proc/kcore, each arch registers its memory range by kclist_add().
In usual,

- range of physical memory
- range of vmalloc area
- text, etc...

are registered but "range of physical memory" has some troubles.  It
doesn't updated at memory hotplug and it tend to include unnecessary
memory holes.  Now, /proc/iomem (kernel/resource.c) includes required
physical memory range information and it's properly updated at memory
hotplug.  Then, it's good to avoid using its own code(duplicating
information) and to rebuild kclist for physical memory based on
/proc/iomem.

Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: WANG Cong <xiyou.wangcong@gmail.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/ia64/mm/init.c
arch/mips/mm/init.c
arch/powerpc/mm/init_32.c
arch/powerpc/mm/init_64.c
arch/sh/mm/init.c
arch/x86/mm/init_32.c
arch/x86/mm/init_64.c
fs/proc/kcore.c

index 3f95ea1d6fb68920b5edf77123e7c43c24d6ac13..1857766a63c1850d968320559471c22a851b6335 100644 (file)
@@ -617,7 +617,6 @@ mem_init (void)
        long reserved_pages, codesize, datasize, initsize;
        pg_data_t *pgdat;
        int i;
-       static struct kcore_list kcore_mem;
 
        BUG_ON(PTRS_PER_PGD * sizeof(pgd_t) != PAGE_SIZE);
        BUG_ON(PTRS_PER_PMD * sizeof(pmd_t) != PAGE_SIZE);
@@ -639,8 +638,6 @@ mem_init (void)
 
        high_memory = __va(max_low_pfn * PAGE_SIZE);
 
-       kclist_add(&kcore_mem, __va(0), max_low_pfn * PAGE_SIZE, KCORE_RAM);
-
        for_each_online_pgdat(pgdat)
                if (pgdat->bdata->node_bootmem_map)
                        totalram_pages += free_all_bootmem_node(pgdat);
index 4a83da499e2da45d86fe79b804685dd6f3e99884..15aa1902a788b4d83fa74833d9ca73a60ff879f7 100644 (file)
@@ -352,7 +352,6 @@ void __init paging_init(void)
        free_area_init_nodes(max_zone_pfns);
 }
 
-static struct kcore_list kcore_mem;
 #ifdef CONFIG_64BIT
 static struct kcore_list kcore_kseg0;
 #endif
@@ -412,7 +411,6 @@ void __init mem_init(void)
                kclist_add(&kcore_kseg0, (void *) CKSEG0,
                                0x80000000 - 4, KCORE_TEXT);
 #endif
-       kclist_add(&kcore_mem, __va(0), max_low_pfn << PAGE_SHIFT, KCORE_RAM);
 
        printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, "
               "%ldk reserved, %ldk data, %ldk init, %ldk highmem)\n",
index 38a450d16b27b715d447896f895adb12ec5cd143..9ddcfb4dc139f058f31a8df44668221bc0e1227b 100644 (file)
@@ -242,35 +242,3 @@ void free_initrd_mem(unsigned long start, unsigned long end)
 }
 #endif
 
-#ifdef CONFIG_PROC_KCORE
-
-static int __init setup_kcore(void)
-{
-       int i;
-
-       for (i = 0; i < lmb.memory.cnt; i++) {
-               unsigned long base;
-               unsigned long size;
-               struct kcore_list *kcore_mem;
-
-               base = lmb.memory.region[i].base;
-               size = lmb.memory.region[i].size;
-
-               kcore_mem = kmalloc(sizeof(struct kcore_list), GFP_ATOMIC);
-               if (!kcore_mem)
-                       panic("%s: kmalloc failed\n", __func__);
-
-               /* must stay under 32 bits */
-               if ( 0xfffffffful - (unsigned long)__va(base) < size) {
-                       size = 0xfffffffful - (unsigned long)(__va(base));
-                       printk(KERN_DEBUG "setup_kcore: restrict size=%lx\n",
-                                               size);
-               }
-
-               kclist_add(kcore_mem, __va(base), size, KCORE_RAM);
-       }
-
-       return 0;
-}
-module_init(setup_kcore);
-#endif
index 87ef492a1a5dcd2fab47d1e95f6aafce88244b13..335c578b9cc324261a29a3a435036ef5178e9f96 100644 (file)
@@ -109,32 +109,6 @@ void free_initrd_mem(unsigned long start, unsigned long end)
 }
 #endif
 
-#ifdef CONFIG_PROC_KCORE
-
-static int __init setup_kcore(void)
-{
-       int i;
-
-       for (i=0; i < lmb.memory.cnt; i++) {
-               unsigned long base, size;
-               struct kcore_list *kcore_mem;
-
-               base = lmb.memory.region[i].base;
-               size = lmb.memory.region[i].size;
-
-               /* GFP_ATOMIC to avoid might_sleep warnings during boot */
-               kcore_mem = kmalloc(sizeof(struct kcore_list), GFP_ATOMIC);
-               if (!kcore_mem)
-                       panic("%s: kmalloc failed\n", __func__);
-
-               kclist_add(kcore_mem, __va(base), size, KCORE_RAM);
-       }
-
-       return 0;
-}
-module_init(setup_kcore);
-#endif
-
 static void pgd_ctor(void *addr)
 {
        memset(addr, 0, PGD_TABLE_SIZE);
index bf8bd026db55e5004f1dfc9efa49f91fa5bfa7c7..8173e38afd38f4ecbf9e6c7a5bfae805a50cba48 100644 (file)
@@ -186,8 +186,6 @@ void __init paging_init(void)
        set_fixmap_nocache(FIX_UNCACHED, __pa(&__uncached_start));
 }
 
-static struct kcore_list kcore_mem;
-
 void __init mem_init(void)
 {
        int codesize, datasize, initsize;
@@ -226,8 +224,6 @@ void __init mem_init(void)
        datasize =  (unsigned long) &_edata - (unsigned long) &_etext;
        initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 
-       kclist_add(&kcore_mem, __va(0), max_low_pfn << PAGE_SHIFT, KCORE_RAM);
-
        printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, "
               "%dk data, %dk init)\n",
                nr_free_pages() << (PAGE_SHIFT-10),
index 7108678ca9bc3c7af82f8717ffb19daba01b93be..30938c1d8d5d554cd3819887dec0dc31f007a9aa 100644 (file)
@@ -857,8 +857,6 @@ static void __init test_wp_bit(void)
        }
 }
 
-static struct kcore_list kcore_mem;
-
 void __init mem_init(void)
 {
        int codesize, reservedpages, datasize, initsize;
@@ -886,8 +884,6 @@ void __init mem_init(void)
        datasize =  (unsigned long) &_edata - (unsigned long) &_etext;
        initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 
-       kclist_add(&kcore_mem, __va(0), max_low_pfn << PAGE_SHIFT, KCORE_RAM);
-
        printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, "
                        "%dk reserved, %dk data, %dk init, %ldk highmem)\n",
                nr_free_pages() << (PAGE_SHIFT-10),
index a0c2efb10cbef75b1b1d4d1ac0e0479abc784697..d5d23cc240763f4afbcc38da55134b682f507e06 100644 (file)
@@ -647,7 +647,7 @@ EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
 
 #endif /* CONFIG_MEMORY_HOTPLUG */
 
-static struct kcore_list kcore_mem, kcore_modules, kcore_vsyscall;
+static struct kcore_list kcore_modules, kcore_vsyscall;
 
 void __init mem_init(void)
 {
@@ -676,7 +676,6 @@ void __init mem_init(void)
        initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 
        /* Register memory areas for /proc/kcore */
-       kclist_add(&kcore_mem, __va(0), max_low_pfn << PAGE_SHIFT, KCORE_RAM);
        kclist_add(&kcore_modules, (void *)MODULES_VADDR, MODULES_LEN,
                        KCORE_OTHER);
        kclist_add(&kcore_vsyscall, (void *)VSYSCALL_START,
index fdde1cc78392cf92040c357e86271c15f4992459..802de33d634102470004173d6c962d57cdca9bb6 100644 (file)
 #include <linux/elfcore.h>
 #include <linux/vmalloc.h>
 #include <linux/highmem.h>
+#include <linux/bootmem.h>
 #include <linux/init.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <linux/list.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/memory.h>
 #include <asm/sections.h>
 
 #define CORE_STR "CORE"
 
 static struct proc_dir_entry *proc_root_kcore;
 
-static int open_kcore(struct inode * inode, struct file * filp)
-{
-       return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
-}
-
-static ssize_t read_kcore(struct file *, char __user *, size_t, loff_t *);
-
-static const struct file_operations proc_kcore_operations = {
-       .read           = read_kcore,
-       .open           = open_kcore,
-};
 
 #ifndef kc_vaddr_to_offset
 #define        kc_vaddr_to_offset(v) ((v) - PAGE_OFFSET)
@@ -61,6 +54,7 @@ struct memelfnote
 
 static LIST_HEAD(kclist_head);
 static DEFINE_RWLOCK(kclist_lock);
+static int kcore_need_update = 1;
 
 void
 kclist_add(struct kcore_list *new, void *addr, size_t size, int type)
@@ -99,6 +93,126 @@ static size_t get_kcore_size(int *nphdr, size_t *elf_buflen)
        return size + *elf_buflen;
 }
 
+static void free_kclist_ents(struct list_head *head)
+{
+       struct kcore_list *tmp, *pos;
+
+       list_for_each_entry_safe(pos, tmp, head, list) {
+               list_del(&pos->list);
+               kfree(pos);
+       }
+}
+/*
+ * Replace all KCORE_RAM information with passed list.
+ */
+static void __kcore_update_ram(struct list_head *list)
+{
+       struct kcore_list *tmp, *pos;
+       LIST_HEAD(garbage);
+
+       write_lock(&kclist_lock);
+       if (kcore_need_update) {
+               list_for_each_entry_safe(pos, tmp, &kclist_head, list) {
+                       if (pos->type == KCORE_RAM)
+                               list_move(&pos->list, &garbage);
+               }
+               list_splice_tail(list, &kclist_head);
+       } else
+               list_splice(list, &garbage);
+       kcore_need_update = 0;
+       write_unlock(&kclist_lock);
+
+       free_kclist_ents(&garbage);
+}
+
+
+#ifdef CONFIG_HIGHMEM
+/*
+ * If no highmem, we can assume [0...max_low_pfn) continuous range of memory
+ * because memory hole is not as big as !HIGHMEM case.
+ * (HIGHMEM is special because part of memory is _invisible_ from the kernel.)
+ */
+static int kcore_update_ram(void)
+{
+       LIST_HEAD(head);
+       struct kcore_list *ent;
+       int ret = 0;
+
+       ent = kmalloc(sizeof(*ent), GFP_KERNEL);
+       if (!ent)
+               return -ENOMEM;
+       ent->addr = (unsigned long)__va(0);
+       ent->size = max_low_pfn << PAGE_SHIFT;
+       ent->type = KCORE_RAM;
+       list_add(&ent->list, &head);
+       __kcore_update_ram(&head);
+       return ret;
+}
+
+#else /* !CONFIG_HIGHMEM */
+
+static int
+kclist_add_private(unsigned long pfn, unsigned long nr_pages, void *arg)
+{
+       struct list_head *head = (struct list_head *)arg;
+       struct kcore_list *ent;
+
+       ent = kmalloc(sizeof(*ent), GFP_KERNEL);
+       if (!ent)
+               return -ENOMEM;
+       ent->addr = (unsigned long)__va((pfn << PAGE_SHIFT));
+       ent->size = nr_pages << PAGE_SHIFT;
+
+       /* Sanity check: Can happen in 32bit arch...maybe */
+       if (ent->addr < (unsigned long) __va(0))
+               goto free_out;
+
+       /* cut not-mapped area. ....from ppc-32 code. */
+       if (ULONG_MAX - ent->addr < ent->size)
+               ent->size = ULONG_MAX - ent->addr;
+
+       /* cut when vmalloc() area is higher than direct-map area */
+       if (VMALLOC_START > (unsigned long)__va(0)) {
+               if (ent->addr > VMALLOC_START)
+                       goto free_out;
+               if (VMALLOC_START - ent->addr < ent->size)
+                       ent->size = VMALLOC_START - ent->addr;
+       }
+
+       ent->type = KCORE_RAM;
+       list_add_tail(&ent->list, head);
+       return 0;
+free_out:
+       kfree(ent);
+       return 1;
+}
+
+static int kcore_update_ram(void)
+{
+       int nid, ret;
+       unsigned long end_pfn;
+       LIST_HEAD(head);
+
+       /* Not inialized....update now */
+       /* find out "max pfn" */
+       end_pfn = 0;
+       for_each_node_state(nid, N_HIGH_MEMORY) {
+               unsigned long node_end;
+               node_end  = NODE_DATA(nid)->node_start_pfn +
+                       NODE_DATA(nid)->node_spanned_pages;
+               if (end_pfn < node_end)
+                       end_pfn = node_end;
+       }
+       /* scan 0 to max_pfn */
+       ret = walk_system_ram_range(0, end_pfn, &head, kclist_add_private);
+       if (ret) {
+               free_kclist_ents(&head);
+               return -ENOMEM;
+       }
+       __kcore_update_ram(&head);
+       return ret;
+}
+#endif /* CONFIG_HIGHMEM */
 
 /*****************************************************************************/
 /*
@@ -373,6 +487,39 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
        return acc;
 }
 
+
+static int open_kcore(struct inode *inode, struct file *filp)
+{
+       if (!capable(CAP_SYS_RAWIO))
+               return -EPERM;
+       if (kcore_need_update)
+               kcore_update_ram();
+       return 0;
+}
+
+
+static const struct file_operations proc_kcore_operations = {
+       .read           = read_kcore,
+       .open           = open_kcore,
+};
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+/* just remember that we have to update kcore */
+static int __meminit kcore_callback(struct notifier_block *self,
+                                   unsigned long action, void *arg)
+{
+       switch (action) {
+       case MEM_ONLINE:
+       case MEM_OFFLINE:
+               write_lock(&kclist_lock);
+               kcore_need_update = 1;
+               write_unlock(&kclist_lock);
+       }
+       return NOTIFY_OK;
+}
+#endif
+
+
 static struct kcore_list kcore_vmalloc;
 
 #ifdef CONFIG_ARCH_PROC_KCORE_TEXT
@@ -393,10 +540,18 @@ static void __init proc_kcore_text_init(void)
 
 static int __init proc_kcore_init(void)
 {
-       proc_root_kcore = proc_create("kcore", S_IRUSR, NULL, &proc_kcore_operations);
+       proc_root_kcore = proc_create("kcore", S_IRUSR, NULL,
+                                     &proc_kcore_operations);
+       /* Store text area if it's special */
        proc_kcore_text_init();
+       /* Store vmalloc area */
        kclist_add(&kcore_vmalloc, (void *)VMALLOC_START,
                VMALLOC_END - VMALLOC_START, KCORE_VMALLOC);
+       /* Store direct-map area from physical memory map */
+       kcore_update_ram();
+       hotplug_memory_notifier(kcore_callback, 0);
+       /* Other special area, area-for-module etc is arch specific. */
+
        return 0;
 }
 module_init(proc_kcore_init);