From: Vinod Koul <vinod.koul@intel.com>
To: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Cc: dan.j.williams@intel.com, laurent.pinchart@ideasonboard.com,
horms@verge.net.au, kuninori.morimoto.gx@renesas.com,
robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com,
ijc+devicetree@hellion.org.uk, galak@codeaurora.org,
dmaengine@vger.kernel.org, linux-sh@vger.kernel.org,
devicetree@vger.kernel.org
Subject: Re: [PATCH 2/2] dmaengine: usb-dmac: Add Renesas USB DMA Controller (USB-DMAC) driver
Date: Thu, 05 Mar 2015 10:21:17 +0000 [thread overview]
Message-ID: <20150305100917.GD2613@intel.com> (raw)
In-Reply-To: <1423469645-2976-3-git-send-email-yoshihiro.shimoda.uh@renesas.com>
On Mon, Feb 09, 2015 at 05:14:05PM +0900, Yoshihiro Shimoda wrote:
> +struct usb_dmac_chan {
> + struct dma_chan chan;
> + void __iomem *iomem;
> + unsigned int index;
> +
> + spinlock_t lock;
> +
> + struct {
> + struct list_head free;
> + struct list_head pending;
> + struct list_head active;
> + struct list_head done;
> + struct list_head wait;
> + struct usb_dmac_desc *running;
> + struct usb_dmac_desc *last_done;
> +
> + struct list_head chunks_free;
> +
> + struct list_head pages;
Thats too many lists, do we need so many? Shouldn't free and done be same
thing. Similarly whats meant by wait here?
Do you submit multiple descriptors to HW?
> +static dma_cookie_t usb_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
> +{
> + struct usb_dmac_chan *chan = to_usb_dmac_chan(tx->chan);
> + struct usb_dmac_desc *desc = to_usb_dmac_desc(tx);
> + unsigned long flags;
> + dma_cookie_t cookie;
> +
> + spin_lock_irqsave(&chan->lock, flags);
> +
> + cookie = dma_cookie_assign(tx);
> +
> + dev_dbg(chan->chan.device->dev, "chan%u: submit #%d@%p\n",
> + chan->index, tx->cookie, desc);
> +
> + list_add_tail(&desc->node, &chan->desc.pending);
> + desc->running = list_first_entry(&desc->chunks,
> + struct usb_dmac_xfer_chunk, node);
what is this required for?
> +static void usb_dmac_desc_put(struct usb_dmac_chan *chan,
> + struct usb_dmac_desc *desc)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&chan->lock, flags);
> + list_splice_tail_init(&desc->chunks, &chan->desc.chunks_free);
> + list_add_tail(&desc->node, &chan->desc.free);
> + spin_unlock_irqrestore(&chan->lock, flags);
> +}
> +
> +static void usb_dmac_desc_recycle_acked(struct usb_dmac_chan *chan)
what is meant by acked here, and by whom?
> +{
> + struct usb_dmac_desc *desc, *_desc;
> + unsigned long flags;
> + LIST_HEAD(list);
> +
> + /*
> + * We have to temporarily move all descriptors from the wait list to a
> + * local list as iterating over the wait list, even with
> + * list_for_each_entry_safe, isn't safe if we release the channel lock
> + * around the usb_dmac_desc_put() call.
> + */
> + spin_lock_irqsave(&chan->lock, flags);
> + list_splice_init(&chan->desc.wait, &list);
> + spin_unlock_irqrestore(&chan->lock, flags);
> +
> + list_for_each_entry_safe(desc, _desc, &list, node) {
> + if (async_tx_test_ack(&desc->async_tx)) {
Hmmm, this part is not correct. ACK is for async_tx API and not for slave
dmaengine drivers.
> +static int usb_dmac_alloc_chan_resources(struct dma_chan *chan)
> +{
> + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
> + int ret;
> +
> + INIT_LIST_HEAD(&uchan->desc.chunks_free);
> + INIT_LIST_HEAD(&uchan->desc.pages);
> +
> + /* Preallocate descriptors. */
> + ret = usb_dmac_xfer_chunk_alloc(uchan, GFP_KERNEL);
GFP_NOWAIT
> + if (ret < 0)
> + return -ENOMEM;
> +
> + ret = usb_dmac_desc_alloc(uchan, GFP_KERNEL);
Ditto
> +static int usb_dmac_chan_terminate_all(struct dma_chan *chan)
> +{
> + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&uchan->lock, flags);
> + usb_dmac_chan_halt(uchan);
> + spin_unlock_irqrestore(&uchan->lock, flags);
> +
> + /*
> + * FIXME: No new interrupt can occur now, but the IRQ thread might still
> + * be running.
> + */
and when ?
> +static unsigned int
> +usb_dmac_chan_get_residue_if_complete(struct usb_dmac_chan *chan)
> +{
> + struct usb_dmac_desc *desc = chan->desc.last_done;
> + struct usb_dmac_xfer_chunk *chunk = desc ? desc->running : NULL;
> +
> + if (!chunk)
> + return 0;
> +
> + return usb_dmac_chan_get_last_residue(chan, chunk, desc->direction);
and this calls for completed descriptor, wont reading HW be wrong here as
you might have submitted another descriptor?
> +static enum dma_status usb_dmac_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie,
> + struct dma_tx_state *txstate)
> +{
> + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
> + enum dma_status status;
> + unsigned long flags;
> + unsigned int residue;
> +
> + status = dma_cookie_status(chan, cookie, txstate);
> + /* a client driver will get residue after DMA_COMPLETE */
> + if (!txstate)
> + return status;
> +
> + spin_lock_irqsave(&uchan->lock, flags);
> + if (status = DMA_COMPLETE)
> + residue = usb_dmac_chan_get_residue_if_complete(uchan);
if it is completed then residue should be zero, so why are we computing this
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int usb_dmac_sleep_suspend(struct device *dev)
> +{
> + /*
> + * TODO: Wait for the current transfer to complete and stop the device.
> + */
> + return 0;
> +}
> +
> +static int usb_dmac_sleep_resume(struct device *dev)
> +{
> + /* TODO: Resume transfers, if any. */
> + return 0;
> +}
> +#endif
what is the point of these?
> +
> +#ifdef CONFIG_PM
> +static int usb_dmac_runtime_suspend(struct device *dev)
> +{
> + return 0;
> +}
ditto
> +static int usb_dmac_chan_probe(struct usb_dmac *dmac,
> + struct usb_dmac_chan *uchan,
> + unsigned int index)
> +{
> + struct platform_device *pdev = to_platform_device(dmac->dev);
> + struct dma_chan *chan = &uchan->chan;
> + char pdev_irqname[5];
> + char *irqname;
> + int irq;
> + int ret;
> +
> + uchan->index = index;
> + uchan->iomem = dmac->iomem + USB_DMAC_CHAN_OFFSET(index);
> +
> + spin_lock_init(&uchan->lock);
> +
> + INIT_LIST_HEAD(&uchan->desc.free);
> + INIT_LIST_HEAD(&uchan->desc.pending);
> + INIT_LIST_HEAD(&uchan->desc.active);
> + INIT_LIST_HEAD(&uchan->desc.done);
> + INIT_LIST_HEAD(&uchan->desc.wait);
> +
> + /* Request the channel interrupt. */
> + sprintf(pdev_irqname, "ch%u", index);
> + irq = platform_get_irq_byname(pdev, pdev_irqname);
> + if (irq < 0) {
> + dev_err(dmac->dev, "no IRQ specified for channel %u\n", index);
> + return -ENODEV;
> + }
> +
> + irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
> + dev_name(dmac->dev), index);
> + if (!irqname)
> + return -ENOMEM;
> +
> + ret = devm_request_threaded_irq(dmac->dev, irq, usb_dmac_isr_channel,
> + usb_dmac_isr_channel_thread,
> + IRQF_SHARED,
> + irqname, uchan);
DMA engine API expects that you are running a tasklet not a thread
> +
> +static int usb_dmac_remove(struct platform_device *pdev)
> +{
> + struct usb_dmac *dmac = platform_get_drvdata(pdev);
> +
> + of_dma_controller_free(pdev->dev.of_node);
> + dma_async_device_unregister(&dmac->engine);
> +
> + pm_runtime_disable(&pdev->dev);
and you have not freed or disabled your irq, neither ensured all
threads/tasklets running are stopped
Overall i feel descriptor management is overtly complicated. Can you see if
you can you use virt-dma infrastructure, that will ease you descriptor management
a lot
Thanks
--
~Vinod
WARNING: multiple messages have this Message-ID (diff)
From: Vinod Koul <vinod.koul@intel.com>
To: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Cc: dan.j.williams@intel.com, laurent.pinchart@ideasonboard.com,
horms@verge.net.au, kuninori.morimoto.gx@renesas.com,
robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com,
ijc+devicetree@hellion.org.uk, galak@codeaurora.org,
dmaengine@vger.kernel.org, linux-sh@vger.kernel.org,
devicetree@vger.kernel.org
Subject: Re: [PATCH 2/2] dmaengine: usb-dmac: Add Renesas USB DMA Controller (USB-DMAC) driver
Date: Thu, 5 Mar 2015 15:39:17 +0530 [thread overview]
Message-ID: <20150305100917.GD2613@intel.com> (raw)
In-Reply-To: <1423469645-2976-3-git-send-email-yoshihiro.shimoda.uh@renesas.com>
On Mon, Feb 09, 2015 at 05:14:05PM +0900, Yoshihiro Shimoda wrote:
> +struct usb_dmac_chan {
> + struct dma_chan chan;
> + void __iomem *iomem;
> + unsigned int index;
> +
> + spinlock_t lock;
> +
> + struct {
> + struct list_head free;
> + struct list_head pending;
> + struct list_head active;
> + struct list_head done;
> + struct list_head wait;
> + struct usb_dmac_desc *running;
> + struct usb_dmac_desc *last_done;
> +
> + struct list_head chunks_free;
> +
> + struct list_head pages;
Thats too many lists, do we need so many? Shouldn't free and done be same
thing. Similarly whats meant by wait here?
Do you submit multiple descriptors to HW?
> +static dma_cookie_t usb_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
> +{
> + struct usb_dmac_chan *chan = to_usb_dmac_chan(tx->chan);
> + struct usb_dmac_desc *desc = to_usb_dmac_desc(tx);
> + unsigned long flags;
> + dma_cookie_t cookie;
> +
> + spin_lock_irqsave(&chan->lock, flags);
> +
> + cookie = dma_cookie_assign(tx);
> +
> + dev_dbg(chan->chan.device->dev, "chan%u: submit #%d@%p\n",
> + chan->index, tx->cookie, desc);
> +
> + list_add_tail(&desc->node, &chan->desc.pending);
> + desc->running = list_first_entry(&desc->chunks,
> + struct usb_dmac_xfer_chunk, node);
what is this required for?
> +static void usb_dmac_desc_put(struct usb_dmac_chan *chan,
> + struct usb_dmac_desc *desc)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&chan->lock, flags);
> + list_splice_tail_init(&desc->chunks, &chan->desc.chunks_free);
> + list_add_tail(&desc->node, &chan->desc.free);
> + spin_unlock_irqrestore(&chan->lock, flags);
> +}
> +
> +static void usb_dmac_desc_recycle_acked(struct usb_dmac_chan *chan)
what is meant by acked here, and by whom?
> +{
> + struct usb_dmac_desc *desc, *_desc;
> + unsigned long flags;
> + LIST_HEAD(list);
> +
> + /*
> + * We have to temporarily move all descriptors from the wait list to a
> + * local list as iterating over the wait list, even with
> + * list_for_each_entry_safe, isn't safe if we release the channel lock
> + * around the usb_dmac_desc_put() call.
> + */
> + spin_lock_irqsave(&chan->lock, flags);
> + list_splice_init(&chan->desc.wait, &list);
> + spin_unlock_irqrestore(&chan->lock, flags);
> +
> + list_for_each_entry_safe(desc, _desc, &list, node) {
> + if (async_tx_test_ack(&desc->async_tx)) {
Hmmm, this part is not correct. ACK is for async_tx API and not for slave
dmaengine drivers.
> +static int usb_dmac_alloc_chan_resources(struct dma_chan *chan)
> +{
> + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
> + int ret;
> +
> + INIT_LIST_HEAD(&uchan->desc.chunks_free);
> + INIT_LIST_HEAD(&uchan->desc.pages);
> +
> + /* Preallocate descriptors. */
> + ret = usb_dmac_xfer_chunk_alloc(uchan, GFP_KERNEL);
GFP_NOWAIT
> + if (ret < 0)
> + return -ENOMEM;
> +
> + ret = usb_dmac_desc_alloc(uchan, GFP_KERNEL);
Ditto
> +static int usb_dmac_chan_terminate_all(struct dma_chan *chan)
> +{
> + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&uchan->lock, flags);
> + usb_dmac_chan_halt(uchan);
> + spin_unlock_irqrestore(&uchan->lock, flags);
> +
> + /*
> + * FIXME: No new interrupt can occur now, but the IRQ thread might still
> + * be running.
> + */
and when ?
> +static unsigned int
> +usb_dmac_chan_get_residue_if_complete(struct usb_dmac_chan *chan)
> +{
> + struct usb_dmac_desc *desc = chan->desc.last_done;
> + struct usb_dmac_xfer_chunk *chunk = desc ? desc->running : NULL;
> +
> + if (!chunk)
> + return 0;
> +
> + return usb_dmac_chan_get_last_residue(chan, chunk, desc->direction);
and this calls for completed descriptor, wont reading HW be wrong here as
you might have submitted another descriptor?
> +static enum dma_status usb_dmac_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie,
> + struct dma_tx_state *txstate)
> +{
> + struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
> + enum dma_status status;
> + unsigned long flags;
> + unsigned int residue;
> +
> + status = dma_cookie_status(chan, cookie, txstate);
> + /* a client driver will get residue after DMA_COMPLETE */
> + if (!txstate)
> + return status;
> +
> + spin_lock_irqsave(&uchan->lock, flags);
> + if (status == DMA_COMPLETE)
> + residue = usb_dmac_chan_get_residue_if_complete(uchan);
if it is completed then residue should be zero, so why are we computing this
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int usb_dmac_sleep_suspend(struct device *dev)
> +{
> + /*
> + * TODO: Wait for the current transfer to complete and stop the device.
> + */
> + return 0;
> +}
> +
> +static int usb_dmac_sleep_resume(struct device *dev)
> +{
> + /* TODO: Resume transfers, if any. */
> + return 0;
> +}
> +#endif
what is the point of these?
> +
> +#ifdef CONFIG_PM
> +static int usb_dmac_runtime_suspend(struct device *dev)
> +{
> + return 0;
> +}
ditto
> +static int usb_dmac_chan_probe(struct usb_dmac *dmac,
> + struct usb_dmac_chan *uchan,
> + unsigned int index)
> +{
> + struct platform_device *pdev = to_platform_device(dmac->dev);
> + struct dma_chan *chan = &uchan->chan;
> + char pdev_irqname[5];
> + char *irqname;
> + int irq;
> + int ret;
> +
> + uchan->index = index;
> + uchan->iomem = dmac->iomem + USB_DMAC_CHAN_OFFSET(index);
> +
> + spin_lock_init(&uchan->lock);
> +
> + INIT_LIST_HEAD(&uchan->desc.free);
> + INIT_LIST_HEAD(&uchan->desc.pending);
> + INIT_LIST_HEAD(&uchan->desc.active);
> + INIT_LIST_HEAD(&uchan->desc.done);
> + INIT_LIST_HEAD(&uchan->desc.wait);
> +
> + /* Request the channel interrupt. */
> + sprintf(pdev_irqname, "ch%u", index);
> + irq = platform_get_irq_byname(pdev, pdev_irqname);
> + if (irq < 0) {
> + dev_err(dmac->dev, "no IRQ specified for channel %u\n", index);
> + return -ENODEV;
> + }
> +
> + irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
> + dev_name(dmac->dev), index);
> + if (!irqname)
> + return -ENOMEM;
> +
> + ret = devm_request_threaded_irq(dmac->dev, irq, usb_dmac_isr_channel,
> + usb_dmac_isr_channel_thread,
> + IRQF_SHARED,
> + irqname, uchan);
DMA engine API expects that you are running a tasklet not a thread
> +
> +static int usb_dmac_remove(struct platform_device *pdev)
> +{
> + struct usb_dmac *dmac = platform_get_drvdata(pdev);
> +
> + of_dma_controller_free(pdev->dev.of_node);
> + dma_async_device_unregister(&dmac->engine);
> +
> + pm_runtime_disable(&pdev->dev);
and you have not freed or disabled your irq, neither ensured all
threads/tasklets running are stopped
Overall i feel descriptor management is overtly complicated. Can you see if
you can you use virt-dma infrastructure, that will ease you descriptor management
a lot
Thanks
--
~Vinod
next prev parent reply other threads:[~2015-03-05 10:21 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-02-09 8:14 [PATCH 0/2] dmaengine: usb-dmac: Add Renesas USB DMA Controller Yoshihiro Shimoda
2015-02-09 8:14 ` Yoshihiro Shimoda
2015-02-09 8:14 ` [PATCH 1/2] dmaengine: renesas,usb-dmac: Add device tree bindings documentation Yoshihiro Shimoda
2015-02-09 8:14 ` Yoshihiro Shimoda
2015-02-09 8:14 ` [PATCH 2/2] dmaengine: usb-dmac: Add Renesas USB DMA Controller (USB-DMAC) driver Yoshihiro Shimoda
2015-02-09 8:14 ` Yoshihiro Shimoda
2015-03-05 10:09 ` Vinod Koul [this message]
2015-03-05 10:21 ` Vinod Koul
2015-03-05 12:35 ` yoshihiro shimoda
2015-03-05 12:35 ` yoshihiro shimoda
[not found] ` <HKNPR06MB32227E774659BA1179C9C5AD81F0-xIjp3w5QIjwsJtb0Mgt7d79PrO6axcR4XA4E9RH9d+qIuWR1G4zioA@public.gmane.org>
2015-03-05 16:21 ` Vinod Koul
2015-03-05 16:33 ` Vinod Koul
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=20150305100917.GD2613@intel.com \
--to=vinod.koul@intel.com \
--cc=dan.j.williams@intel.com \
--cc=devicetree@vger.kernel.org \
--cc=dmaengine@vger.kernel.org \
--cc=galak@codeaurora.org \
--cc=horms@verge.net.au \
--cc=ijc+devicetree@hellion.org.uk \
--cc=kuninori.morimoto.gx@renesas.com \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-sh@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=pawel.moll@arm.com \
--cc=robh+dt@kernel.org \
--cc=yoshihiro.shimoda.uh@renesas.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.