dma client test

在driver/dma/dmatest.c 可以测试dma client.
config DMATEST
    tristate "DMA Test client"
    depends on DMA_ENGINE
    select DMA_ENGINE_RAID
    help
      Simple DMA test client. Say N unless you're debugging a
      DMA Device driver.

#dmatest
obj-$(CONFIG_DMATEST) += dmatest.o

一般情况下会把dmatest.c build 成ko。这样的话,就可以通过下面三种方法给这个ko传递参数
Example of usage:
    % modprobe dmatest channel=dma0chan0 timeout=2000 iterations=1 run=1

...or:
    % modprobe dmatest
    % echo dma0chan0 > /sys/module/dmatest/parameters/channel
    % echo 2000 > /sys/module/dmatest/parameters/timeout
    % echo 1 > /sys/module/dmatest/parameters/iterations
    % echo 1 > /sys/module/dmatest/parameters/run

...or on the kernel command line:

    dmatest.channel=dma0chan0 dmatest.timeout=2000 dmatest.iterations=1 dmatest.run=1
static int __init dmatest_init(void)
{
    struct dmatest_info *info = &test_info;
    struct dmatest_params *params = &info->params;
//由于dmatest.run=1,所以这里的dmatest_run 为1,所以调用run_threaded_test
    if (dmatest_run) {
        mutex_lock(&info->lock);
        run_threaded_test(info);
        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
     */
    info->did_init = true;

    return 0;
}
/* when compiled-in wait for drivers to load first */
late_initcall(dmatest_init);

static void run_threaded_test(struct dmatest_info *info)
{
    struct dmatest_params *params = &info->params;
这段主要是设置参数,有点参数是外部传递过来的。例如timeout
    /* Copy test parameters */
    params->buf_size = test_buf_size;
    strlcpy(params->channel, strim(test_channel), sizeof(params->channel));
    strlcpy(params->device, strim(test_device), sizeof(params->device));
    params->threads_per_chan = threads_per_chan;
    params->max_channels = max_channels;
    params->iterations = iterations;
    params->xor_sources = xor_sources;
    params->pq_sources = pq_sources;
    params->timeout = timeout;
    params->noverify = noverify;
// 可以看到主要测试client的memcpy/xor/sg/pq
    request_channels(info, DMA_MEMCPY);
    request_channels(info, DMA_XOR);
    request_channels(info, DMA_SG);
    request_channels(info, DMA_PQ);
}

这里以memcpy 为例
static void request_channels(struct dmatest_info *info,
                 enum dma_transaction_type type)
{
    dma_cap_mask_t mask;
//将dma_cap_mask_t mask 先清0再全部置1
    dma_cap_zero(mask);
    dma_cap_set(type, mask);
    for (;;) {
        struct dmatest_params *params = &info->params;
        struct dma_chan *chan;
//使用dma client的时候需要先申请一个dma channel
        chan = dma_request_channel(mask, filter, params);
        if (chan) {
//申请dma channel 成功后调用dmatest_add_channel 测试
            if (dmatest_add_channel(info, chan)) {
                dma_release_channel(chan);
                break; /* add_channel failed, punt */
            }
        } else
            break; /* no more channels available */
        if (params->max_channels &&
            info->nr_channels >= params->max_channels)
            break; /* we have all we need */
    }
}
static int dmatest_add_channel(struct dmatest_info *info,
        struct dma_chan *chan)
{
    struct dmatest_chan    *dtc;
    struct dma_device    *dma_dev = chan->device;
    unsigned int        thread_count = 0;
    int cnt;

    dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL);
    if (!dtc) {
        pr_warn("No memory for %s\n", dma_chan_name(chan));
        return -ENOMEM;
    }

    dtc->chan = chan;
    INIT_LIST_HEAD(&dtc->threads);
//检查dma controller是否有能力做DMA_MEMCPY,如果有的话,调用dmatest_add_threads 测试
    if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
        if (dmatest == 0) {
            cnt = dmatest_add_threads(info, dtc, DMA_MEMCPY);
            thread_count += cnt > 0 ? cnt : 0;
        }
    }

    pr_info("Started %u threads using %s\n",
        thread_count, dma_chan_name(chan));

    list_add_tail(&dtc->node, &info->channels);
    info->nr_channels++;

    return 0;
}

static int dmatest_add_threads(struct dmatest_info *info,
        struct dmatest_chan *dtc, enum dma_transaction_type type)
{
    struct dmatest_params *params = &info->params;
    struct dmatest_thread *thread;
    struct dma_chan *chan = dtc->chan;
    char *op;
    unsigned int i;
//这里的op是copy
    if (type == DMA_MEMCPY)
        op = "copy";
    else if (type == DMA_SG)
        op = "sg";
    else if (type == DMA_XOR)
        op = "xor";
    else if (type == DMA_PQ)
        op = "pq";
    else
        return -EINVAL;

    for (i = 0; i < params->threads_per_chan; i++) {
        thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL);
        if (!thread) {
            pr_warn("No memory for %s-%s%u\n",
                dma_chan_name(chan), op, i);
            break;
        }
        thread->info = info;
        thread->chan = dtc->chan;
        thread->type = type;
        smp_wmb();
//新建一个thread dmatest_func
        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 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);
//让thread->task 这个thread 开始工作
        wake_up_process(thread->task);
    }

    return i;
}
在dmatest_func 中按照下面四步来测试memcpy
1:通过dmaengine_get_unmap_data申请一个    struct dmaengine_unmap_data *um;

um = dmaengine_get_unmap_data(dev->dev, src_cnt + dst_cnt,  GFP_KERNEL);
2:调用dma_map_page 分别映射要memcpy的原地址和目的地址

        for (i = 0; i < src_cnt; i++) {
            void *buf = thread->srcs[i];
            struct page *pg = virt_to_page(buf);
            unsigned long pg_off = offset_in_page(buf);

            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) {
                dmaengine_unmap_put(um);
                result("src mapping error", total_tests,
                       src_off, dst_off, len, ret);
                failed_tests++;
                continue;
            }
            um->to_cnt++;
        }

3:调用device_prep_dma_memcpy 准备开始发送的地址
        if (thread->type == DMA_MEMCPY)
            tx = dev->device_prep_dma_memcpy(chan,
                             dsts[0] + dst_off,
                             srcs[0], len, flags);
4:设置一个一个callback这样当dma 完成memcpy操作的时候会调用这个callback函数

        done.done = false;
        tx->callback = dmatest_callback;
        tx->callback_param = &done;
        cookie = tx->tx_submit(tx);
5:调用    dma_async_issue_pending(chan);来是memcpy的操作
6:通过wait queue等待操作完成,
wait_event_freezable_timeout(done_wait, done.done,  msecs_to_jiffies(params->timeout));

这个wait queue在dmatest_func 刚开始时初始化
    DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_wait);
    struct dmatest_thread    *thread = data;
    struct dmatest_done    done = { .wait = &done_wait };

最终在dmatest_callback中 唤醒wait queue
static void dmatest_callback(void *arg)
{
    struct dmatest_done *done = arg;

    done->done = true;
    wake_up_all(done->wait);
}
7:最后通过        status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
来得到memcpy操作的结果,正常情况下应该是DMA_COMPLETE



  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值