[PATCH] /proc/cpumem

From: Itsuro Oda
Date: Wed Feb 16 2005 - 03:54:42 EST


Hi, Eric and all

Attached is an implementation of /proc/cpumem.
/proc/cpumem shows the valid physical memory ranges.

* i386 and x86_64
* implement valid_phys_addr_range() and use it.
(the first argument of the i386 version is little uncomfortable.)
* /dev/mem of the i386 version should be mofified. but not yet.

example: amd64 8GB Mem
# cat /proc/cpumem
0000000000000000 000000000009b800
0000000000100000 00000000fbe70000
0000000100000000 0000000100000000
#
start address and size. hex digit.

Any comments, recomendations and suggestions are welcom.

BTW, does not kexec/kdump run on 2.6.11-rc3-mm2 ?
How do I get and examine the latest kexec/kdump ?

Thanks.
--
Itsuro ODA <oda@xxxxxxxxxxxxx>

---
--- linux-2.6.11-rc3-mm2/drivers/char/mem.c 2005-02-16 15:36:31.000000000 +0900
+++ linux-2.6.11-rc3-mm2-test/drivers/char/mem.c 2005-02-16 23:32:15.244876816 +0900
@@ -25,6 +25,9 @@
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/crash_dump.h>
+#include <linux/bootmem.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>

#include <asm/uaccess.h>
#include <asm/io.h>
@@ -759,3 +762,125 @@
}

fs_initcall(chr_dev_init);
+
+#ifdef CONFIG_PROC_FS
+/*
+ * /proc/cpumem: show valid physical address range
+ */
+struct cpumem_info {
+ unsigned long long addr;
+ unsigned long long size;
+};
+
+static void *cpumem_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct cpumem_info *p = m->private;
+ unsigned long long end = (unsigned long long)max_pfn << PAGE_SHIFT;
+ unsigned long long addr;
+ size_t size;
+ int found = 0;
+
+ (*pos)++;
+
+ if (p->addr >= end) {
+ return NULL;
+ }
+
+ /* always start page boundary */
+ addr = ((p->addr + p->size + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT;
+ size = 0xf0000000;
+
+ while (addr < end) {
+ if (valid_phys_addr_range(addr, &size)) {
+ if (!found) {
+ found = 1;
+ p->addr = addr;
+ p->size = size;
+ } else {
+ p->size += size;
+ }
+ addr += size;
+ size = 0xf0000000;
+ } else {
+ if (found) {
+ return p;
+ }
+ addr += PAGE_SIZE;
+ }
+ }
+
+ return found ? p : NULL;
+}
+
+static void *cpumem_start(struct seq_file *m, loff_t *pos)
+{
+ struct cpumem_info *p = m->private;
+ loff_t n = 0;
+
+ p->addr = 0;
+ p->size = 0;
+
+ while (n <= *pos) {
+ if (!cpumem_next(m, NULL, &n)) {
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+static void cpumem_stop(struct seq_file *m, void *v)
+{
+}
+
+static int cpumem_show(struct seq_file *m, void *v)
+{
+ struct cpumem_info *p = m->private;
+ unsigned long long end = (unsigned long long)max_pfn << PAGE_SHIFT;
+
+ if (p->addr < end) {
+ seq_printf(m, "%016llx %016llx\n", p->addr, p->size);
+ }
+ return 0;
+}
+
+struct seq_operations cpumem_op = {
+ .start = cpumem_start,
+ .next = cpumem_next,
+ .stop = cpumem_stop,
+ .show = cpumem_show
+};
+
+static int cpumem_open(struct inode *inode, struct file *file)
+{
+ int res = seq_open(file, &cpumem_op);
+ if (!res) {
+ struct seq_file *m = file->private_data;
+ m->private = kmalloc(sizeof(struct cpumem_info), GFP_KERNEL);
+ if (!m->private) {
+ seq_release(inode, file);
+ return -ENOMEM;
+ }
+ }
+ return res;
+}
+
+static struct file_operations proc_cpumem_operations = {
+ .open = cpumem_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private
+};
+
+static int __init cpumem_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ entry = create_proc_entry("cpumem", 0, NULL);
+ if (entry) {
+ entry->proc_fops = &proc_cpumem_operations;
+ }
+ return 0;
+}
+__initcall(cpumem_init);
+#endif /* CONFIG_PROC_FS */

---
--- linux-2.6.11-rc3-mm2/arch/i386/mm/init.c 2005-02-16 15:36:29.000000000 +0900
+++ linux-2.6.11-rc3-mm2-test/arch/i386/mm/init.c 2005-02-16 23:32:29.499709752 +0900
@@ -248,6 +248,47 @@
return 0;
}

+int valid_phys_addr_range(unsigned long long phys_addr, size_t *size)
+{
+ int i;
+ unsigned long long addr, end;
+ efi_memory_desc_t *md;
+
+ if (efi_enabled) {
+ for (i = 0; i < memmap.nr_map; i++) {
+ md = &memmap.map[i];
+ if (!is_available_memory(md)) {
+ continue;
+ }
+ addr = md->phys_addr;
+ end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT);
+ if ((phys_addr >= addr) && (phys_addr < end)) {
+ if (*size > end - phys_addr) {
+ *size = end - phys_addr;
+ }
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ for (i = 0; i < e820.nr_map; i++) {
+ if (e820.map[i].type != E820_RAM) {
+ continue;
+ }
+ addr = e820.map[i].addr;
+ end = e820.map[i].addr + e820.map[i].size;
+ if ((phys_addr >= addr) && (phys_addr < end)) {
+ if (*size > end - phys_addr) {
+ *size = end - phys_addr;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(valid_phys_addr_range);
+
#ifdef CONFIG_HIGHMEM
pte_t *kmap_pte;
pgprot_t kmap_prot;

---
--- linux-2.6.11-rc3-mm2/include/asm-i386/io.h 2004-12-25 06:35:40.000000000 +0900
+++ linux-2.6.11-rc3-mm2-test/include/asm-i386/io.h 2005-02-16 23:36:24.454991120 +0900
@@ -90,6 +90,12 @@
*/
#define page_to_phys(page) ((dma_addr_t)page_to_pfn(page) << PAGE_SHIFT)

+/*
+ * for /dev/mem
+ */
+#define ARCH_HAS_VALID_PHYS_ADDR_RANGE
+extern int valid_phys_addr_range(unsigned long long, size_t *);
+
extern void __iomem * __ioremap(unsigned long offset, unsigned long size, unsigned long flags);

/**

---
--- linux-2.6.11-rc3-mm2/arch/x86_64/mm/init.c 2005-02-16 15:36:30.000000000 +0900
+++ linux-2.6.11-rc3-mm2-test/arch/x86_64/mm/init.c 2005-02-16 16:23:08.000000000 +0900
@@ -22,6 +22,7 @@
#include <linux/pagemap.h>
#include <linux/bootmem.h>
#include <linux/proc_fs.h>
+#include <linux/module.h>

#include <asm/processor.h>
#include <asm/system.h>
@@ -395,6 +396,27 @@
return 0;
}

+int valid_phys_addr_range(unsigned long phys_addr, size_t *size)
+{
+ int i;
+ unsigned long end;
+
+ for (i = 0; i < e820.nr_map; i++) {
+ if (e820.map[i].type != E820_RAM) {
+ continue;
+ }
+ end = e820.map[i].addr + e820.map[i].size;
+ if (phys_addr >= e820.map[i].addr && phys_addr < end) {
+ if (*size > end - phys_addr) {
+ *size = end - phys_addr;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(valid_phys_addr_range);
+
extern int swiotlb_force;

/*

---
--- linux-2.6.11-rc3-mm2/include/asm-x86_64/io.h 2005-02-16 15:36:12.000000000 +0900
+++ linux-2.6.11-rc3-mm2-test/include/asm-x86_64/io.h 2005-02-16 16:23:59.000000000 +0900
@@ -123,6 +123,9 @@
{
return __va(address);
}
+
+#define ARCH_HAS_VALID_PHYS_ADDR_RANGE
+extern int valid_phys_addr_range(unsigned long, size_t *);
#endif

/*

---

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/