All of lore.kernel.org
 help / color / mirror / Atom feed
From: Maxime Ripard <maxime.ripard@free-electrons.com>
To: Ludovic Desroches <ludovic.desroches@atmel.com>
Cc: Vinod Koul <vinod.koul@intel.com>,
	dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org,
	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Subject: Re: [PATCH RFC 1/2] dmaengine: Introduce scheduled DMA framework
Date: Mon, 23 Mar 2015 12:07:27 -0700	[thread overview]
Message-ID: <20150323190727.GD15676@lukather> (raw)
In-Reply-To: <20150323132513.GF32259@odux.rfo.atmel.com>

[-- Attachment #1: Type: text/plain, Size: 10791 bytes --]

Hi Ludo,

On Mon, Mar 23, 2015 at 02:25:13PM +0100, Ludovic Desroches wrote:
> On Sat, Mar 21, 2015 at 12:42:06PM -0700, Maxime Ripard wrote:
> > This framework aims at easing the development of dmaengine drivers by providing
> > generic implementations of the functions usually required by dmaengine, while
> > abstracting away most of the logic required.
> 
> For sure it will ease writing new dmaengine drivers! Moreover, adopting
> this framework will convert the driver to use virtual channels isn't it?

Yes, it relies on virt-dma

> > It is very relevant for controllers that have more requests than channels,
> > where you need to have some scheduling that is usually very bug prone, and
> > shouldn't be implemented in each and every driver.
> > 
> > This introduces a new set of callbacks for drivers to implement the device
> > specific behaviour. These new sets of callbacks aims at providing both how to
> > handle channel related operations (start the transfer of a new descriptor,
> > pause, resume, etc.) and the LLI related operations (iterator and various
> > accessors).
> > 
> > So far, the only transfer types supported are memcpy and slave transfers, but
> > eventually should support new transfer types as new drivers get converted.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> >  drivers/dma/Kconfig         |   4 +
> >  drivers/dma/Makefile        |   1 +
> >  drivers/dma/scheduled-dma.c | 571 ++++++++++++++++++++++++++++++++++++++++++++
> >  drivers/dma/scheduled-dma.h | 140 +++++++++++
> >  4 files changed, 716 insertions(+)
> >  create mode 100644 drivers/dma/scheduled-dma.c
> >  create mode 100644 drivers/dma/scheduled-dma.h
> > 
> > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> > index a874b6ec6650..032bf5fcd58b 100644
> > --- a/drivers/dma/Kconfig
> > +++ b/drivers/dma/Kconfig
> > @@ -406,6 +406,7 @@ config DMA_SUN6I
> >  	depends on RESET_CONTROLLER
> >  	select DMA_ENGINE
> >  	select DMA_VIRTUAL_CHANNELS
> > +	select DMA_SCHEDULED
> >  	help
> >  	  Support for the DMA engine first found in Allwinner A31 SoCs.
> >  
> > @@ -431,6 +432,9 @@ config DMA_ENGINE
> >  config DMA_VIRTUAL_CHANNELS
> >  	tristate
> >  
> > +config DMA_SCHEDULED
> > +	bool
> > +
> 
> I think, it should select DMA_VIRTUAL_CHANNELS

Yep, indeed.

> >  config DMA_ACPI
> >  	def_bool y
> >  	depends on ACPI
> > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> > index f915f61ec574..1db31814c749 100644
> > --- a/drivers/dma/Makefile
> > +++ b/drivers/dma/Makefile
> > @@ -5,6 +5,7 @@ obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
> >  obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
> >  obj-$(CONFIG_DMA_ACPI) += acpi-dma.o
> >  obj-$(CONFIG_DMA_OF) += of-dma.o
> > +obj-$(CONFIG_DMA_SCHEDULED) += scheduled-dma.o
> >  
> >  obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
> >  obj-$(CONFIG_DMATEST) += dmatest.o
> > diff --git a/drivers/dma/scheduled-dma.c b/drivers/dma/scheduled-dma.c
> > new file mode 100644
> > index 000000000000..482d04f2ccbc
> > --- /dev/null
> > +++ b/drivers/dma/scheduled-dma.c
> > @@ -0,0 +1,571 @@
> > +/*
> > + * Copyright (C) 2015 Maxime Ripard
> > + * Maxime Ripard <maxime.ripard@free-electrons.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + */
> > +
> > +#include <linux/dmaengine.h>
> > +#include <linux/dmapool.h>
> > +#include <linux/list.h>
> > +#include <linux/slab.h>
> > +
> > +#include "scheduled-dma.h"
> > +#include "virt-dma.h"
> > +
> > +static struct sdma_request *sdma_pop_queued_transfer(struct sdma *sdma,
> > +						     struct sdma_channel *schan)
> > +{
> > +	struct sdma_request *sreq = NULL;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&sdma->lock, flags);
> > +
> > +	/* No requests are awaiting an available channel */
> > +	if (list_empty(&sdma->pend_reqs))
> > +		goto out;
> > +
> > +	/*
> > +	 * If we don't have any validate_request callback, any request
> > +	 * can be dispatched to any channel.
> > +	 *
> > +	 * Remove the first entry and return it.
> > +	 */
> > +	if (!sdma->ops->validate_request) {
> > +		sreq = list_first_entry(&sdma->pend_reqs,
> > +					struct sdma_request, node);
> > +		list_del_init(&sreq->node);
> > +		goto out;
> > +	}
> > +
> > +	list_for_each_entry(sreq, &sdma->pend_reqs, node) {
> > +		/*
> > +		 * Ask the driver to validate that the request can
> > +		 * happen on the channel.
> > +		 */
> > +		if (sdma->ops->validate_request(schan, sreq)) {
> > +			list_del_init(&sreq->node);
> > +			goto out;
> > +		}
> > +
> > +		/* Otherwise, just keep looping */
> > +	}
> > +
> > +out:
> > +	spin_unlock_irqrestore(&sdma->lock, flags);
> > +
> > +	return sreq;
> > +}
> > +
> > +/*
> > + * Besoin d'une fonction pour pusher un descriptor vers un pchan
> > + *
> > + * Flow normal:
> > + *   - Election d'un pchan (Framework)
> > + *   - Push d'un descripteur vers le pchan (Driver)
> > + *   - idle....
> > + *   - interrupt (driver)
> > + *   - Transfert terminé, notification vers le framework (driver)
> > + *   - Nouveau transfert dans la queue?
> > + *     + Si oui, on est reparti
> > + *     + Si non, on sort de l'interrupt, le pchan est libéré
> > + */
> > +
> > +struct sdma_desc *sdma_report(struct sdma *sdma,
> > +			      struct sdma_channel *schan,
> > +			      enum sdma_report_status status)
> > +{
> > +	struct sdma_desc *sdesc = NULL;
> > +	struct virt_dma_desc *vdesc;
> > +	struct sdma_request *sreq;
> > +
> > +	switch (status) {
> > +	case SDMA_REPORT_TRANSFER:
> > +		/*
> > +		 * We're done with the current transfer that was in this
> > +		 * physical channel.
> > +		 */
> > +		vchan_cookie_complete(&schan->desc->vdesc);
> > +
> > +		/*
> > +		 * Now, try to see if there's any queued transfer
> > +		 * awaiting an available channel.
> > +		 *
> > +		 * If not, just bail out, and mark the pchan as
> > +		 * available.
> > +		 *
> > +		 * If there's such a transfer, validate that the
> > +		 * driver can handle it, and ask it to do the
> > +		 * transfer.
> > +		 */
> > +		sreq = sdma_pop_queued_transfer(sdma, schan);
> > +		if (!sreq) {
> > +			list_add_tail(&schan->node, &sdma->avail_chans);
> > +			return NULL;
> > +		}
> > +
> > +		/* Mark the request as assigned to a particular channel */
> > +		sreq->chan = schan;
> > +
> > +		/* Retrieve the next transfer descriptor */
> > +		vdesc = vchan_next_desc(&sreq->vchan);
> > +		schan->desc = sdesc = to_sdma_desc(&vdesc->tx);
> > +
> > +		break;
> > +
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return sdesc;
> > +}
> > +EXPORT_SYMBOL_GPL(sdma_report);
> > +
> > +static enum dma_status sdma_tx_status(struct dma_chan *chan,
> > +				      dma_cookie_t cookie,
> > +				      struct dma_tx_state *state)
> > +{
> > +	struct sdma_request *sreq = to_sdma_request(chan);
> > +	struct sdma *sdma = to_sdma(chan->device);
> > +	struct sdma_channel *schan = sreq->chan;
> > +	struct virt_dma_desc *vd;
> > +	struct sdma_desc *desc;
> > +	enum dma_status ret;
> > +	unsigned long flags;
> > +	size_t bytes = 0;
> > +	void *lli;
> > +
> > +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> > +
> > +	ret = dma_cookie_status(chan, cookie, state);
> > +	if (ret == DMA_COMPLETE)
> > +		goto out;
> > +
> > +	vd = vchan_find_desc(&sreq->vchan, cookie);
> > +	desc = to_sdma_desc(&vd->tx);
> > +
> > +	if (vd) {
> > +		lli = desc->v_lli;
> > +		while (true) {
> > +			bytes += sdma->ops->lli_size(lli);
> > +
> > +			if (!sdma->ops->lli_has_next(lli))
> > +				break;
> > +
> > +			lli = sdma->ops->lli_next(lli);
> > +		}
> > +	} else if (chan) {
> > +		bytes = sdma->ops->channel_residue(schan);
> > +	}
> > +
> > +	dma_set_residue(state, bytes);
> > +
> > +out:
> > +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> > +
> > +	return ret;
> > +};
> > +
> > +static int sdma_config(struct dma_chan *chan,
> > +		       struct dma_slave_config *config)
> > +{
> > +	struct sdma_request *sreq = to_sdma_request(chan);
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> > +	memcpy(&sreq->cfg, config, sizeof(*config));
> > +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> > +
> > +	return 0;
> > +}
> > +
> > +static int sdma_pause(struct dma_chan *chan)
> > +{
> > +	struct sdma_request *sreq = to_sdma_request(chan);
> > +	struct sdma_channel *schan = sreq->chan;
> > +	struct sdma *sdma = to_sdma(chan->device);
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> > +
> > +	/*
> > +	 * If the request is currently scheduled on a channel, just
> > +	 * pause the channel.
> > +	 *
> > +	 * If not, remove the request from the pending list.
> > +	 */
> > +	if (schan) {
> > +		sdma->ops->channel_pause(schan);
> > +	} else {
> > +		spin_lock(&sdma->lock);
> > +		list_del_init(&sreq->node);
> > +		spin_unlock(&sdma->lock);
> > +	}
> > +
> > +	spin_unlock_irqrestore(&sreq->vchan.lock, flags);
> > +
> > +	return 0;
> > +}
> > +
> > +static int sdma_resume(struct dma_chan *chan)
> > +{
> > +	struct sdma_request *sreq = to_sdma_request(chan);
> > +	struct sdma_channel *schan = sreq->chan;
> > +	struct sdma *sdma = to_sdma(chan->device);
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&sreq->vchan.lock, flags);
> > +
> > +	/*
> > +	 * If the request is currently scheduled on a channel, just
> > +	 * resume the channel.
> > +	 *
> > +	 * If not, add the request from the pending list.
> > +	 */
> 
> Is such a case possible?

Yes, it's possible, if you have more requests than channels, and all
channels are currently busy with other transfers, we can end up in a
case where we have a requests where issue_pending has been called, but
is not currently scheduled on any channel, for an unknown amount of
time.

In such a case, you might very well end up with clients calling
pause/resume on these requests.

> The request should be already in the pending list, isn't it?

Not really, it would have been removed by sdma_pause.

> By the way, I may have missed something but where do you add a
> request to the pending list? I have seen it only in the resume case.

It should be in issue_pending and pause. If not, it's a bug :)

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

  reply	other threads:[~2015-03-23 19:10 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-21 19:42 [PATCH RFC 0/2] dmaengine: Introduce Scheduled DMA sub-framework Maxime Ripard
2015-03-21 19:42 ` [PATCH RFC 1/2] dmaengine: Introduce scheduled DMA framework Maxime Ripard
2015-03-23  9:15   ` Ludovic Desroches
2015-03-23 16:55     ` Maxime Ripard
2015-03-23 13:25   ` Ludovic Desroches
2015-03-23 19:07     ` Maxime Ripard [this message]
2015-03-23 16:38   ` Vinod Koul
2015-03-23 21:51     ` Maxime Ripard
2015-03-24 16:20       ` Vinod Koul
2015-03-25  0:30         ` Maxime Ripard
2015-03-21 19:42 ` [PATCH RFC 2/2] dmaengine: sun6i: Convert to scheduled DMA Maxime Ripard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150323190727.GD15676@lukather \
    --to=maxime.ripard@free-electrons.com \
    --cc=dmaengine@vger.kernel.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ludovic.desroches@atmel.com \
    --cc=vinod.koul@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.