support section mmap, update test
authornotaz <notasas@gmail.com>
Mon, 26 Nov 2012 17:05:00 +0000 (19:05 +0200)
committernotaz <notasas@gmail.com>
Mon, 26 Nov 2012 19:21:33 +0000 (21:21 +0200)
module/warm_main.c
test.c
warm.c
warm.h

index 18e9762..3b3a148 100644 (file)
@@ -337,6 +337,81 @@ static int do_cache_ops(int ops, u32 addr, u32 size)
        return 0;
 }
 
+static int do_map_op(u32 vaddr, u32 paddr, u32 size, int cb, int is_unmap)
+{
+       int count = 0, retval = 0;
+       u32 pstart, start, end;
+       u32 desc1, apcb_bits;
+       u32 *pgtable;
+       u32 v, mask;
+
+       apcb_bits = (3 << 10) | (1 << 5); /* r/w, dom 1 */
+       if (cb & WCB_C_BIT)
+               apcb_bits |= 8;
+       if (cb & WCB_B_BIT)
+               apcb_bits |= 4;
+       // spinlock
+
+       mask = SECTION_SIZE - 1;
+       size = (size + mask) & ~mask;
+
+       pstart = paddr;
+       start = vaddr;
+       end = start + size;
+
+       /* check for overflows */
+       if (end - 1 < start)
+               return -EINVAL;
+       if (pstart + size - 1 < pstart)
+               return -EINVAL;
+
+       pgtable = get_pgtable();
+
+       for (; vaddr < end; vaddr += SECTION_SIZE, paddr += SECTION_SIZE)
+       {
+               desc1 = pgtable[vaddr >> 20];
+
+               if (is_unmap) {
+                       if ((desc1 & 3) != 2) {
+                               printk(KERN_WARNING PFX "vaddr %08x is not a section? (%08x)\n",
+                                               vaddr, desc1);
+                               return -EINVAL;
+                       }
+                       v = 0;
+               } else {
+                       if ((desc1 & 3) != 0) {
+                               printk(KERN_WARNING PFX "vaddr %08x already mapped? (%08x)\n",
+                                               vaddr, desc1);
+                               retval = -EINVAL;
+                               break;
+                       }
+                       v = (paddr & ~mask) | apcb_bits | 0x12;
+               }
+
+               pgtable[vaddr >> 20] = v;
+               count++;
+       }
+
+       if (retval != 0) {
+               /* undo mappings */
+               vaddr = start;
+
+               for (; vaddr < end && count > 0; vaddr += SECTION_SIZE, count--)
+                       pgtable[vaddr >> 20] = 0;
+       }
+
+       warm_cop_clean_d();
+       warm_drain_wb_inval_tlb();
+
+       if (retval == 0 && !is_unmap) {
+               WARM_INFO("mapped %08x to %08x with %c%c bit(s) (%d section(s))\n",
+                       start, pstart, apcb_bits & 8 ? 'c' : ' ',
+                       apcb_bits & 4 ? 'b' : ' ', count);
+       }
+
+       return retval;
+}
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
 static long warm_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
 #else
@@ -348,6 +423,7 @@ static int warm_ioctl(struct inode *inode, struct file *file,
        union {
                struct warm_cache_op wcop;
                struct warm_change_cb ccb;
+               struct warm_map_op mop;
                unsigned long addr;
        } u;
        long ret;
@@ -381,6 +457,14 @@ static int warm_ioctl(struct inode *inode, struct file *file,
                if (copy_to_user(arg, &u.addr, sizeof(u.addr)))
                        return -EFAULT;
                break;
+       case WARMC_MMAP:
+               if (copy_from_user(&u.mop, arg, sizeof(u.mop)))
+                       return -EFAULT;
+               if (u.mop.cb & ~(WCB_C_BIT|WCB_B_BIT))
+                       return -EINVAL;
+               ret = do_map_op(u.mop.virt_addr, u.mop.phys_addr, u.mop.size,
+                       u.mop.cb, u.mop.is_unmap);
+               break;
        default:
                ret = -ENOTTY;
                break;
diff --git a/test.c b/test.c
index 55dfe7b..8a9fd90 100644 (file)
--- a/test.c
+++ b/test.c
 #include <sys/time.h>
 #include <time.h>
 #include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/fb.h>
 
 #if 1
 #include "warm.h"
 typedef unsigned long long u64;
 static u64 start_time, end_time;
 
-static unsigned char buff[8 * 1024 * 1024] __attribute__((aligned(32)));
-static unsigned char *buff_mid = &buff[8 * 1024 * 1024 / 2];
+static unsigned char buff[2 * 1024 * 1024] __attribute__((aligned(32)));
+static unsigned char *buff_mid = &buff[sizeof(buff) / 2];
+
+#define REP 32
 
 static u64 xtime(void)
 {
@@ -71,6 +79,18 @@ static void word_fill(void)
                *p++ = 0;
 }
 
+static void cached_byte_fill(void)
+{
+       char *p = (void *)buff;
+       int i, j, v;
+
+       for (i = sizeof(buff) / 32; i > 0; i--) {
+               v += *p;
+               for (j = 32; j > 0; j--)
+                       *p++ = v;
+       }
+}
+
 static void do_memcpy(void)
 {
        memcpy(buff, buff_mid, sizeof(buff) / 2);
@@ -105,40 +125,96 @@ static void word_inc(void)
        }
 }
 
+#define TEST_PAGE 4096
+
+static void page_writes_ref(void *buf)
+{
+       long *d = buf;
+       int i, j;
+
+       for (j = 0; j < 0x100000 / TEST_PAGE; j++)
+               for (i = 0; i < TEST_PAGE / 4; i++)
+                       d[j * TEST_PAGE / 4 + i] = 0;
+}
+
+static void page_inc_ref(void *buf)
+{
+       long *d = buf;
+       int i, j;
+
+       for (j = 0; j < 0x100000 / TEST_PAGE; j++)
+               for (i = 0; i < TEST_PAGE / 4; i++)
+                       d[j * TEST_PAGE / 4 + i]++;
+}
+
+static void page_writes(void *buf)
+{
+       long *d = buf;
+       int i, j;
+
+       for (i = 0; i < TEST_PAGE / 4; i++)
+               for (j = 0; j < 0x100000 / TEST_PAGE; j++)
+                       d[j * TEST_PAGE / 4 + i] = 0;
+}
+
+static void page_inc(void *buf)
+{
+       long *d = buf;
+       int i, j;
+
+       for (i = 0; i < TEST_PAGE / 4; i++)
+               for (j = 0; j < 0x100000 / TEST_PAGE; j++)
+                       d[j * TEST_PAGE / 4 + i]++;
+}
+
 #define ONE_TEST(count, func) \
        test_start(); \
        for (i = count; i > 0; i--) \
-               func(); \
+               func; \
        test_end()
 
 static void tests(void)
 {
        int i;
 
-       ONE_TEST(64, do_memset);
-       show_result("memset", sizeof(buff) * 64);
+       ONE_TEST(REP, do_memset());
+       show_result("memset", sizeof(buff) * REP);
+
+       ONE_TEST(REP, byte_fill());
+       show_result("byte fill", sizeof(buff) * REP);
 
-       ONE_TEST(64, byte_fill);
-       show_result("byte fill", sizeof(buff) * 64);
+       ONE_TEST(REP, word_fill());
+       show_result("word fill", sizeof(buff) * REP);
 
-       ONE_TEST(64, word_fill);
-       show_result("word fill", sizeof(buff) * 64);
+       ONE_TEST(REP, cached_byte_fill());
+       show_result("c. byte fill", sizeof(buff) * REP);
 
-       ONE_TEST(128, do_memcpy);
-       show_result("memcpy", sizeof(buff) * 128 / 2);
+       ONE_TEST(REP * 2, do_memcpy());
+       show_result("memcpy", sizeof(buff) * REP);
 
-       ONE_TEST(128, byte_cpy);
-       show_result("byte copy", sizeof(buff) * 128 / 2);
+       ONE_TEST(REP * 2, byte_cpy());
+       show_result("byte copy", sizeof(buff) * REP);
 
-       ONE_TEST(128, word_cpy);
-       show_result("word copy", sizeof(buff) * 128 / 2);
+       ONE_TEST(REP * 2, word_cpy());
+       show_result("word copy", sizeof(buff) * REP);
 
-       ONE_TEST(64, word_inc);
-       show_result("word inc", sizeof(buff) * 64);
+       ONE_TEST(REP, word_inc());
+       show_result("word inc", sizeof(buff) * REP);
 
        usleep(200000);
 }
 
+static void page_tests(void *buf)
+{
+       int i;
+
+       ONE_TEST(REP, page_writes(buf));
+       show_result("page_writes", 0x100000 * REP);
+
+       ONE_TEST(REP, page_inc(buf));
+       show_result("page_inc", 0x100000 * REP);
+}
+
 #if 1
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -225,7 +301,11 @@ void coherency_test(void)
 
 int main()
 {
-       int ret;
+       struct fb_fix_screeninfo fbfix;
+       void *mmap_mem;
+       void *section_mem;
+       int fbdev;
+       int i, ret;
 
        ret = warm_init();
        if (ret != 0)
@@ -236,6 +316,9 @@ int main()
 
        printf("buff: %p - %p\n", buff, buff + sizeof(buff) - 1);
 
+       // prefault
+       do_memset();
+
        printf("-- default --\n");
        tests();
 
@@ -252,9 +335,75 @@ int main()
        warm_change_cb_range(WCB_B_BIT, 0, buff, sizeof(buff));
        tests();
 
+       //printf("--  c b --\n");
        warm_change_cb_range(WCB_C_BIT|WCB_B_BIT, 1, buff, sizeof(buff));
+       //tests();
+
        coherency_test();
 
+       mmap_mem = mmap((void *)0x60000000, 0x100000, PROT_READ | PROT_WRITE,
+                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+       // find safe location for section map, we'll use fb
+       fbdev = open("/dev/fb0", O_RDWR);
+       if (fbdev == -1) {
+               perror("fb open");
+               goto out;
+       }
+
+       ret = ioctl(fbdev, FBIOGET_FSCREENINFO, &fbfix);
+       if (ret == -1) {
+               perror("ioctl(fbdev)");
+               goto out;
+       }
+
+       section_mem = (void *)0x70000000;
+       ret = warm_mmap_section(section_mem, fbfix.smem_start, 0x100000,
+                               WCB_C_BIT|WCB_B_BIT);
+       if (ret != 0) {
+               fprintf(stderr, "section map failed\n");
+               goto out;
+       }
+
+       // prefault
+       memset(mmap_mem, 0, 0x100000);
+       memset(section_mem, 0, 0x100000);
+
+       ONE_TEST(REP, page_writes_ref(mmap_mem));
+       show_result("page_wr_ref_m", 0x100000 * REP);
+
+       ONE_TEST(REP, page_inc_ref(mmap_mem));
+       show_result("page_inc_ref_m", 0x100000 * REP);
+
+       ONE_TEST(REP, page_writes_ref(section_mem));
+       show_result("page_wr_ref_s", 0x100000 * REP);
+
+       ONE_TEST(REP, page_inc_ref(section_mem));
+       show_result("page_inc_ref_s", 0x100000 * REP);
+
+       printf("== pages ==\n");
+       page_tests(mmap_mem);
+
+       printf("== section ==\n");
+       printf("-- default --\n");
+       page_tests(section_mem);
+
+       printf("-- ncnb --\n");
+       warm_change_cb_range(WCB_C_BIT|WCB_B_BIT, 0, section_mem, 0x100000);
+       page_tests(section_mem);
+
+       printf("-- nc b --\n");
+       warm_change_cb_range(WCB_B_BIT, 1, section_mem, 0x100000);
+       page_tests(section_mem);
+
+       printf("--  cnb --\n");
+       warm_change_cb_range(WCB_C_BIT, 1, section_mem, 0x100000);
+       warm_change_cb_range(WCB_B_BIT, 0, section_mem, 0x100000);
+       page_tests(section_mem);
+
+       warm_munmap_section(section_mem, 0x100000);
+
+out:
        warm_finish();
 
        return 0;
diff --git a/warm.c b/warm.c
index c79c63e..d17b122 100644 (file)
--- a/warm.c
+++ b/warm.c
@@ -205,7 +205,7 @@ int warm_cache_op_range(int op, void *addr, unsigned long size)
 
        ret = ioctl(warm_fd, WARMC_CACHE_OP, &wop);
        if (ret != 0) {
-               perror("WARMC_CACHE_OP failed");
+               perror(PFX "WARMC_CACHE_OP failed");
                return -1;
        }
 
@@ -232,7 +232,7 @@ int warm_change_cb_range(int cb, int is_set, void *addr, unsigned long size)
 
        ret = ioctl(warm_fd, WARMC_CHANGE_CB, &ccb);
        if (ret != 0) {
-               perror("WARMC_CHANGE_CB failed");
+               perror(PFX "WARMC_CHANGE_CB failed");
                return -1;
        }
 
@@ -252,10 +252,55 @@ unsigned long warm_virt2phys(const void *ptr)
        ptrio = (unsigned long)ptr;
        ret = ioctl(warm_fd, WARMC_VIRT2PHYS, &ptrio);
        if (ret != 0) {
-               perror("WARMC_VIRT2PHYS failed");
+               perror(PFX "WARMC_VIRT2PHYS failed");
                return (unsigned long)-1;
        }
 
        return ptrio;
 }
 
+int warm_do_section(void *virt_addr, unsigned long phys_addr,
+       unsigned long size, int cb, int is_unmap)
+{
+       struct warm_map_op mop;
+       unsigned long vaddr;
+       int ret;
+
+       if (warm_fd < 0)
+               return -1;
+
+       vaddr = (unsigned long)virt_addr;
+       if (vaddr & 0xfffff) {
+               fprintf(stderr, PFX "virt_addr is unaligned\n");
+               return -1;
+       }
+       if (phys_addr & 0xfffff) {
+               fprintf(stderr, PFX "phys_addr is unaligned\n");
+               return -1;
+       }
+
+       mop.virt_addr = vaddr;
+       mop.phys_addr = phys_addr;
+       mop.size = size;
+       mop.cb = cb;
+       mop.is_unmap = is_unmap;
+
+       ret = ioctl(warm_fd, WARMC_MMAP, &mop);
+       if (ret != 0) {
+               perror(PFX "WARMC_MMAP failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+int warm_mmap_section(void *virt_addr, unsigned long phys_addr,
+       unsigned long size, int cb)
+{
+       return warm_do_section(virt_addr, phys_addr, size, cb, 0);
+}
+
+int warm_munmap_section(void *virt_addr, unsigned long size)
+{
+       return warm_do_section(virt_addr, 0, size, 0, 1);
+}
diff --git a/warm.h b/warm.h
index 1ef5df2..c36d750 100644 (file)
--- a/warm.h
+++ b/warm.h
@@ -63,6 +63,10 @@ int warm_change_cb_range(int cb, int is_set, void *virt_addr, unsigned long size
 
 unsigned long warm_virt2phys(const void *ptr);
 
+int warm_mmap_section(void *virt_addr, unsigned long phys_addr,
+       unsigned long size, int cb);
+int warm_munmap_section(void *virt_addr, unsigned long size);
+
 void warm_finish(void);
 
 #ifdef __cplusplus
@@ -91,9 +95,19 @@ struct warm_change_cb
        int is_set;     /* set (1) or clear (0) */
 };
 
+struct warm_map_op
+{
+       unsigned long virt_addr;
+       unsigned long phys_addr;
+       unsigned long size;
+       int cb;
+       int is_unmap;
+};
+
 #define WARMC_CACHE_OP _IOW(WARM_IOCTL_BASE,  0, struct warm_cache_op)
 #define WARMC_CHANGE_CB        _IOW(WARM_IOCTL_BASE,  1, struct warm_change_cb)
 #define WARMC_VIRT2PHYS        _IOWR(WARM_IOCTL_BASE, 2, unsigned long)
+#define WARMC_MMAP     _IOW(WARM_IOCTL_BASE,  3, struct warm_map_op)
 
 #endif /* WARM_CODE */
 #endif /* !__ASSEMBLER__ */