[01/11] dmapool: add alloc/free performance test
Commit Message
From: Keith Busch <kbusch@kernel.org>
Provide a module that allocates and frees many blocks of various sizes
and report how long it takes. This is intended to provide a consistent
way to measure how changes to the dma_pool_alloc/free routines affect
timing.
Signed-off-by: Keith Busch <kbusch@kernel.org>
---
mm/Kconfig | 9 +++
mm/Makefile | 1 +
mm/dmapool_test.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 148 insertions(+)
create mode 100644 mm/dmapool_test.c
Comments
Hi Keith,
I love your patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v6.1-rc8]
[cannot apply to next-20221205]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Keith-Busch/dmapool-enhancements/20221205-232116
patch link: https://lore.kernel.org/r/20221205145937.54367-2-kbusch%40meta.com
patch subject: [PATCH 01/11] dmapool: add alloc/free performance test
config: powerpc-randconfig-c041-20221205
compiler: powerpc-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/68ca0ec415bed31d06a536a0832c305b9d14b1b9
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Keith-Busch/dmapool-enhancements/20221205-232116
git checkout 68ca0ec415bed31d06a536a0832c305b9d14b1b9
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
mm/dmapool_test.c: In function 'dmapool_checks':
>> mm/dmapool_test.c:111:17: error: 'struct device' has no member named 'dma_ops'
111 | test_dev.dma_ops = NULL;
| ^
vim +111 mm/dmapool_test.c
95
96 static int dmapool_checks(void)
97 {
98 int i, ret;
99
100 ret = dev_set_name(&test_dev, "dmapool-test");
101 if (ret)
102 return ret;
103
104 ret = device_register(&test_dev);
105 if (ret) {
106 printk("%s: register failed:%d\n", __func__, ret);
107 goto put_device;
108 }
109
110 test_dev.release = dmapool_test_release;
> 111 test_dev.dma_ops = NULL;
112 test_dev.dma_mask = &dma_mask;
113 ret = dma_set_mask_and_coherent(&test_dev, DMA_BIT_MASK(64));
114 if (ret) {
115 printk("%s: mask failed:%d\n", __func__, ret);
116 goto del_device;
117 }
118
119 for (i = 0; i < ARRAY_SIZE(block_sizes); i++) {
120 ret = dmapool_test_block(block_sizes[i]);
121 if (ret)
122 break;
123 }
124
125 del_device:
126 device_del(&test_dev);
127 put_device:
128 put_device(&test_dev);
129 return ret;
130 }
131
@@ -1044,6 +1044,15 @@ config GUP_TEST
comment "GUP_TEST needs to have DEBUG_FS enabled"
depends on !GUP_TEST && !DEBUG_FS
+config DMAPOOL_TEST
+ tristate "Enable a module to run time tests on dma_pool"
+ depends on HAS_DMA
+ help
+ Provides a module that will allocate and free many blocks of various
+ sizes and report how long it takes. This is intended to provide a
+ consistent way to measure how changes to the dma_pool_alloc/free
+ routines affect performance.
+
config GUP_GET_PTE_LOW_HIGH
bool
@@ -103,6 +103,7 @@ obj-$(CONFIG_MEMCG) += swap_cgroup.o
endif
obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o
obj-$(CONFIG_GUP_TEST) += gup_test.o
+obj-$(CONFIG_DMAPOOL_TEST) += dmapool_test.o
obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o
obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
new file mode 100644
@@ -0,0 +1,138 @@
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+
+#define NR_TESTS (100)
+
+struct dma_pool_pair {
+ dma_addr_t dma;
+ void *v;
+};
+
+static const int block_sizes[] = {
+ 16,
+ 64,
+ 256,
+ 1024,
+ 4096
+};
+
+static struct dma_pool *pool;
+struct device test_dev;
+u64 dma_mask;
+
+static inline int nr_blocks(int size)
+{
+ return clamp_t(int, (PAGE_SIZE / size) * 512, 1024, 8192);
+}
+
+static int dmapool_test_alloc(struct dma_pool_pair *p, int blocks)
+{
+ int i;
+
+ for (i = 0; i < blocks; i++) {
+ p[i].v = dma_pool_alloc(pool, GFP_KERNEL,
+ &p[i].dma);
+ if (!p[i].v)
+ goto pool_fail;
+ }
+
+ for (i = 0; i < blocks; i++)
+ dma_pool_free(pool, p[i].v, p[i].dma);
+
+ return 0;
+
+pool_fail:
+ for (--i; i >= 0; i--)
+ dma_pool_free(pool, p[i].v, p[i].dma);
+ return -ENOMEM;
+}
+
+static int dmapool_test_block(int block_size)
+{
+ int blocks = nr_blocks(block_size);
+ ktime_t start_time, end_time;
+ struct dma_pool_pair *p;
+ int i, ret;
+
+ p = kcalloc(blocks, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ pool = dma_pool_create("test pool", &test_dev, block_size,
+ block_size, 0);
+ if (!pool) {
+ ret = -ENOMEM;
+ goto free_pairs;
+ }
+
+ start_time = ktime_get();
+ for (i = 0; i < NR_TESTS; i++) {
+ ret = dmapool_test_alloc(p, blocks);
+ if (ret)
+ goto free_pool;
+ if (need_resched())
+ cond_resched();
+ }
+ end_time = ktime_get();
+
+ printk("dmapool test: size:%-4d blocks:%-6d time:%llu\n", block_size,
+ blocks, ktime_us_delta(end_time, start_time));
+
+free_pool:
+ dma_pool_destroy(pool);
+free_pairs:
+ kfree(p);
+ return ret;
+}
+
+static void dmapool_test_release(struct device *dev)
+{
+}
+
+static int dmapool_checks(void)
+{
+ int i, ret;
+
+ ret = dev_set_name(&test_dev, "dmapool-test");
+ if (ret)
+ return ret;
+
+ ret = device_register(&test_dev);
+ if (ret) {
+ printk("%s: register failed:%d\n", __func__, ret);
+ goto put_device;
+ }
+
+ test_dev.release = dmapool_test_release;
+ test_dev.dma_ops = NULL;
+ test_dev.dma_mask = &dma_mask;
+ ret = dma_set_mask_and_coherent(&test_dev, DMA_BIT_MASK(64));
+ if (ret) {
+ printk("%s: mask failed:%d\n", __func__, ret);
+ goto del_device;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(block_sizes); i++) {
+ ret = dmapool_test_block(block_sizes[i]);
+ if (ret)
+ break;
+ }
+
+del_device:
+ device_del(&test_dev);
+put_device:
+ put_device(&test_dev);
+ return ret;
+}
+
+static void dmapool_exit(void)
+{
+}
+
+module_init(dmapool_checks);
+module_exit(dmapool_exit);
+MODULE_LICENSE("GPL");