dmatest: gather test results in the linked list
[deliverable/linux.git] / drivers / dma / dmatest.c
index 4225a292d371a3f949902b71999d146eded75ab8..3697bd49ed4ce0028d2ed24f89819f8ecc2ee651 100644 (file)
@@ -86,6 +86,39 @@ MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
 #define PATTERN_OVERWRITE      0x20
 #define PATTERN_COUNT_MASK     0x1f
 
+enum dmatest_error_type {
+       DMATEST_ET_OK,
+       DMATEST_ET_MAP_SRC,
+       DMATEST_ET_MAP_DST,
+       DMATEST_ET_PREP,
+       DMATEST_ET_SUBMIT,
+       DMATEST_ET_TIMEOUT,
+       DMATEST_ET_DMA_ERROR,
+       DMATEST_ET_DMA_IN_PROGRESS,
+       DMATEST_ET_VERIFY,
+};
+
+struct dmatest_thread_result {
+       struct list_head        node;
+       unsigned int            n;
+       unsigned int            src_off;
+       unsigned int            dst_off;
+       unsigned int            len;
+       enum dmatest_error_type type;
+       union {
+               unsigned long           data;
+               dma_cookie_t            cookie;
+               enum dma_status         status;
+               int                     error;
+       };
+};
+
+struct dmatest_result {
+       struct list_head        node;
+       char                    *name;
+       struct list_head        results;
+};
+
 struct dmatest_info;
 
 struct dmatest_thread {
@@ -146,6 +179,10 @@ struct dmatest_info {
        /* debugfs related stuff */
        struct dentry           *root;
        struct dmatest_params   dbgfs_params;
+
+       /* Test results */
+       struct list_head        results;
+       struct mutex            results_lock;
 };
 
 static struct dmatest_info test_info;
@@ -303,6 +340,98 @@ static unsigned int min_odd(unsigned int x, unsigned int y)
        return val % 2 ? val : val - 1;
 }
 
+static char *thread_result_get(const char *name,
+               struct dmatest_thread_result *tr)
+{
+       static const char * const messages[] = {
+               [DMATEST_ET_OK]                 = "No errors",
+               [DMATEST_ET_MAP_SRC]            = "src mapping error",
+               [DMATEST_ET_MAP_DST]            = "dst mapping error",
+               [DMATEST_ET_PREP]               = "prep error",
+               [DMATEST_ET_SUBMIT]             = "submit error",
+               [DMATEST_ET_TIMEOUT]            = "test timed out",
+               [DMATEST_ET_DMA_ERROR]          =
+                       "got completion callback (DMA_ERROR)",
+               [DMATEST_ET_DMA_IN_PROGRESS]    =
+                       "got completion callback (DMA_IN_PROGRESS)",
+               [DMATEST_ET_VERIFY]             = "errors",
+       };
+       static char buf[512];
+
+       snprintf(buf, sizeof(buf) - 1,
+                "%s: #%u: %s with src_off=0x%x ""dst_off=0x%x len=0x%x (%lu)",
+                name, tr->n, messages[tr->type], tr->src_off, tr->dst_off,
+                tr->len, tr->data);
+
+       return buf;
+}
+
+static int thread_result_add(struct dmatest_info *info,
+               struct dmatest_result *r, enum dmatest_error_type type,
+               unsigned int n, unsigned int src_off, unsigned int dst_off,
+               unsigned int len, unsigned long data)
+{
+       struct dmatest_thread_result *tr;
+
+       tr = kzalloc(sizeof(*tr), GFP_KERNEL);
+       if (!tr)
+               return -ENOMEM;
+
+       tr->type = type;
+       tr->n = n;
+       tr->src_off = src_off;
+       tr->dst_off = dst_off;
+       tr->len = len;
+       tr->data = data;
+
+       mutex_lock(&info->results_lock);
+       list_add_tail(&tr->node, &r->results);
+       mutex_unlock(&info->results_lock);
+
+       pr_warn("%s\n", thread_result_get(r->name, tr));
+       return 0;
+}
+
+static void result_free(struct dmatest_info *info, const char *name)
+{
+       struct dmatest_result *r, *_r;
+
+       mutex_lock(&info->results_lock);
+       list_for_each_entry_safe(r, _r, &info->results, node) {
+               struct dmatest_thread_result *tr, *_tr;
+
+               if (name && strcmp(r->name, name))
+                       continue;
+
+               list_for_each_entry_safe(tr, _tr, &r->results, node) {
+                       list_del(&tr->node);
+                       kfree(tr);
+               }
+
+               kfree(r->name);
+               list_del(&r->node);
+               kfree(r);
+       }
+
+       mutex_unlock(&info->results_lock);
+}
+
+static struct dmatest_result *result_init(struct dmatest_info *info,
+               const char *name)
+{
+       struct dmatest_result *r;
+
+       r = kzalloc(sizeof(*r), GFP_KERNEL);
+       if (r) {
+               r->name = kstrdup(name, GFP_KERNEL);
+               INIT_LIST_HEAD(&r->results);
+               mutex_lock(&info->results_lock);
+               list_add_tail(&r->node, &info->results);
+               mutex_unlock(&info->results_lock);
+       }
+       return r;
+}
+
 /*
  * This function repeatedly tests DMA transfers of various lengths and
  * offsets for a given operation type until it is told to exit by
@@ -339,6 +468,7 @@ static int dmatest_func(void *data)
        int                     src_cnt;
        int                     dst_cnt;
        int                     i;
+       struct dmatest_result   *result;
 
        thread_name = current->comm;
        set_freezable();
@@ -370,6 +500,10 @@ static int dmatest_func(void *data)
        } else
                goto err_thread_type;
 
+       result = result_init(info, thread_name);
+       if (!result)
+               goto err_srcs;
+
        thread->srcs = kcalloc(src_cnt+1, sizeof(u8 *), GFP_KERNEL);
        if (!thread->srcs)
                goto err_srcs;
@@ -443,10 +577,10 @@ static int dmatest_func(void *data)
                        ret = dma_mapping_error(dev->dev, dma_srcs[i]);
                        if (ret) {
                                unmap_src(dev->dev, dma_srcs, len, i);
-                               pr_warn("%s: #%u: mapping error %d with "
-                                       "src_off=0x%x len=0x%x\n",
-                                       thread_name, total_tests - 1, ret,
-                                       src_off, len);
+                               thread_result_add(info, result,
+                                                 DMATEST_ET_MAP_SRC,
+                                                 total_tests, src_off, dst_off,
+                                                 len, ret);
                                failed_tests++;
                                continue;
                        }
@@ -461,10 +595,10 @@ static int dmatest_func(void *data)
                                unmap_src(dev->dev, dma_srcs, len, src_cnt);
                                unmap_dst(dev->dev, dma_dsts, params->buf_size,
                                          i);
-                               pr_warn("%s: #%u: mapping error %d with "
-                                       "dst_off=0x%x len=0x%x\n",
-                                       thread_name, total_tests - 1, ret,
-                                       dst_off, params->buf_size);
+                               thread_result_add(info, result,
+                                                 DMATEST_ET_MAP_DST,
+                                                 total_tests, src_off, dst_off,
+                                                 len, ret);
                                failed_tests++;
                                continue;
                        }
@@ -494,10 +628,9 @@ static int dmatest_func(void *data)
                        unmap_src(dev->dev, dma_srcs, len, src_cnt);
                        unmap_dst(dev->dev, dma_dsts, params->buf_size,
                                  dst_cnt);
-                       pr_warning("%s: #%u: prep error with src_off=0x%x "
-                                       "dst_off=0x%x len=0x%x\n",
-                                       thread_name, total_tests - 1,
-                                       src_off, dst_off, len);
+                       thread_result_add(info, result, DMATEST_ET_PREP,
+                                         total_tests, src_off, dst_off,
+                                         len, 0);
                        msleep(100);
                        failed_tests++;
                        continue;
@@ -509,10 +642,9 @@ static int dmatest_func(void *data)
                cookie = tx->tx_submit(tx);
 
                if (dma_submit_error(cookie)) {
-                       pr_warning("%s: #%u: submit error %d with src_off=0x%x "
-                                       "dst_off=0x%x len=0x%x\n",
-                                       thread_name, total_tests - 1, cookie,
-                                       src_off, dst_off, len);
+                       thread_result_add(info, result, DMATEST_ET_SUBMIT,
+                                         total_tests, src_off, dst_off,
+                                         len, cookie);
                        msleep(100);
                        failed_tests++;
                        continue;
@@ -534,15 +666,17 @@ static int dmatest_func(void *data)
                         * free it this time?" dancing.  For now, just
                         * leave it dangling.
                         */
-                       pr_warning("%s: #%u: test timed out\n",
-                                  thread_name, total_tests - 1);
+                       thread_result_add(info, result, DMATEST_ET_TIMEOUT,
+                                         total_tests, src_off, dst_off,
+                                         len, 0);
                        failed_tests++;
                        continue;
                } else if (status != DMA_SUCCESS) {
-                       pr_warning("%s: #%u: got completion callback,"
-                                  " but status is \'%s\'\n",
-                                  thread_name, total_tests - 1,
-                                  status == DMA_ERROR ? "error" : "in progress");
+                       enum dmatest_error_type type = (status == DMA_ERROR) ?
+                               DMATEST_ET_DMA_ERROR : DMATEST_ET_DMA_IN_PROGRESS;
+                       thread_result_add(info, result, type,
+                                         total_tests, src_off, dst_off,
+                                         len, status);
                        failed_tests++;
                        continue;
                }
@@ -574,16 +708,14 @@ static int dmatest_func(void *data)
                                PATTERN_DST, false);
 
                if (error_count) {
-                       pr_warning("%s: #%u: %u errors with "
-                               "src_off=0x%x dst_off=0x%x len=0x%x\n",
-                               thread_name, total_tests - 1, error_count,
-                               src_off, dst_off, len);
+                       thread_result_add(info, result, DMATEST_ET_VERIFY,
+                                         total_tests, src_off, dst_off,
+                                         len, error_count);
                        failed_tests++;
                } else {
-                       pr_debug("%s: #%u: No errors with "
-                               "src_off=0x%x dst_off=0x%x len=0x%x\n",
-                               thread_name, total_tests - 1,
-                               src_off, dst_off, len);
+                       thread_result_add(info, result, DMATEST_ET_OK,
+                                         total_tests, src_off, dst_off,
+                                         len, 0);
                }
        }
 
@@ -807,6 +939,9 @@ static int __restart_threaded_test(struct dmatest_info *info, bool run)
        if (run == false)
                return 0;
 
+       /* Clear results from previous run */
+       result_free(info, NULL);
+
        /* Copy test parameters */
        memcpy(params, &info->dbgfs_params, sizeof(*params));
 
@@ -945,6 +1080,35 @@ static const struct file_operations dtf_run_fops = {
        .llseek = default_llseek,
 };
 
+static int dtf_results_show(struct seq_file *sf, void *data)
+{
+       struct dmatest_info *info = sf->private;
+       struct dmatest_result *result;
+       struct dmatest_thread_result *tr;
+
+       mutex_lock(&info->results_lock);
+       list_for_each_entry(result, &info->results, node) {
+               list_for_each_entry(tr, &result->results, node)
+                       seq_printf(sf, "%s\n",
+                               thread_result_get(result->name, tr));
+       }
+
+       mutex_unlock(&info->results_lock);
+       return 0;
+}
+
+static int dtf_results_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dtf_results_show, inode->i_private);
+}
+
+static const struct file_operations dtf_results_fops = {
+       .open           = dtf_results_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 static int dmatest_register_dbgfs(struct dmatest_info *info)
 {
        struct dentry *d;
@@ -1015,6 +1179,12 @@ static int dmatest_register_dbgfs(struct dmatest_info *info)
        if (IS_ERR_OR_NULL(d))
                goto err_node;
 
+       /* Results of test in progress */
+       d = debugfs_create_file("results", S_IRUGO, info->root, info,
+                               &dtf_results_fops);
+       if (IS_ERR_OR_NULL(d))
+               goto err_node;
+
        return 0;
 
 err_node:
@@ -1035,6 +1205,9 @@ static int __init dmatest_init(void)
        mutex_init(&info->lock);
        INIT_LIST_HEAD(&info->channels);
 
+       mutex_init(&info->results_lock);
+       INIT_LIST_HEAD(&info->results);
+
        /* Set default parameters */
        params->buf_size = test_buf_size;
        strlcpy(params->channel, test_channel, sizeof(params->channel));
@@ -1065,6 +1238,7 @@ static void __exit dmatest_exit(void)
 
        debugfs_remove_recursive(info->root);
        stop_threaded_test(info);
+       result_free(info, NULL);
 }
 module_exit(dmatest_exit);
 
This page took 0.037414 seconds and 5 git commands to generate.