]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Support /dev/kmem access to vmalloc space (Marc Boucher)
authorDave Jones <davej@suse.de>
Tue, 26 Feb 2002 03:35:18 +0000 (19:35 -0800)
committerLinus Torvalds <torvalds@penguin.transmeta.com>
Tue, 26 Feb 2002 03:35:18 +0000 (19:35 -0800)
From 2.4.17

drivers/char/mem.c
mm/vmalloc.c

index 9df1b5af4d43ccb04e8ada7cc5282554df590b5e..92368fb0ff988c7f57c034d2f9623f829e2f5c57 100644 (file)
@@ -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__)
index 7f582486a66c6e54ba57685b656fadfdea637ebf..0b08f4a2445656eceaa0f43bcf7b02558afbd520 100644 (file)
@@ -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;
+}