Module: xenomai-3
Branch: next
Commit: f1382fa8534381a644a3b3162978a4c8fb2f1437
URL:    
http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=f1382fa8534381a644a3b3162978a4c8fb2f1437

Author: Philippe Gerum <r...@xenomai.org>
Date:   Sun May 13 19:00:50 2018 +0200

drivers/testing: add core heap test module

---

 include/rtdm/uapi/testing.h        |   41 ++-
 kernel/drivers/testing/Kconfig     |    6 +
 kernel/drivers/testing/Makefile    |    3 +
 kernel/drivers/testing/heapcheck.c |  515 ++++++++++++++++++++++++++++++++++++
 4 files changed, 564 insertions(+), 1 deletion(-)

diff --git a/include/rtdm/uapi/testing.h b/include/rtdm/uapi/testing.h
index 06b8f1e..f8207b8 100644
--- a/include/rtdm/uapi/testing.h
+++ b/include/rtdm/uapi/testing.h
@@ -87,6 +87,37 @@ struct rttst_swtest_error {
 #define RTTST_RTDM_MAGIC_PRIMARY       0xfefbfefb
 #define RTTST_RTDM_MAGIC_SECONDARY     0xa5b9a5b9
 
+#define RTTST_HEAPCHECK_ZEROOVRD   1
+#define RTTST_HEAPCHECK_SHUFFLE    2
+#define RTTST_HEAPCHECK_PATTERN    4
+#define RTTST_HEAPCHECK_HOT        8
+
+struct rttst_heap_parms {
+       __u64 heap_size;
+       __u64 block_size;
+       int flags;
+       int nrstats;
+};
+
+struct rttst_heap_stats {
+       __u64 heap_size;
+       __u64 user_size;
+       __u64 block_size;
+       __s64 alloc_avg_ns;
+       __s64 alloc_max_ns;
+       __s64 free_avg_ns;
+       __s64 free_max_ns;
+       __u64 maximum_free;
+       __u64 largest_free;
+       int nrblocks;
+       int flags;
+};
+
+struct rttst_heap_stathdr {
+       int nrstats;
+       struct rttst_heap_stats *buf;
+};
+
 #define RTIOC_TYPE_TESTING             RTDM_CLASS_TESTING
 
 /*!
@@ -100,6 +131,8 @@ struct rttst_swtest_error {
 #define RTDM_SUBCLASS_SWITCHTEST       2
 /** subclase name: "rtdm" */
 #define RTDM_SUBCLASS_RTDMTEST         3
+/** subclase name: "heapcheck" */
+#define RTDM_SUBCLASS_HEAPCHECK                4
 /** @} */
 
 /*!
@@ -153,7 +186,13 @@ struct rttst_swtest_error {
   
 #define RTTST_RTIOC_RTDM_PING_SECONDARY \
        _IOR(RTIOC_TYPE_TESTING, 0x43, __u32)
-  
+
+#define RTTST_RTIOC_HEAP_CHECK \
+       _IOR(RTIOC_TYPE_TESTING, 0x44, struct rttst_heap_parms)
+
+#define RTTST_RTIOC_HEAP_STAT_COLLECT \
+       _IOR(RTIOC_TYPE_TESTING, 0x45, int)
+
 /** @} */
 
 #endif /* !_RTDM_UAPI_TESTING_H */
diff --git a/kernel/drivers/testing/Kconfig b/kernel/drivers/testing/Kconfig
index bb44abb..88c043c 100644
--- a/kernel/drivers/testing/Kconfig
+++ b/kernel/drivers/testing/Kconfig
@@ -14,6 +14,12 @@ config XENO_DRIVERS_SWITCHTEST
        Kernel-based driver for unit testing context switches and
        FPU switches.
 
+config XENO_DRIVERS_HEAPCHECK
+       tristate "Memory allocator test driver"
+       default y
+       help
+       Kernel-based driver for testing Cobalt's memory allocator.
+
 config XENO_DRIVERS_RTDMTEST
        depends on m
        tristate "RTDM unit tests driver"
diff --git a/kernel/drivers/testing/Makefile b/kernel/drivers/testing/Makefile
index 367a22e..09b0763 100644
--- a/kernel/drivers/testing/Makefile
+++ b/kernel/drivers/testing/Makefile
@@ -2,9 +2,12 @@
 obj-$(CONFIG_XENO_DRIVERS_TIMERBENCH) += xeno_timerbench.o
 obj-$(CONFIG_XENO_DRIVERS_SWITCHTEST) += xeno_switchtest.o
 obj-$(CONFIG_XENO_DRIVERS_RTDMTEST)   += xeno_rtdmtest.o
+obj-$(CONFIG_XENO_DRIVERS_HEAPCHECK)   += xeno_heapcheck.o
 
 xeno_timerbench-y := timerbench.o
 
 xeno_switchtest-y := switchtest.o
 
 xeno_rtdmtest-y := rtdmtest.o
+
+xeno_heapcheck-y := heapcheck.o
diff --git a/kernel/drivers/testing/heapcheck.c 
b/kernel/drivers/testing/heapcheck.c
new file mode 100644
index 0000000..bed5a05
--- /dev/null
+++ b/kernel/drivers/testing/heapcheck.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2018 Philippe Gerum <r...@xenomai.org>.
+ *
+ * Xenomai is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * Xenomai is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xenomai; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/random.h>
+#include <cobalt/kernel/assert.h>
+#include <cobalt/kernel/heap.h>
+#include <rtdm/uapi/testing.h>
+#include <rtdm/driver.h>
+
+#define complain(__fmt, __args...)     \
+       printk(XENO_WARNING "heap check: " __fmt "\n", ##__args)
+
+static struct xnheap test_heap = {
+       .name = "test_heap"
+};
+
+enum pattern {
+       alphabet_series,
+       digit_series,
+       binary_series,
+};
+
+struct chunk {
+       void *ptr;
+       enum pattern pattern;
+};
+
+struct runstats {
+       struct rttst_heap_stats stats;
+       struct runstats *next;
+};
+
+static struct runstats *statistics;
+
+static int nrstats;
+
+static inline void breathe(int loops)
+{
+       if ((loops % 1000) == 0)
+               rtdm_task_sleep(300000ULL);
+}
+
+static inline void do_swap(void *left, void *right, const size_t size)
+{
+       char trans[size];
+
+       memcpy(trans, left, size);
+       memcpy(left, right, size);
+       memcpy(right, trans, size);
+}
+
+static void random_shuffle(void *vbase, size_t nmemb, const size_t size)
+{
+       struct {
+               char x[size];
+       } __attribute__((packed)) *base = vbase;
+       unsigned int j, k;
+
+       for (j = nmemb; j > 0; j--) {
+               k = (unsigned int)(prandom_u32() % nmemb) + 1;
+               if (j == k)
+                       continue;
+               do_swap(&base[j - 1], &base[k - 1], size);
+       }
+}
+
+static void fill_pattern(char *p, size_t size, enum pattern pat)
+{
+       unsigned int val, count;
+
+       switch (pat) {
+       case alphabet_series:
+               val = 'a';
+               count = 26;
+               break;
+       case digit_series:
+               val = '0';
+               count = 10;
+               break;
+       default:
+               val = 0;
+               count = 255;
+               break;
+       }
+
+       while (size-- > 0) {
+               *p++ = (char)(val % count);
+               val++;
+       }
+}
+
+static int check_pattern(const char *p, size_t size, enum pattern pat)
+{
+       unsigned int val, count;
+
+       switch (pat) {
+       case alphabet_series:
+               val = 'a';
+               count = 26;
+               break;
+       case digit_series:
+               val = '0';
+               count = 10;
+               break;
+       default:
+               val = 0;
+               count = 255;
+               break;
+       }
+
+       while (size-- > 0) {
+               if (*p++ != (char)(val % count))
+                       return 0;
+               val++;
+       }
+
+       return 1;
+}
+
+static size_t find_largest_free(size_t free_size, size_t block_size)
+{
+       void *p;
+
+       for (;;) {
+               p = xnheap_alloc(&test_heap, free_size);
+               if (p) {
+                       xnheap_free(&test_heap, p);
+                       break;
+               }
+               if (free_size <= block_size)
+                       break;
+               free_size -= block_size;
+       }
+
+       return free_size;
+}
+
+static int test_seq(size_t heap_size, size_t block_size, int flags)
+{
+       long alloc_sum_ns, alloc_avg_ns, free_sum_ns, free_avg_ns,
+               alloc_max_ns, free_max_ns, d;
+       size_t user_size, largest_free, maximum_free, freed;
+       int ret, n, k, maxblocks, nrblocks;
+       nanosecs_rel_t start, end;
+       struct chunk *chunks;
+       struct runstats *st;
+       bool done_frag;
+       void *mem, *p;
+
+       maxblocks = heap_size / block_size;
+
+       mem = vmalloc(heap_size);
+       if (mem == NULL)
+               return -ENOMEM;
+
+       ret = xnheap_init(&test_heap, mem, heap_size);
+       if (ret) {
+               complain("cannot init heap with size %zu",
+                      heap_size);
+               goto out;
+       }
+
+       chunks = vmalloc(sizeof(*chunks) * maxblocks);
+       if (chunks == NULL) {
+               ret = -ENOMEM;
+               goto no_chunks;
+       }
+       memset(chunks, 0, sizeof(*chunks) * maxblocks);
+
+       ret = xnthread_harden();
+       if (ret)
+               goto done;
+
+       if (xnheap_get_size(&test_heap) != heap_size) {
+               complain("memory size inconsistency (%zu / %zu bytes)",
+                        heap_size, xnheap_get_size(&test_heap));
+               goto bad;
+       }
+
+       user_size = 0;
+       alloc_avg_ns = 0;
+       free_avg_ns = 0;
+       alloc_max_ns = 0;
+       free_max_ns = 0;
+       maximum_free = 0;
+       largest_free = 0;
+
+       for (n = 0, alloc_sum_ns = 0; ; n++) {
+               start = rtdm_clock_read_monotonic();
+               p = xnheap_alloc(&test_heap, block_size);
+               end = rtdm_clock_read_monotonic();
+               d = end - start;
+               if (d > alloc_max_ns)
+                       alloc_max_ns = d;
+               alloc_sum_ns += d;
+               if (p == NULL)
+                       break;
+               user_size += block_size;
+               if (n >= maxblocks) {
+                       complain("too many blocks fetched"
+                              " (heap=%zu, block=%zu, "
+                              "got more than %d blocks)",
+                              heap_size, block_size, maxblocks);
+                       goto bad;
+               }
+               chunks[n].ptr = p;
+               if (flags & RTTST_HEAPCHECK_PATTERN) {
+                       chunks[n].pattern = (enum pattern)(prandom_u32() % 3);
+                       fill_pattern(chunks[n].ptr, block_size, 
chunks[n].pattern);
+               }
+               breathe(n);
+       }
+
+       nrblocks = n;
+       if (nrblocks == 0)
+               goto do_stats;
+
+       if ((flags & RTTST_HEAPCHECK_ZEROOVRD) && nrblocks != maxblocks) {
+               complain("too few blocks fetched, unexpected overhead"
+                        " (heap=%zu, block=%zu, "
+                        "got %d, less than %d blocks)",
+                        heap_size, block_size, nrblocks, maxblocks);
+               goto bad;
+       }
+
+       breathe(0);
+
+       /* Make sure we did not trash any busy block while allocating. */
+       if (flags & RTTST_HEAPCHECK_PATTERN) {
+               for (n = 0; n < nrblocks; n++) {
+                       if (!check_pattern(chunks[n].ptr, block_size,
+                                          chunks[n].pattern)) {
+                               complain("corrupted block #%d on alloc"
+                                        " sequence (pattern %d)",
+                                        n, chunks[n].pattern);
+                               goto bad;
+                       }
+                       breathe(n);
+               }
+       }
+       
+       if (flags & RTTST_HEAPCHECK_SHUFFLE)
+               random_shuffle(chunks, nrblocks, sizeof(*chunks));
+
+       /*
+        * Release all blocks.
+        */
+       for (n = 0, free_sum_ns = 0, freed = 0, done_frag = false;
+            n < nrblocks; n++) {
+               start = rtdm_clock_read_monotonic();
+               xnheap_free(&test_heap, chunks[n].ptr);
+               end = rtdm_clock_read_monotonic();
+               d = end - start;
+               if (d > free_max_ns)
+                       free_max_ns = d;
+               free_sum_ns += d;
+               chunks[n].ptr = NULL;
+               /* Make sure we did not trash busy blocks while freeing. */
+               if (flags & RTTST_HEAPCHECK_PATTERN) {
+                       for (k = 0; k < nrblocks; k++) {
+                               if (chunks[k].ptr &&
+                                   !check_pattern(chunks[k].ptr, block_size,
+                                                  chunks[k].pattern)) {
+                                       complain("corrupted block #%d on 
release"
+                                                " sequence (pattern %d)",
+                                                k, chunks[k].pattern);
+                                       goto bad;
+                               }
+                               breathe(k);
+                       }
+               }
+               freed += block_size;
+               /*
+                * Get a sense of the fragmentation for the tested
+                * allocation pattern, heap and block sizes when half
+                * of the usable heap size should be available to us.
+                * NOTE: user_size excludes the overhead, this is
+                * actually what we managed to get from the current
+                * heap out of the allocation loop.
+                */
+               if (!done_frag && freed >= user_size / 2) {
+                       /* Calculate the external fragmentation. */
+                       largest_free = find_largest_free(freed, block_size);
+                       maximum_free = freed;
+                       done_frag = true;
+               }
+               breathe(n);
+       }
+
+       /*
+        * If the deallocation mechanism is broken, we might not be
+        * able to reproduce the same allocation pattern with the same
+        * outcome, check this.
+        */
+       if (flags & RTTST_HEAPCHECK_HOT) {
+               for (n = 0, alloc_max_ns = alloc_sum_ns = 0; ; n++) {
+                       start = rtdm_clock_read_monotonic();
+                       p = xnheap_alloc(&test_heap, block_size);
+                       end = rtdm_clock_read_monotonic();
+                       d = end - start;
+                       if (d > alloc_max_ns)
+                               alloc_max_ns = d;
+                       alloc_sum_ns += d;
+                       if (p == NULL)
+                               break;
+                       if (n >= maxblocks) {
+                               complain("too many blocks fetched during hot 
pass"
+                                        " (heap=%zu, block=%zu, "
+                                        "got more than %d blocks)",
+                                        heap_size, block_size, maxblocks);
+                               goto bad;
+                       }
+                       chunks[n].ptr = p;
+                       breathe(n);
+               }
+               if (n != nrblocks) {
+                       complain("inconsistent block count fetched"
+                                " during hot pass (heap=%zu, block=%zu, "
+                                "got %d blocks vs %d during alloc)",
+                                heap_size, block_size, n, nrblocks);
+                       goto bad;
+               }
+               for (n = 0, free_max_ns = free_sum_ns = 0; n < nrblocks; n++) {
+                       start = rtdm_clock_read_monotonic();
+                       xnheap_free(&test_heap, chunks[n].ptr);
+                       end = rtdm_clock_read_monotonic();
+                       d = end - start;
+                       if (d > free_max_ns)
+                               free_max_ns = d;
+                       free_sum_ns += d;
+                       breathe(n);
+               }
+       }
+
+       alloc_avg_ns = alloc_sum_ns / nrblocks;
+       free_avg_ns = free_sum_ns / nrblocks;
+
+       if ((flags & RTTST_HEAPCHECK_ZEROOVRD) && heap_size != user_size) {
+               complain("unexpected overhead reported");
+               goto bad;
+       }
+
+       if (xnheap_get_used(&test_heap) > 0) {
+               complain("memory leakage reported: %zu bytes missing",
+                        xnheap_get_used(&test_heap));
+               goto bad;
+       }
+               
+do_stats:
+       xnthread_relax(0, 0);
+       ret = 0;
+       /*
+        * Don't report stats when running a pattern check, timings
+        * are affected.
+        */
+       if (!(flags & RTTST_HEAPCHECK_PATTERN)) {
+               st = kmalloc(sizeof(*st), GFP_KERNEL);
+               if (st == NULL) {
+                       complain("failed allocating memory");
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               st->stats.heap_size = heap_size;
+               st->stats.user_size = user_size;
+               st->stats.block_size = block_size;
+               st->stats.nrblocks = nrblocks;
+               st->stats.alloc_avg_ns = alloc_avg_ns;
+               st->stats.alloc_max_ns = alloc_max_ns;
+               st->stats.free_avg_ns = free_avg_ns;
+               st->stats.free_max_ns = free_max_ns;
+               st->stats.maximum_free = maximum_free;
+               st->stats.largest_free = largest_free;
+               st->stats.flags = flags;
+               st->next = statistics;
+               statistics = st;
+               nrstats++;
+       }
+
+done:
+       vfree(chunks);
+no_chunks:
+       xnheap_destroy(&test_heap);
+out:
+       vfree(mem);
+
+       return ret;
+bad:
+       xnthread_relax(0, 0);
+       ret = -EPROTO;
+       goto done;
+}
+
+static int collect_stats(struct rtdm_fd *fd,
+                        struct rttst_heap_stats __user *buf, int nr)
+{
+       struct runstats *p, *next;
+       int ret, n;
+
+       if (nr < 0)
+               return -EINVAL;
+
+       for (p = statistics, n = nr; p && n > 0 && nrstats > 0;
+            n--, nrstats--, p = next, buf += sizeof(p->stats)) {
+               ret = rtdm_copy_to_user(fd, buf, &p->stats, sizeof(p->stats));
+               if (ret)
+                       return ret;
+               next = p->next;
+               statistics = next;
+               kfree(p);
+       }
+
+       return nr - n;
+}
+
+static void heapcheck_close(struct rtdm_fd *fd)
+{
+       struct runstats *p, *next;
+
+       for (p = statistics; p; p = next) {
+               next = p->next;
+               kfree(p);
+       }
+
+       statistics = NULL;
+}
+
+static int heapcheck_ioctl(struct rtdm_fd *fd,
+                          unsigned int request, void __user *arg)
+{
+       struct rttst_heap_stathdr sthdr;
+       struct rttst_heap_parms parms;
+       int ret;
+       
+       switch (request) {
+       case RTTST_RTIOC_HEAP_CHECK:
+               ret = rtdm_copy_from_user(fd, &parms, arg, sizeof(parms));
+               if (ret)
+                       return ret;
+               ret = test_seq(parms.heap_size,
+                              parms.block_size,
+                              parms.flags);
+               if (ret)
+                       return ret;
+               parms.nrstats = nrstats;
+               ret = rtdm_copy_to_user(fd, arg, &parms, sizeof(parms));
+               break;
+       case RTTST_RTIOC_HEAP_STAT_COLLECT:
+               ret = rtdm_copy_from_user(fd, &sthdr, arg, sizeof(sthdr));
+               if (ret)
+                       return ret;
+               ret = collect_stats(fd, sthdr.buf, sthdr.nrstats);
+               if (ret < 0)
+                       return ret;
+               sthdr.nrstats = ret;
+               ret = rtdm_copy_to_user(fd, arg, &sthdr, sizeof(sthdr));
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static struct rtdm_driver heapcheck_driver = {
+       .profile_info           = RTDM_PROFILE_INFO(heap_check,
+                                                   RTDM_CLASS_TESTING,
+                                                   RTDM_SUBCLASS_HEAPCHECK,
+                                                   RTTST_PROFILE_VER),
+       .device_flags           = RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE,
+       .device_count           = 1,
+       .ops = {
+               .close          = heapcheck_close,
+               .ioctl_nrt      = heapcheck_ioctl,
+       },
+};
+
+static struct rtdm_device heapcheck_device = {
+       .driver = &heapcheck_driver,
+       .label = "heapcheck",
+};
+
+static int __init heapcheck_init(void)
+{
+       if (!realtime_core_enabled())
+               return -ENODEV;
+
+       return rtdm_dev_register(&heapcheck_device);
+}
+
+static void __exit heapcheck_exit(void)
+{
+       rtdm_dev_unregister(&heapcheck_device);
+}
+
+module_init(heapcheck_init);
+module_exit(heapcheck_exit);


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
https://xenomai.org/mailman/listinfo/xenomai-git

Reply via email to