Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 133e2a31 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx:
  dma: Add SoF and EoF debugging to ipu_idmac.c, minor cleanup
  dw_dmac: add cyclic API to DW DMA driver
  dmaengine: Add privatecnt to revert DMA_PRIVATE property
  dmatest: add dma interrupts and callbacks
  dmatest: add xor test
  dmaengine: allow dma support for async_tx to be toggled
  async_tx: provide __async_inline for HAS_DMA=n archs
  dmaengine: kill some unused headers
  dmaengine: initialize tx_list in dma_async_tx_descriptor_init
  dma: i.MX31 IPU DMA robustness improvements
  dma: improve section assignment in i.MX31 IPU DMA driver
  dma: ipu_idmac driver cosmetic clean-up
  dmaengine: fail device registration if channel registration fails
parents 20bec8ab 8c6db1bb
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@
#ifdef CONFIG_DMA_ENGINE
static int __init async_tx_init(void)
{
	dmaengine_get();
	async_dmaengine_get();

	printk(KERN_INFO "async_tx: api initialized (async)\n");

@@ -39,7 +39,7 @@ static int __init async_tx_init(void)

static void __exit async_tx_exit(void)
{
	dmaengine_put();
	async_dmaengine_put();
}

/**
@@ -56,7 +56,7 @@ __async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx,
	if (depend_tx &&
	    dma_has_cap(tx_type, depend_tx->chan->device->cap_mask))
		return depend_tx->chan;
	return dma_find_channel(tx_type);
	return async_dma_find_channel(tx_type);
}
EXPORT_SYMBOL_GPL(__async_tx_find_channel);
#else
+2 −5
Original line number Diff line number Diff line
@@ -30,11 +30,8 @@
#include <linux/raid/xor.h>
#include <linux/async_tx.h>

/* do_async_xor - dma map the pages and perform the xor with an engine.
 * 	This routine is marked __always_inline so it can be compiled away
 * 	when CONFIG_DMA_ENGINE=n
 */
static __always_inline struct dma_async_tx_descriptor *
/* do_async_xor - dma map the pages and perform the xor with an engine */
static __async_inline struct dma_async_tx_descriptor *
do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
	     unsigned int offset, int src_cnt, size_t len,
	     enum async_tx_flags flags,
+11 −0
Original line number Diff line number Diff line
@@ -98,6 +98,17 @@ config NET_DMA
	  Say Y here if you enabled INTEL_IOATDMA or FSL_DMA, otherwise
	  say N.

config ASYNC_TX_DMA
	bool "Async_tx: Offload support for the async_tx api"
	depends on DMA_ENGINE
	help
	  This allows the async_tx api to take advantage of offload engines for
	  memcpy, memset, xor, and raid6 p+q operations.  If your platform has
	  a dma engine that can perform raid operations and you have enabled
	  MD_RAID456 say Y.

	  If unsure, say N.

config DMATEST
	tristate "DMA Test client"
	depends on DMA_ENGINE
+48 −12
Original line number Diff line number Diff line
@@ -507,6 +507,7 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
			 * published in the general-purpose allocator
			 */
			dma_cap_set(DMA_PRIVATE, device->cap_mask);
			device->privatecnt++;
			err = dma_chan_get(chan);

			if (err == -ENODEV) {
@@ -518,6 +519,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
				       dma_chan_name(chan), err);
			else
				break;
			if (--device->privatecnt == 0)
				dma_cap_clear(DMA_PRIVATE, device->cap_mask);
			chan->private = NULL;
			chan = NULL;
		}
@@ -537,6 +540,9 @@ void dma_release_channel(struct dma_chan *chan)
	WARN_ONCE(chan->client_count != 1,
		  "chan reference count %d != 1\n", chan->client_count);
	dma_chan_put(chan);
	/* drop PRIVATE cap enabled by __dma_request_channel() */
	if (--chan->device->privatecnt == 0)
		dma_cap_clear(DMA_PRIVATE, chan->device->cap_mask);
	chan->private = NULL;
	mutex_unlock(&dma_list_mutex);
}
@@ -602,6 +608,24 @@ void dmaengine_put(void)
}
EXPORT_SYMBOL(dmaengine_put);

static int get_dma_id(struct dma_device *device)
{
	int rc;

 idr_retry:
	if (!idr_pre_get(&dma_idr, GFP_KERNEL))
		return -ENOMEM;
	mutex_lock(&dma_list_mutex);
	rc = idr_get_new(&dma_idr, NULL, &device->dev_id);
	mutex_unlock(&dma_list_mutex);
	if (rc == -EAGAIN)
		goto idr_retry;
	else if (rc != 0)
		return rc;

	return 0;
}

/**
 * dma_async_device_register - registers DMA devices found
 * @device: &dma_device
@@ -640,27 +664,25 @@ int dma_async_device_register(struct dma_device *device)
	idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL);
	if (!idr_ref)
		return -ENOMEM;
	atomic_set(idr_ref, 0);
 idr_retry:
	if (!idr_pre_get(&dma_idr, GFP_KERNEL))
		return -ENOMEM;
	mutex_lock(&dma_list_mutex);
	rc = idr_get_new(&dma_idr, NULL, &device->dev_id);
	mutex_unlock(&dma_list_mutex);
	if (rc == -EAGAIN)
		goto idr_retry;
	else if (rc != 0)
	rc = get_dma_id(device);
	if (rc != 0) {
		kfree(idr_ref);
		return rc;
	}

	atomic_set(idr_ref, 0);

	/* represent channels in sysfs. Probably want devs too */
	list_for_each_entry(chan, &device->channels, device_node) {
		rc = -ENOMEM;
		chan->local = alloc_percpu(typeof(*chan->local));
		if (chan->local == NULL)
			continue;
			goto err_out;
		chan->dev = kzalloc(sizeof(*chan->dev), GFP_KERNEL);
		if (chan->dev == NULL) {
			free_percpu(chan->local);
			continue;
			chan->local = NULL;
			goto err_out;
		}

		chan->chan_id = chancnt++;
@@ -677,6 +699,8 @@ int dma_async_device_register(struct dma_device *device)
		if (rc) {
			free_percpu(chan->local);
			chan->local = NULL;
			kfree(chan->dev);
			atomic_dec(idr_ref);
			goto err_out;
		}
		chan->client_count = 0;
@@ -701,12 +725,23 @@ int dma_async_device_register(struct dma_device *device)
			}
		}
	list_add_tail_rcu(&device->global_node, &dma_device_list);
	if (dma_has_cap(DMA_PRIVATE, device->cap_mask))
		device->privatecnt++;	/* Always private */
	dma_channel_rebalance();
	mutex_unlock(&dma_list_mutex);

	return 0;

err_out:
	/* if we never registered a channel just release the idr */
	if (atomic_read(idr_ref) == 0) {
		mutex_lock(&dma_list_mutex);
		idr_remove(&dma_idr, device->dev_id);
		mutex_unlock(&dma_list_mutex);
		kfree(idr_ref);
		return rc;
	}

	list_for_each_entry(chan, &device->channels, device_node) {
		if (chan->local == NULL)
			continue;
@@ -893,6 +928,7 @@ void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx,
{
	tx->chan = chan;
	spin_lock_init(&tx->lock);
	INIT_LIST_HEAD(&tx->tx_list);
}
EXPORT_SYMBOL(dma_async_tx_descriptor_init);

+214 −93
Original line number Diff line number Diff line
@@ -38,6 +38,11 @@ module_param(max_channels, uint, S_IRUGO);
MODULE_PARM_DESC(max_channels,
		"Maximum number of channels to use (default: all)");

static unsigned int xor_sources = 3;
module_param(xor_sources, uint, S_IRUGO);
MODULE_PARM_DESC(xor_sources,
		"Number of xor source buffers (default: 3)");

/*
 * Initialization patterns. All bytes in the source buffer has bit 7
 * set, all bytes in the destination buffer has bit 7 cleared.
@@ -59,8 +64,9 @@ struct dmatest_thread {
	struct list_head	node;
	struct task_struct	*task;
	struct dma_chan		*chan;
	u8			*srcbuf;
	u8			*dstbuf;
	u8			**srcs;
	u8			**dsts;
	enum dma_transaction_type type;
};

struct dmatest_chan {
@@ -98,10 +104,12 @@ static unsigned long dmatest_random(void)
	return buf;
}

static void dmatest_init_srcbuf(u8 *buf, unsigned int start, unsigned int len)
static void dmatest_init_srcs(u8 **bufs, unsigned int start, unsigned int len)
{
	unsigned int i;
	u8 *buf;

	for (; (buf = *bufs); bufs++) {
		for (i = 0; i < start; i++)
			buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK);
		for ( ; i < start + len; i++)
@@ -109,12 +117,16 @@ static void dmatest_init_srcbuf(u8 *buf, unsigned int start, unsigned int len)
				| (~i & PATTERN_COUNT_MASK);;
		for ( ; i < test_buf_size; i++)
			buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK);
		buf++;
	}
}

static void dmatest_init_dstbuf(u8 *buf, unsigned int start, unsigned int len)
static void dmatest_init_dsts(u8 **bufs, unsigned int start, unsigned int len)
{
	unsigned int i;
	u8 *buf;

	for (; (buf = *bufs); bufs++) {
		for (i = 0; i < start; i++)
			buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK);
		for ( ; i < start + len; i++)
@@ -123,6 +135,7 @@ static void dmatest_init_dstbuf(u8 *buf, unsigned int start, unsigned int len)
		for ( ; i < test_buf_size; i++)
			buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK);
	}
}

static void dmatest_mismatch(u8 actual, u8 pattern, unsigned int index,
		unsigned int counter, bool is_srcbuf)
@@ -150,24 +163,31 @@ static void dmatest_mismatch(u8 actual, u8 pattern, unsigned int index,
				thread_name, index, expected, actual);
}

static unsigned int dmatest_verify(u8 *buf, unsigned int start,
static unsigned int dmatest_verify(u8 **bufs, unsigned int start,
		unsigned int end, unsigned int counter, u8 pattern,
		bool is_srcbuf)
{
	unsigned int i;
	unsigned int error_count = 0;
	u8 actual;
	u8 expected;
	u8 *buf;
	unsigned int counter_orig = counter;

	for (; (buf = *bufs); bufs++) {
		counter = counter_orig;
		for (i = start; i < end; i++) {
			actual = buf[i];
		if (actual != (pattern | (~counter & PATTERN_COUNT_MASK))) {
			expected = pattern | (~counter & PATTERN_COUNT_MASK);
			if (actual != expected) {
				if (error_count < 32)
				dmatest_mismatch(actual, pattern, i, counter,
						is_srcbuf);
					dmatest_mismatch(actual, pattern, i,
							 counter, is_srcbuf);
				error_count++;
			}
			counter++;
		}
	}

	if (error_count > 32)
		pr_warning("%s: %u errors suppressed\n",
@@ -176,12 +196,17 @@ static unsigned int dmatest_verify(u8 *buf, unsigned int start,
	return error_count;
}

static void dmatest_callback(void *completion)
{
	complete(completion);
}

/*
 * This function repeatedly tests DMA transfers of various lengths and
 * offsets until it is told to exit by kthread_stop(). There may be
 * multiple threads running this function in parallel for a single
 * channel, and there may be multiple channels being tested in
 * parallel.
 * offsets for a given operation type until it is told to exit by
 * kthread_stop(). There may be multiple threads running this function
 * in parallel for a single channel, and there may be multiple channels
 * being tested in parallel.
 *
 * Before each test, the source and destination buffer is initialized
 * with a known pattern. This pattern is different depending on
@@ -201,25 +226,57 @@ static int dmatest_func(void *data)
	unsigned int		total_tests = 0;
	dma_cookie_t		cookie;
	enum dma_status		status;
	enum dma_ctrl_flags 	flags;
	int			ret;
	int			src_cnt;
	int			dst_cnt;
	int			i;

	thread_name = current->comm;

	ret = -ENOMEM;
	thread->srcbuf = kmalloc(test_buf_size, GFP_KERNEL);
	if (!thread->srcbuf)
		goto err_srcbuf;
	thread->dstbuf = kmalloc(test_buf_size, GFP_KERNEL);
	if (!thread->dstbuf)
		goto err_dstbuf;

	smp_rmb();
	chan = thread->chan;
	if (thread->type == DMA_MEMCPY)
		src_cnt = dst_cnt = 1;
	else if (thread->type == DMA_XOR) {
		src_cnt = xor_sources | 1; /* force odd to ensure dst = src */
		dst_cnt = 1;
	} else
		goto err_srcs;

	thread->srcs = kcalloc(src_cnt+1, sizeof(u8 *), GFP_KERNEL);
	if (!thread->srcs)
		goto err_srcs;
	for (i = 0; i < src_cnt; i++) {
		thread->srcs[i] = kmalloc(test_buf_size, GFP_KERNEL);
		if (!thread->srcs[i])
			goto err_srcbuf;
	}
	thread->srcs[i] = NULL;

	thread->dsts = kcalloc(dst_cnt+1, sizeof(u8 *), GFP_KERNEL);
	if (!thread->dsts)
		goto err_dsts;
	for (i = 0; i < dst_cnt; i++) {
		thread->dsts[i] = kmalloc(test_buf_size, GFP_KERNEL);
		if (!thread->dsts[i])
			goto err_dstbuf;
	}
	thread->dsts[i] = NULL;

	set_user_nice(current, 10);

	flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT;

	while (!kthread_should_stop()) {
		struct dma_device *dev = chan->device;
		struct dma_async_tx_descriptor *tx;
		dma_addr_t dma_src, dma_dest;
		struct dma_async_tx_descriptor *tx = NULL;
		dma_addr_t dma_srcs[src_cnt];
		dma_addr_t dma_dsts[dst_cnt];
		struct completion cmp;
		unsigned long tmo = msecs_to_jiffies(3000);

		total_tests++;

@@ -227,22 +284,41 @@ static int dmatest_func(void *data)
		src_off = dmatest_random() % (test_buf_size - len + 1);
		dst_off = dmatest_random() % (test_buf_size - len + 1);

		dmatest_init_srcbuf(thread->srcbuf, src_off, len);
		dmatest_init_dstbuf(thread->dstbuf, dst_off, len);
		dmatest_init_srcs(thread->srcs, src_off, len);
		dmatest_init_dsts(thread->dsts, dst_off, len);

		dma_src = dma_map_single(dev->dev, thread->srcbuf + src_off,
				len, DMA_TO_DEVICE);
		for (i = 0; i < src_cnt; i++) {
			u8 *buf = thread->srcs[i] + src_off;

			dma_srcs[i] = dma_map_single(dev->dev, buf, len,
						     DMA_TO_DEVICE);
		}
		/* map with DMA_BIDIRECTIONAL to force writeback/invalidate */
		dma_dest = dma_map_single(dev->dev, thread->dstbuf,
				test_buf_size, DMA_BIDIRECTIONAL);
		for (i = 0; i < dst_cnt; i++) {
			dma_dsts[i] = dma_map_single(dev->dev, thread->dsts[i],
						     test_buf_size,
						     DMA_BIDIRECTIONAL);
		}

		if (thread->type == DMA_MEMCPY)
			tx = dev->device_prep_dma_memcpy(chan,
							 dma_dsts[0] + dst_off,
							 dma_srcs[0], len,
							 flags);
		else if (thread->type == DMA_XOR)
			tx = dev->device_prep_dma_xor(chan,
						      dma_dsts[0] + dst_off,
						      dma_srcs, xor_sources,
						      len, flags);

		tx = dev->device_prep_dma_memcpy(chan, dma_dest + dst_off,
				dma_src, len,
				DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP);
		if (!tx) {
			dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE);
			dma_unmap_single(dev->dev, dma_dest,
					test_buf_size, DMA_BIDIRECTIONAL);
			for (i = 0; i < src_cnt; i++)
				dma_unmap_single(dev->dev, dma_srcs[i], len,
						 DMA_TO_DEVICE);
			for (i = 0; i < dst_cnt; i++)
				dma_unmap_single(dev->dev, dma_dsts[i],
						 test_buf_size,
						 DMA_BIDIRECTIONAL);
			pr_warning("%s: #%u: prep error with src_off=0x%x "
					"dst_off=0x%x len=0x%x\n",
					thread_name, total_tests - 1,
@@ -251,7 +327,10 @@ static int dmatest_func(void *data)
			failed_tests++;
			continue;
		}
		tx->callback = NULL;

		init_completion(&cmp);
		tx->callback = dmatest_callback;
		tx->callback_param = &cmp;
		cookie = tx->tx_submit(tx);

		if (dma_submit_error(cookie)) {
@@ -263,44 +342,50 @@ static int dmatest_func(void *data)
			failed_tests++;
			continue;
		}
		dma_async_memcpy_issue_pending(chan);
		dma_async_issue_pending(chan);

		do {
			msleep(1);
			status = dma_async_memcpy_complete(
					chan, cookie, NULL, NULL);
		} while (status == DMA_IN_PROGRESS);
		tmo = wait_for_completion_timeout(&cmp, tmo);
		status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);

		if (status == DMA_ERROR) {
			pr_warning("%s: #%u: error during copy\n",
		if (tmo == 0) {
			pr_warning("%s: #%u: test timed out\n",
				   thread_name, total_tests - 1);
			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");
			failed_tests++;
			continue;
		}

		/* Unmap by myself (see DMA_COMPL_SKIP_DEST_UNMAP above) */
		dma_unmap_single(dev->dev, dma_dest,
				test_buf_size, DMA_BIDIRECTIONAL);
		for (i = 0; i < dst_cnt; i++)
			dma_unmap_single(dev->dev, dma_dsts[i], test_buf_size,
					 DMA_BIDIRECTIONAL);

		error_count = 0;

		pr_debug("%s: verifying source buffer...\n", thread_name);
		error_count += dmatest_verify(thread->srcbuf, 0, src_off,
		error_count += dmatest_verify(thread->srcs, 0, src_off,
				0, PATTERN_SRC, true);
		error_count += dmatest_verify(thread->srcbuf, src_off,
		error_count += dmatest_verify(thread->srcs, src_off,
				src_off + len, src_off,
				PATTERN_SRC | PATTERN_COPY, true);
		error_count += dmatest_verify(thread->srcbuf, src_off + len,
		error_count += dmatest_verify(thread->srcs, src_off + len,
				test_buf_size, src_off + len,
				PATTERN_SRC, true);

		pr_debug("%s: verifying dest buffer...\n",
				thread->task->comm);
		error_count += dmatest_verify(thread->dstbuf, 0, dst_off,
		error_count += dmatest_verify(thread->dsts, 0, dst_off,
				0, PATTERN_DST, false);
		error_count += dmatest_verify(thread->dstbuf, dst_off,
		error_count += dmatest_verify(thread->dsts, dst_off,
				dst_off + len, src_off,
				PATTERN_SRC | PATTERN_COPY, false);
		error_count += dmatest_verify(thread->dstbuf, dst_off + len,
		error_count += dmatest_verify(thread->dsts, dst_off + len,
				test_buf_size, dst_off + len,
				PATTERN_DST, false);

@@ -319,10 +404,16 @@ static int dmatest_func(void *data)
	}

	ret = 0;
	kfree(thread->dstbuf);
	for (i = 0; thread->dsts[i]; i++)
		kfree(thread->dsts[i]);
err_dstbuf:
	kfree(thread->srcbuf);
	kfree(thread->dsts);
err_dsts:
	for (i = 0; thread->srcs[i]; i++)
		kfree(thread->srcs[i]);
err_srcbuf:
	kfree(thread->srcs);
err_srcs:
	pr_notice("%s: terminating after %u tests, %u failures (status %d)\n",
			thread_name, total_tests, failed_tests, ret);
	return ret;
@@ -344,35 +435,36 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
	kfree(dtc);
}

static int dmatest_add_channel(struct dma_chan *chan)
static int dmatest_add_threads(struct dmatest_chan *dtc, enum dma_transaction_type type)
{
	struct dmatest_chan	*dtc;
	struct dmatest_thread *thread;
	struct dma_chan *chan = dtc->chan;
	char *op;
	unsigned int i;

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

	dtc->chan = chan;
	INIT_LIST_HEAD(&dtc->threads);
	if (type == DMA_MEMCPY)
		op = "copy";
	else if (type == DMA_XOR)
		op = "xor";
	else
		return -EINVAL;

	for (i = 0; i < threads_per_chan; i++) {
		thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL);
		if (!thread) {
			pr_warning("dmatest: No memory for %s-test%u\n",
				   dma_chan_name(chan), i);
			pr_warning("dmatest: No memory for %s-%s%u\n",
				   dma_chan_name(chan), op, i);

			break;
		}
		thread->chan = dtc->chan;
		thread->type = type;
		smp_wmb();
		thread->task = kthread_run(dmatest_func, thread, "%s-test%u",
				dma_chan_name(chan), i);
		thread->task = kthread_run(dmatest_func, thread, "%s-%s%u",
				dma_chan_name(chan), op, i);
		if (IS_ERR(thread->task)) {
			pr_warning("dmatest: Failed to run thread %s-test%u\n",
					dma_chan_name(chan), i);
			pr_warning("dmatest: Failed to run thread %s-%s%u\n",
					dma_chan_name(chan), op, i);
			kfree(thread);
			break;
		}
@@ -382,7 +474,36 @@ static int dmatest_add_channel(struct dma_chan *chan)
		list_add_tail(&thread->node, &dtc->threads);
	}

	pr_info("dmatest: Started %u threads using %s\n", i, dma_chan_name(chan));
	return i;
}

static int dmatest_add_channel(struct dma_chan *chan)
{
	struct dmatest_chan	*dtc;
	struct dma_device	*dma_dev = chan->device;
	unsigned int		thread_count = 0;
	unsigned int		cnt;

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

	dtc->chan = chan;
	INIT_LIST_HEAD(&dtc->threads);

	if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
		cnt = dmatest_add_threads(dtc, DMA_MEMCPY);
		thread_count += cnt > 0 ?: 0;
	}
	if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
		cnt = dmatest_add_threads(dtc, DMA_XOR);
		thread_count += cnt > 0 ?: 0;
	}

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

	list_add_tail(&dtc->node, &dmatest_channels);
	nr_channels++;
Loading