[PATCH 4/4 -mm] kexec based hibernation -v7 : kimgcore

Huang, Ying ying.huang at intel.com
Fri Dec 7 10:53:44 EST 2007


This patch adds a file in proc file system to access the loaded
kexec_image, which may contains the memory image of kexeced
system. This can be used by kexec based hibernation to create a file
image of hibernating kernel, so that a kernel booting process is not
needed for each hibernating.

Signed-off-by: Huang Ying <ying.huang at intel.com>

---
 fs/proc/Makefile      |    1 
 fs/proc/kimgcore.c    |  204 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/proc/proc_misc.c   |    5 +
 include/linux/kexec.h |    7 +
 kernel/kexec.c        |    5 -
 5 files changed, 217 insertions(+), 5 deletions(-)

--- /dev/null
+++ b/fs/proc/kimgcore.c
@@ -0,0 +1,204 @@
+/*
+ * fs/proc/kimgcore.c - Interface for accessing the loaded
+ * kexec_image, which may contains the memory image of kexeced system.
+ * Heavily borrowed from fs/proc/kcore.c
+ *
+ * Copyright (C) 2007, Intel Corp.
+ *      Huang Ying <ying.huang at intel.com>
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/user.h>
+#include <linux/elf.h>
+#include <linux/init.h>
+#include <linux/kexec.h>
+#include <linux/io.h>
+#include <linux/highmem.h>
+#include <asm/uaccess.h>
+
+struct proc_dir_entry *proc_root_kimgcore;
+
+static u32 kimgcore_size;
+
+static char *elfcorebuf;
+static size_t elfcorebuf_sz;
+
+static void *buf_page;
+
+static ssize_t kimage_copy_to_user(struct kimage *image, char __user *buf,
+				  unsigned long offset, size_t count)
+{
+	kimage_entry_t *ptr, entry;
+	unsigned long off = 0, offinp, trunk;
+	struct page *page;
+	void *vaddr;
+
+	for_each_kimage_entry(image, ptr, entry) {
+		if (!(entry & IND_SOURCE))
+			continue;
+		if (off + PAGE_SIZE > offset) {
+			offinp = offset - off;
+			if (count > PAGE_SIZE - offinp)
+				trunk = PAGE_SIZE - offinp;
+			else
+				trunk = count;
+			page = pfn_to_page(entry >> PAGE_SHIFT);
+			if (PageHighMem(page)) {
+				vaddr = kmap(page);
+				memcpy(buf_page, vaddr+offinp, trunk);
+				kunmap(page);
+				vaddr = buf_page;
+			} else
+				vaddr = __va(entry & PAGE_MASK) + offinp;
+			if (copy_to_user(buf, vaddr, trunk))
+				return -EFAULT;
+			buf += trunk;
+			offset += trunk;
+			count -= trunk;
+			if (!count)
+				break;
+		}
+		off += PAGE_SIZE;
+	}
+	return count;
+}
+
+static ssize_t read_kimgcore(struct file *file, char __user *buffer,
+			     size_t buflen, loff_t *fpos)
+{
+	size_t acc = 0;
+	size_t tsz;
+	ssize_t ssz;
+
+	if (buflen == 0 || *fpos >= kimgcore_size)
+		return 0;
+
+	/* trim buflen to not go beyond EOF */
+	if (buflen > kimgcore_size - *fpos)
+		buflen = kimgcore_size - *fpos;
+	/* Read ELF core header */
+	if (*fpos < elfcorebuf_sz) {
+		tsz = elfcorebuf_sz - *fpos;
+		if (buflen < tsz)
+			tsz = buflen;
+		if (copy_to_user(buffer, elfcorebuf + *fpos, tsz))
+			return -EFAULT;
+		buflen -= tsz;
+		*fpos += tsz;
+		buffer += tsz;
+		acc += tsz;
+
+		/* leave now if filled buffer already */
+		if (buflen == 0)
+			return acc;
+	}
+
+	ssz = kimage_copy_to_user(kexec_image, buffer,
+				  *fpos - elfcorebuf_sz, buflen);
+	if (ssz < 0)
+		return ssz;
+
+	*fpos += (buflen - ssz);
+	acc += (buflen - ssz);
+
+	return acc;
+}
+
+static int init_kimgcore(void)
+{
+	Elf64_Ehdr *ehdr;
+	Elf64_Phdr *phdr;
+	struct kexec_segment *seg;
+	Elf64_Off off;
+	unsigned long i;
+
+	elfcorebuf_sz = sizeof(Elf64_Ehdr) +
+		kexec_image->nr_segments * sizeof(Elf64_Phdr);
+	elfcorebuf = kzalloc(elfcorebuf_sz, GFP_KERNEL);
+	if (!elfcorebuf)
+		return -ENOMEM;
+	ehdr = (Elf64_Ehdr *)elfcorebuf;
+	memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+	ehdr->e_ident[EI_CLASS] = ELFCLASS64;
+	ehdr->e_ident[EI_DATA]	= ELFDATA2LSB;
+	ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+	ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
+	memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+	ehdr->e_type = ET_CORE;
+	ehdr->e_machine = ELF_ARCH;
+	ehdr->e_version = EV_CURRENT;
+	ehdr->e_entry = kexec_image->start;
+	ehdr->e_phoff = sizeof(Elf64_Ehdr);
+	ehdr->e_shoff = 0;
+	ehdr->e_flags = 0;
+	ehdr->e_ehsize = sizeof(Elf64_Ehdr);
+	ehdr->e_phentsize = sizeof(Elf64_Phdr);
+	ehdr->e_phnum = kexec_image->nr_segments;
+	ehdr->e_shentsize = 0;
+	ehdr->e_shnum = 0;
+	ehdr->e_shstrndx = 0;
+
+	off = elfcorebuf_sz;
+	phdr = (Elf64_Phdr *)(elfcorebuf + sizeof(Elf64_Ehdr));
+	seg = kexec_image->segment;
+	for (i = 0; i < kexec_image->nr_segments; i++, phdr++, seg++) {
+		phdr->p_type = PT_LOAD;
+		phdr->p_flags = PF_R|PF_W|PF_X;
+		phdr->p_offset = off;
+		phdr->p_paddr = seg->mem;
+		phdr->p_filesz = seg->memsz;
+		phdr->p_memsz = seg->memsz;
+		phdr->p_align = PAGE_SIZE;
+		off += seg->memsz;
+	}
+	kimgcore_size = off;
+
+	buf_page = (void *)__get_free_page(GFP_KERNEL);
+	if (!buf_page) {
+		kfree(elfcorebuf);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void destroy_kimgcore(void)
+{
+	kfree(elfcorebuf);
+	free_page((unsigned long)buf_page);
+	elfcorebuf_sz = 0;
+	kimgcore_size = 0;
+}
+
+static int open_kimgcore(struct inode *inode, struct file *filp)
+{
+	int ret;
+	if (xchg(&kexec_lock, 1))
+		return -EBUSY;
+	if (!kexec_image) {
+		ret = -ENOENT;
+		goto unlock;
+	}
+	ret = init_kimgcore();
+	if (ret)
+		goto unlock;
+	return 0;
+unlock:
+	xchg(&kexec_lock, 0);
+	return ret;
+}
+
+static int release_kimgcore(struct inode *inode, struct file *filp)
+{
+	destroy_kimgcore();
+	xchg(&kexec_lock, 0);
+	return 0;
+}
+
+const struct file_operations proc_kimgcore_operations = {
+	.read = read_kimgcore,
+	.open = open_kimgcore,
+	.release = release_kimgcore,
+};
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -10,6 +10,7 @@
 #include <linux/elfcore.h>
 #include <linux/elf.h>
 #include <linux/notifier.h>
+#include <linux/proc_fs.h>
 #include <asm/kexec.h>
 
 /* Verify architecture specific macros are defined */
@@ -108,6 +109,10 @@ struct kimage {
 };
 
 
+#define for_each_kimage_entry(image, ptr, entry) \
+	for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
+		ptr = (entry & IND_INDIRECTION)? \
+			phys_to_virt((entry & PAGE_MASK)): ptr + 1)
 
 /* kexec interface functions */
 extern void machine_kexec(struct kimage *image);
@@ -228,6 +233,8 @@ extern size_t vmcoreinfo_max_size;
 int __init parse_crashkernel(char *cmdline, unsigned long long system_ram,
 		unsigned long long *crash_size, unsigned long long *crash_base);
 
+extern const struct file_operations proc_kimgcore_operations;
+extern struct proc_dir_entry *proc_root_kimgcore;
 #else /* !CONFIG_KEXEC */
 struct pt_regs;
 struct task_struct;
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -14,5 +14,6 @@ proc-$(CONFIG_PROC_SYSCTL)	+= proc_sysct
 proc-$(CONFIG_NET)		+= proc_net.o
 proc-$(CONFIG_PROC_KCORE)	+= kcore.o
 proc-$(CONFIG_PROC_VMCORE)	+= vmcore.o
+proc-$(CONFIG_KEXEC)		+= kimgcore.o
 proc-$(CONFIG_PROC_DEVICETREE)	+= proc_devtree.o
 proc-$(CONFIG_PRINTK)	+= kmsg.o
--- a/fs/proc/proc_misc.c
+++ b/fs/proc/proc_misc.c
@@ -1034,6 +1034,11 @@ void __init proc_misc_init(void)
 	if (proc_vmcore)
 		proc_vmcore->proc_fops = &proc_vmcore_operations;
 #endif
+#ifdef CONFIG_KEXEC
+	proc_root_kimgcore = create_proc_entry("kimgcore", S_IRUSR, NULL);
+	if (proc_root_kimgcore)
+		proc_root_kimgcore->proc_fops = &proc_kimgcore_operations;
+#endif
 #ifdef CONFIG_MAGIC_SYSRQ
 	{
 		struct proc_dir_entry *entry;
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -614,11 +614,6 @@ static int kimage_terminate(struct kimag
 	return 0;
 }
 
-#define for_each_kimage_entry(image, ptr, entry) \
-	for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
-		ptr = (entry & IND_INDIRECTION)? \
-			phys_to_virt((entry & PAGE_MASK)): ptr +1)
-
 static void kimage_free_entry(kimage_entry_t entry)
 {
 	struct page *page;



More information about the kexec mailing list