Merge branch 'clockevents/fixes' of git://git.linaro.org/people/daniel.lezcano/linux...
[linux-drm-fsl-dcu.git] / drivers / dma / dmatest.c
index 26b502069638ab09426a75b416b8f83ef290aa00..20f9a3aaf9266ea6daa71a18f08d258afa8a1a1e 100644 (file)
@@ -70,6 +70,10 @@ static bool noverify;
 module_param(noverify, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(noverify, "Disable random data setup and verification");
 
+static bool verbose;
+module_param(verbose, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)");
+
 /**
  * struct dmatest_params - test parameters.
  * @buf_size:          size of the memcpy test buffer
@@ -161,6 +165,43 @@ struct dmatest_chan {
        struct list_head        threads;
 };
 
+static DECLARE_WAIT_QUEUE_HEAD(thread_wait);
+static bool wait;
+
+static bool is_threaded_test_run(struct dmatest_info *info)
+{
+       struct dmatest_chan *dtc;
+
+       list_for_each_entry(dtc, &info->channels, node) {
+               struct dmatest_thread *thread;
+
+               list_for_each_entry(thread, &dtc->threads, node) {
+                       if (!thread->done)
+                               return true;
+               }
+       }
+
+       return false;
+}
+
+static int dmatest_wait_get(char *val, const struct kernel_param *kp)
+{
+       struct dmatest_info *info = &test_info;
+       struct dmatest_params *params = &info->params;
+
+       if (params->iterations)
+               wait_event(thread_wait, !is_threaded_test_run(info));
+       wait = true;
+       return param_get_bool(val, kp);
+}
+
+static struct kernel_param_ops wait_ops = {
+       .get = dmatest_wait_get,
+       .set = param_set_bool,
+};
+module_param_cb(wait, &wait_ops, &wait, S_IRUGO);
+MODULE_PARM_DESC(wait, "Wait for tests to complete (default: false)");
+
 static bool dmatest_match_channel(struct dmatest_params *params,
                struct dma_chan *chan)
 {
@@ -289,20 +330,6 @@ static void dmatest_callback(void *arg)
        wake_up_all(done->wait);
 }
 
-static inline void unmap_src(struct device *dev, dma_addr_t *addr, size_t len,
-                            unsigned int count)
-{
-       while (count--)
-               dma_unmap_single(dev, addr[count], len, DMA_TO_DEVICE);
-}
-
-static inline void unmap_dst(struct device *dev, dma_addr_t *addr, size_t len,
-                            unsigned int count)
-{
-       while (count--)
-               dma_unmap_single(dev, addr[count], len, DMA_BIDIRECTIONAL);
-}
-
 static unsigned int min_odd(unsigned int x, unsigned int y)
 {
        unsigned int val = min(x, y);
@@ -313,7 +340,7 @@ static unsigned int min_odd(unsigned int x, unsigned int y)
 static void result(const char *err, unsigned int n, unsigned int src_off,
                   unsigned int dst_off, unsigned int len, unsigned long data)
 {
-       pr_info("%s: result #%u: '%s' with src_off=0x%x ""dst_off=0x%x len=0x%x (%lu)",
+       pr_info("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)",
                current->comm, n, err, src_off, dst_off, len, data);
 }
 
@@ -321,10 +348,17 @@ static void dbg_result(const char *err, unsigned int n, unsigned int src_off,
                       unsigned int dst_off, unsigned int len,
                       unsigned long data)
 {
-       pr_debug("%s: result #%u: '%s' with src_off=0x%x ""dst_off=0x%x len=0x%x (%lu)",
-                current->comm, n, err, src_off, dst_off, len, data);
+       pr_debug("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)",
+                  current->comm, n, err, src_off, dst_off, len, data);
 }
 
+#define verbose_result(err, n, src_off, dst_off, len, data) ({ \
+       if (verbose) \
+               result(err, n, src_off, dst_off, len, data); \
+       else \
+               dbg_result(err, n, src_off, dst_off, len, data); \
+})
+
 static unsigned long long dmatest_persec(s64 runtime, unsigned int val)
 {
        unsigned long long per_sec = 1000000;
@@ -447,8 +481,9 @@ static int dmatest_func(void *data)
        while (!kthread_should_stop()
               && !(params->iterations && total_tests >= params->iterations)) {
                struct dma_async_tx_descriptor *tx = NULL;
-               dma_addr_t dma_srcs[src_cnt];
-               dma_addr_t dma_dsts[dst_cnt];
+               struct dmaengine_unmap_data *um;
+               dma_addr_t srcs[src_cnt];
+               dma_addr_t *dsts;
                u8 align = 0;
 
                total_tests++;
@@ -493,61 +528,75 @@ static int dmatest_func(void *data)
                        len = 1 << align;
                total_len += len;
 
-               for (i = 0; i < src_cnt; i++) {
-                       u8 *buf = thread->srcs[i] + src_off;
+               um = dmaengine_get_unmap_data(dev->dev, src_cnt+dst_cnt,
+                                             GFP_KERNEL);
+               if (!um) {
+                       failed_tests++;
+                       result("unmap data NULL", total_tests,
+                              src_off, dst_off, len, ret);
+                       continue;
+               }
 
-                       dma_srcs[i] = dma_map_single(dev->dev, buf, len,
-                                                    DMA_TO_DEVICE);
-                       ret = dma_mapping_error(dev->dev, dma_srcs[i]);
+               um->len = params->buf_size;
+               for (i = 0; i < src_cnt; i++) {
+                       unsigned long buf = (unsigned long) thread->srcs[i];
+                       struct page *pg = virt_to_page(buf);
+                       unsigned pg_off = buf & ~PAGE_MASK;
+
+                       um->addr[i] = dma_map_page(dev->dev, pg, pg_off,
+                                                  um->len, DMA_TO_DEVICE);
+                       srcs[i] = um->addr[i] + src_off;
+                       ret = dma_mapping_error(dev->dev, um->addr[i]);
                        if (ret) {
-                               unmap_src(dev->dev, dma_srcs, len, i);
+                               dmaengine_unmap_put(um);
                                result("src mapping error", total_tests,
                                       src_off, dst_off, len, ret);
                                failed_tests++;
                                continue;
                        }
+                       um->to_cnt++;
                }
                /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */
+               dsts = &um->addr[src_cnt];
                for (i = 0; i < dst_cnt; i++) {
-                       dma_dsts[i] = dma_map_single(dev->dev, thread->dsts[i],
-                                                    params->buf_size,
-                                                    DMA_BIDIRECTIONAL);
-                       ret = dma_mapping_error(dev->dev, dma_dsts[i]);
+                       unsigned long buf = (unsigned long) thread->dsts[i];
+                       struct page *pg = virt_to_page(buf);
+                       unsigned pg_off = buf & ~PAGE_MASK;
+
+                       dsts[i] = dma_map_page(dev->dev, pg, pg_off, um->len,
+                                              DMA_BIDIRECTIONAL);
+                       ret = dma_mapping_error(dev->dev, dsts[i]);
                        if (ret) {
-                               unmap_src(dev->dev, dma_srcs, len, src_cnt);
-                               unmap_dst(dev->dev, dma_dsts, params->buf_size,
-                                         i);
+                               dmaengine_unmap_put(um);
                                result("dst mapping error", total_tests,
                                       src_off, dst_off, len, ret);
                                failed_tests++;
                                continue;
                        }
+                       um->bidi_cnt++;
                }
 
                if (thread->type == DMA_MEMCPY)
                        tx = dev->device_prep_dma_memcpy(chan,
-                                                        dma_dsts[0] + dst_off,
-                                                        dma_srcs[0], len,
-                                                        flags);
+                                                        dsts[0] + dst_off,
+                                                        srcs[0], len, flags);
                else if (thread->type == DMA_XOR)
                        tx = dev->device_prep_dma_xor(chan,
-                                                     dma_dsts[0] + dst_off,
-                                                     dma_srcs, src_cnt,
+                                                     dsts[0] + dst_off,
+                                                     srcs, src_cnt,
                                                      len, flags);
                else if (thread->type == DMA_PQ) {
                        dma_addr_t dma_pq[dst_cnt];
 
                        for (i = 0; i < dst_cnt; i++)
-                               dma_pq[i] = dma_dsts[i] + dst_off;
-                       tx = dev->device_prep_dma_pq(chan, dma_pq, dma_srcs,
+                               dma_pq[i] = dsts[i] + dst_off;
+                       tx = dev->device_prep_dma_pq(chan, dma_pq, srcs,
                                                     src_cnt, pq_coefs,
                                                     len, flags);
                }
 
                if (!tx) {
-                       unmap_src(dev->dev, dma_srcs, len, src_cnt);
-                       unmap_dst(dev->dev, dma_dsts, params->buf_size,
-                                 dst_cnt);
+                       dmaengine_unmap_put(um);
                        result("prep error", total_tests, src_off,
                               dst_off, len, ret);
                        msleep(100);
@@ -561,6 +610,7 @@ static int dmatest_func(void *data)
                cookie = tx->tx_submit(tx);
 
                if (dma_submit_error(cookie)) {
+                       dmaengine_unmap_put(um);
                        result("submit error", total_tests, src_off,
                               dst_off, len, ret);
                        msleep(100);
@@ -583,11 +633,13 @@ static int dmatest_func(void *data)
                         * free it this time?" dancing.  For now, just
                         * leave it dangling.
                         */
+                       dmaengine_unmap_put(um);
                        result("test timed out", total_tests, src_off, dst_off,
                               len, 0);
                        failed_tests++;
                        continue;
-               } else if (status != DMA_SUCCESS) {
+               } else if (status != DMA_COMPLETE) {
+                       dmaengine_unmap_put(um);
                        result(status == DMA_ERROR ?
                               "completion error status" :
                               "completion busy status", total_tests, src_off,
@@ -596,13 +648,11 @@ static int dmatest_func(void *data)
                        continue;
                }
 
-               /* Unmap by myself */
-               unmap_src(dev->dev, dma_srcs, len, src_cnt);
-               unmap_dst(dev->dev, dma_dsts, params->buf_size, dst_cnt);
+               dmaengine_unmap_put(um);
 
                if (params->noverify) {
-                       dbg_result("test passed", total_tests, src_off, dst_off,
-                                  len, 0);
+                       verbose_result("test passed", total_tests, src_off,
+                                      dst_off, len, 0);
                        continue;
                }
 
@@ -631,8 +681,8 @@ static int dmatest_func(void *data)
                               len, error_count);
                        failed_tests++;
                } else {
-                       dbg_result("test passed", total_tests, src_off, dst_off,
-                                  len, 0);
+                       verbose_result("test passed", total_tests, src_off,
+                                      dst_off, len, 0);
                }
        }
        runtime = ktime_us_delta(ktime_get(), ktime);
@@ -660,12 +710,7 @@ err_thread_type:
                dmaengine_terminate_all(chan);
 
        thread->done = true;
-
-       if (params->iterations > 0)
-               while (!kthread_should_stop()) {
-                       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit);
-                       interruptible_sleep_on(&wait_dmatest_exit);
-               }
+       wake_up(&thread_wait);
 
        return ret;
 }
@@ -681,6 +726,7 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
                pr_debug("thread %s exited with status %d\n",
                         thread->task->comm, ret);
                list_del(&thread->node);
+               put_task_struct(thread->task);
                kfree(thread);
        }
 
@@ -719,18 +765,19 @@ static int dmatest_add_threads(struct dmatest_info *info,
                thread->chan = dtc->chan;
                thread->type = type;
                smp_wmb();
-               thread->task = kthread_run(dmatest_func, thread, "%s-%s%u",
+               thread->task = kthread_create(dmatest_func, thread, "%s-%s%u",
                                dma_chan_name(chan), op, i);
                if (IS_ERR(thread->task)) {
-                       pr_warn("Failed to run thread %s-%s%u\n",
+                       pr_warn("Failed to create thread %s-%s%u\n",
                                dma_chan_name(chan), op, i);
                        kfree(thread);
                        break;
                }
 
                /* srcbuf and dstbuf are allocated by the thread itself */
-
+               get_task_struct(thread->task);
                list_add_tail(&thread->node, &dtc->threads);
+               wake_up_process(thread->task);
        }
 
        return i;
@@ -863,22 +910,6 @@ static void restart_threaded_test(struct dmatest_info *info, bool run)
        run_threaded_test(info);
 }
 
-static bool is_threaded_test_run(struct dmatest_info *info)
-{
-       struct dmatest_chan *dtc;
-
-       list_for_each_entry(dtc, &info->channels, node) {
-               struct dmatest_thread *thread;
-
-               list_for_each_entry(thread, &dtc->threads, node) {
-                       if (!thread->done)
-                               return true;
-               }
-       }
-
-       return false;
-}
-
 static int dmatest_run_get(char *val, const struct kernel_param *kp)
 {
        struct dmatest_info *info = &test_info;
@@ -920,6 +951,7 @@ static int dmatest_run_set(const char *val, const struct kernel_param *kp)
 static int __init dmatest_init(void)
 {
        struct dmatest_info *info = &test_info;
+       struct dmatest_params *params = &info->params;
 
        if (dmatest_run) {
                mutex_lock(&info->lock);
@@ -927,6 +959,9 @@ static int __init dmatest_init(void)
                mutex_unlock(&info->lock);
        }
 
+       if (params->iterations && wait)
+               wait_event(thread_wait, !is_threaded_test_run(info));
+
        /* module parameters are stable, inittime tests are started,
         * let userspace take over 'run' control
         */