Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Jones <davej@suse.de>2002-02-25 19:35:18 -0800
committerLinus Torvalds <torvalds@penguin.transmeta.com>2002-02-25 19:35:18 -0800
commita3245879f664fb42b1903bc98af670da6d783db5 (patch)
tree30090d0524db4b07e85569f8fbf8b22c8cad6bca
parent16d6f85007ec524568db6ad9bbf7b44e3b1cd365 (diff)
[PATCH] Support /dev/kmem access to vmalloc space (Marc Boucher)
From 2.4.17
-rw-r--r--drivers/char/mem.c46
-rw-r--r--mm/vmalloc.c39
2 files changed, 80 insertions, 5 deletions
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 9df1b5af4d43..92368fb0ff98 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -273,6 +273,8 @@ static ssize_t read_kmem(struct file *file, char *buf,
return virtr + read;
}
+extern long vwrite(char *buf, char *addr, unsigned long count);
+
/*
* This function writes to the *virtual* memory as seen by the kernel.
*/
@@ -280,12 +282,46 @@ static ssize_t write_kmem(struct file * file, const char * buf,
size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
+ ssize_t wrote = 0;
+ ssize_t virtr = 0;
+ char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
- if (p >= (unsigned long) high_memory)
- return 0;
- if (count > (unsigned long) high_memory - p)
- count = (unsigned long) high_memory - p;
- return do_write_mem(file, (void*)p, p, buf, count, ppos);
+ if (p < (unsigned long) high_memory) {
+ wrote = count;
+ if (count > (unsigned long) high_memory - p)
+ wrote = (unsigned long) high_memory - p;
+
+ wrote = do_write_mem(file, (void*)p, p, buf, wrote, ppos);
+
+ p += wrote;
+ buf += wrote;
+ count -= wrote;
+ }
+
+ if (count > 0) {
+ kbuf = (char *)__get_free_page(GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+ while (count > 0) {
+ int len = count;
+
+ if (len > PAGE_SIZE)
+ len = PAGE_SIZE;
+ if (len && copy_from_user(kbuf, buf, len)) {
+ free_page((unsigned long)kbuf);
+ return -EFAULT;
+ }
+ len = vwrite(kbuf, (char *)p, len);
+ count -= len;
+ buf += len;
+ virtr += len;
+ p += len;
+ }
+ free_page((unsigned long)kbuf);
+ }
+
+ *ppos = p;
+ return virtr + wrote;
}
#if !defined(__mc68000__)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 7f582486a66c..0b08f4a24456 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -6,6 +6,7 @@
* SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian <tigran@veritas.com>, May 2000
*/
+#include <linux/config.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/spinlock.h>
@@ -163,6 +164,7 @@ inline int vmalloc_area_pages (unsigned long address, unsigned long size,
ret = 0;
} while (address && (address < end));
spin_unlock(&init_mm.page_table_lock);
+ flush_cache_all();
return ret;
}
@@ -282,3 +284,40 @@ finished:
read_unlock(&vmlist_lock);
return buf - buf_start;
}
+
+long vwrite(char *buf, char *addr, unsigned long count)
+{
+ struct vm_struct *tmp;
+ char *vaddr, *buf_start = buf;
+ unsigned long n;
+
+ /* Don't allow overflow */
+ if ((unsigned long) addr + count < count)
+ count = -(unsigned long) addr;
+
+ read_lock(&vmlist_lock);
+ for (tmp = vmlist; tmp; tmp = tmp->next) {
+ vaddr = (char *) tmp->addr;
+ if (addr >= vaddr + tmp->size - PAGE_SIZE)
+ continue;
+ while (addr < vaddr) {
+ if (count == 0)
+ goto finished;
+ buf++;
+ addr++;
+ count--;
+ }
+ n = vaddr + tmp->size - PAGE_SIZE - addr;
+ do {
+ if (count == 0)
+ goto finished;
+ *addr = *buf;
+ buf++;
+ addr++;
+ count--;
+ } while (--n > 0);
+ }
+finished:
+ read_unlock(&vmlist_lock);
+ return buf - buf_start;
+}