aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mm/cache_perf.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mm/cache_perf.c')
-rw-r--r--arch/arm/mm/cache_perf.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/arch/arm/mm/cache_perf.c b/arch/arm/mm/cache_perf.c
new file mode 100644
index 0000000..a29bcb1
--- /dev/null
+++ b/arch/arm/mm/cache_perf.c
@@ -0,0 +1,348 @@
+/* linux/arch/arm/mm/cache-perf.c
+ *
+ * Copyright 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/dma-mapping.h>
+#include <linux/types.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+
+#include <asm/outercache.h>
+#include <asm/cacheflush.h>
+#include <linux/string.h>
+
+enum memtype {
+ MT_WBWA = 1,
+ MT_NC,
+ MT_SO,
+ MT_MAX,
+};
+
+static unsigned int try_cnt = 100;
+module_param(try_cnt, uint, S_IRUGO);
+MODULE_PARM_DESC(try_cnt, "Try count to test");
+
+static bool l1 = 1;
+module_param(l1, bool, S_IRUGO);
+MODULE_PARM_DESC(l1, "Set for L1 check");
+
+static bool l2 = 1;
+module_param(l2, bool, S_IRUGO);
+MODULE_PARM_DESC(l2, "Set for L2 check");
+
+static unsigned int mcpy = MT_WBWA;
+module_param(mcpy, uint, S_IRUGO);
+MODULE_PARM_DESC(mcpy, "Set for mcpy");
+
+static bool cm = 1;
+module_param(cm, bool, S_IRUGO);
+MODULE_PARM_DESC(cm, "Set for cache maintenance");
+
+static unsigned int mcpy_size = SZ_4M;
+module_param(mcpy_size, uint, S_IRUGO);
+MODULE_PARM_DESC(mcpy_size, "Set for mcpy size");
+
+struct task_struct *cacheperf_task;
+static bool thread_running;
+
+#define START_SIZE (64)
+#define END_SIZE (SZ_4M)
+#define OUT_TRY_CNT 100
+#define SCRAMBLE_SIZE (128+64+32+16+8+4+2+1)
+#define SMALL_SIZE (7)
+
+enum cachemaintenance {
+ CM_CLEAN,
+ CM_INV,
+ CM_FLUSH,
+ CM_FLUSHALL,
+};
+
+static long update_timeval(struct timespec lhs, struct timespec rhs)
+{
+ long val;
+ struct timespec ts;
+
+ ts = timespec_sub(rhs, lhs);
+ val = ts.tv_sec*NSEC_PER_SEC + ts.tv_nsec;
+
+ return val;
+}
+
+bool buf_compare(u8 src[], u8 dst[], unsigned int bytes)
+{
+ unsigned int i;
+
+ for (i = 0; i < bytes; i++) {
+ if (src[i] != dst[i]) {
+ printk(KERN_ERR "Failed to compare: %d, %x:%x-%x:%x\n",
+ i, (u32)src, src[i], (u32)dst, dst[i]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void *remap_vm(dma_addr_t phys, u32 size, pgprot_t pgprot)
+{
+ unsigned long num_pages, i;
+ struct page **pages;
+ void *virt;
+
+ num_pages = size >> PAGE_SHIFT;
+ pages = kmalloc(num_pages * sizeof(struct page *), GFP_KERNEL);
+
+ if (!pages)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < num_pages; i++)
+ pages[i] = pfn_to_page((phys >> PAGE_SHIFT) + i);
+
+ virt = vmap(pages, num_pages, VM_MAP, pgprot);
+
+ if (!virt) {
+ kfree(pages);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ kfree(pages);
+
+ return virt;
+}
+
+static void memcpyperf(void *src, void *dst, u32 size, u32 cnt)
+{
+ struct timespec beforets;
+ struct timespec afterts;
+ long val[OUT_TRY_CNT];
+ long sum = 0;
+ u32 i, j;
+
+ memset(src, 0xab, size);
+ memset(dst, 0x00, size);
+
+ for (j = 0; j < OUT_TRY_CNT; j++) {
+ getnstimeofday(&beforets);
+ for (i = 0; i < cnt; i++)
+ memcpy(dst, src, size);
+ getnstimeofday(&afterts);
+ mdelay(100);
+ val[j] = update_timeval(beforets, afterts)/cnt;
+ }
+
+ for (j = 0; j < OUT_TRY_CNT; j++)
+ sum += val[j];
+
+ printk(KERN_ERR "%lu\n", sum/OUT_TRY_CNT);
+
+ if (buf_compare(src, dst, size))
+ printk(KERN_ERR "copy err\n");
+}
+
+static void cacheperf(void *vbuf, enum cachemaintenance id)
+{
+ struct timespec beforets;
+ struct timespec afterts;
+ phys_addr_t pbuf = virt_to_phys(vbuf);
+ u32 pbufend, xfer_size, i;
+ long timeval;
+
+ xfer_size = START_SIZE;
+ while (xfer_size <= END_SIZE) {
+ pbufend = pbuf + xfer_size;
+ timeval = 0;
+
+ for (i = 0; i < try_cnt; i++) {
+ memset(vbuf, i, xfer_size);
+ getnstimeofday(&beforets);
+
+ switch (id) {
+ case CM_CLEAN:
+ if (l1)
+ dmac_map_area(vbuf, xfer_size,
+ DMA_TO_DEVICE);
+ if (l2)
+ outer_clean_range(pbuf, pbufend);
+ break;
+ case CM_INV:
+ if (l2)
+ outer_inv_range(pbuf, pbufend);
+ if (l1)
+ dmac_unmap_area(vbuf, xfer_size,
+ DMA_FROM_DEVICE);
+ break;
+ case CM_FLUSH:
+ if (l1)
+ dmac_flush_range(vbuf,
+ (void *)((u32) vbuf + xfer_size));
+ if (l2)
+ outer_flush_range(pbuf, pbufend);
+ break;
+ case CM_FLUSHALL:
+ if (l1)
+ flush_cache_all();
+ if (l2)
+ outer_flush_all();
+ break;
+ }
+ getnstimeofday(&afterts);
+ timeval += update_timeval(beforets, afterts);
+ }
+ printk(KERN_INFO "%lu\n", timeval/try_cnt);
+ xfer_size *= 2;
+ }
+}
+
+static int perfmain(void)
+{
+ phys_addr_t dmasrc, dmadst;
+ void *srcbuf[MT_MAX];
+ void *dstbuf[MT_MAX];
+ u32 xfer_size;
+
+ srcbuf[MT_WBWA] = kmalloc(END_SIZE, GFP_KERNEL);
+ dstbuf[MT_WBWA] = kmalloc(END_SIZE, GFP_KERNEL);
+ srcbuf[MT_NC] = dma_alloc_writecombine(
+ NULL, mcpy_size, &dmasrc, GFP_KERNEL);
+ dstbuf[MT_NC] = dma_alloc_writecombine(
+ NULL, mcpy_size, &dmadst, GFP_KERNEL);
+ if (!srcbuf[MT_WBWA] && !srcbuf[MT_NC] &&
+ !dstbuf[MT_WBWA] && !dstbuf[MT_NC]) {
+ printk(KERN_ERR "Memory allocation error!\n");
+ dma_free_coherent(NULL, mcpy_size, srcbuf[MT_NC], dmasrc);
+ dma_free_coherent(NULL, mcpy_size, dstbuf[MT_NC], dmadst);
+ kfree(srcbuf[MT_WBWA]);
+ kfree(dstbuf[MT_WBWA]);
+ return 0;
+ }
+
+ if (mcpy) {
+ printk(KERN_INFO "## Memcpy perf (ns, unit tr size: %dKB)\n",
+ mcpy_size/SZ_1K);
+
+ if (mcpy >= MT_SO) {
+ printk(KERN_INFO "1. SO type\n");
+ srcbuf[MT_SO] = remap_vm(dmasrc, mcpy_size,
+ pgprot_noncached(PAGE_KERNEL));
+ dstbuf[MT_SO] = remap_vm(dmadst, mcpy_size,
+ pgprot_noncached(PAGE_KERNEL));
+ xfer_size = START_SIZE;
+ while (xfer_size <= END_SIZE) {
+ memcpyperf(srcbuf[MT_SO], dstbuf[MT_SO],
+ xfer_size, 10);
+ xfer_size *= 2;
+ }
+ vunmap(srcbuf[MT_SO]);
+ vunmap(dstbuf[MT_SO]);
+ }
+
+ if (mcpy >= MT_NC) {
+ printk(KERN_INFO "2. Normal NCNB type\n");
+ xfer_size = START_SIZE;
+ while (xfer_size <= END_SIZE) {
+ memcpyperf(srcbuf[MT_NC], dstbuf[MT_NC],
+ xfer_size, 10);
+ xfer_size *= 2;
+ }
+ }
+
+ printk(KERN_INFO "3. Cache memcpy\n");
+ printk(KERN_INFO "scramble size:");
+ memcpyperf(srcbuf[MT_WBWA], dstbuf[MT_WBWA],
+ SCRAMBLE_SIZE, try_cnt);
+ memset(dstbuf[MT_WBWA], 0x0, SCRAMBLE_SIZE);
+
+ printk(KERN_INFO "small size:");
+ memcpyperf(srcbuf[MT_WBWA], dstbuf[MT_WBWA],
+ SMALL_SIZE, try_cnt);
+ memset(dstbuf[MT_WBWA], 0x0, SMALL_SIZE);
+
+ printk(KERN_INFO "size (%d ~ %d)\n ", START_SIZE, END_SIZE);
+ xfer_size = START_SIZE;
+ while (xfer_size <= END_SIZE) {
+ memcpyperf(srcbuf[MT_WBWA], dstbuf[MT_WBWA],
+ xfer_size, try_cnt);
+ xfer_size *= 2;
+ }
+ }
+
+ if (cm) {
+ printk(KERN_INFO "## Memcpy perf (ns)\n");
+
+ printk(KERN_INFO "1. Clean perf\n");
+ cacheperf(srcbuf[MT_WBWA], CM_CLEAN);
+
+ printk(KERN_INFO "2. Invalidate perf\n");
+ cacheperf(srcbuf[MT_WBWA], CM_INV);
+
+ printk(KERN_INFO "3. Flush perf\n");
+ cacheperf(srcbuf[MT_WBWA], CM_FLUSH);
+
+ printk(KERN_INFO "4. Flush all perf\n");
+ cacheperf(srcbuf[MT_WBWA], CM_FLUSHALL);
+ }
+
+ dma_free_coherent(NULL, mcpy_size, srcbuf[MT_NC], dmasrc);
+ dma_free_coherent(NULL, mcpy_size, dstbuf[MT_NC], dmadst);
+ kfree(srcbuf[MT_WBWA]);
+ kfree(dstbuf[MT_WBWA]);
+
+ return 0;
+}
+
+static int thread_func(void *data)
+{
+ thread_running = 1;
+ perfmain();
+ thread_running = 0;
+
+ return 0;
+}
+
+int __init cacheperf_init(void)
+{
+#ifndef CONFIG_OUTER_CACHE
+ l2 = 0;
+#endif
+
+ printk(KERN_ERR "Test condition: l1: %d, l2: %d, try_cnt:%d, (%dB ~ %dMB)\n",
+ l1, l2, try_cnt, START_SIZE, END_SIZE/SZ_1M);
+
+ cacheperf_task = kzalloc(sizeof(struct task_struct), GFP_KERNEL);
+ cacheperf_task = kthread_run(thread_func, NULL, "cacheperf_thread");
+ if (IS_ERR(cacheperf_task))
+ printk(KERN_INFO "Failed to create module\n");
+
+ return 0;
+}
+module_init(cacheperf_init)
+
+void cacheperf_exit(void)
+{
+ printk(KERN_ERR "Exit module: thread_running: %d\n", thread_running);
+
+ if (thread_running)
+ kthread_stop(cacheperf_task);
+
+ kfree(cacheperf_task);
+}
+module_exit(cacheperf_exit);
+MODULE_LICENSE("GPL");