Skip to content

Commit

Permalink
Merge tag 'dmaengine-fix-4.10-rc7' of git://git.infradead.org/users/v…
Browse files Browse the repository at this point in the history
…koul/slave-dma

Pull dmaengine fixes from Vinod Koul:
 "A couple of fixes showed up late in the cycle so sending them up and
  sending early in the week and not on Friday :).

  They fix a double lock in pl330 driver and runtime pm fixes for cppi
  driver"

* tag 'dmaengine-fix-4.10-rc7' of git://git.infradead.org/users/vkoul/slave-dma:
  dmaengine: pl330: fix double lock
  dmaengine: cppi41: Clean up pointless warnings
  dmaengine: cppi41: Fix oops in cppi41_runtime_resume
  dmaengine: cppi41: Fix runtime PM timeouts with USB mass storage
  • Loading branch information
Linus Torvalds committed Feb 1, 2017
2 parents c325b35 + 91539eb commit 3560950
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 36 deletions.
69 changes: 46 additions & 23 deletions drivers/dma/cppi41.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ struct cppi41_dd {

/* context for suspend/resume */
unsigned int dma_tdfdq;

bool is_suspended;
};

#define FIST_COMPLETION_QUEUE 93
Expand Down Expand Up @@ -257,6 +259,10 @@ static struct cppi41_channel *desc_to_chan(struct cppi41_dd *cdd, u32 desc)
BUG_ON(desc_num >= ALLOC_DECS_NUM);
c = cdd->chan_busy[desc_num];
cdd->chan_busy[desc_num] = NULL;

/* Usecount for chan_busy[], paired with push_desc_queue() */
pm_runtime_put(cdd->ddev.dev);

return c;
}

Expand Down Expand Up @@ -317,12 +323,12 @@ static irqreturn_t cppi41_irq(int irq, void *data)

while (val) {
u32 desc, len;
int error;

error = pm_runtime_get(cdd->ddev.dev);
if (error < 0)
dev_err(cdd->ddev.dev, "%s pm runtime get: %i\n",
__func__, error);
/*
* This should never trigger, see the comments in
* push_desc_queue()
*/
WARN_ON(cdd->is_suspended);

q_num = __fls(val);
val &= ~(1 << q_num);
Expand All @@ -343,9 +349,6 @@ static irqreturn_t cppi41_irq(int irq, void *data)
c->residue = pd_trans_len(c->desc->pd6) - len;
dma_cookie_complete(&c->txd);
dmaengine_desc_get_callback_invoke(&c->txd, NULL);

pm_runtime_mark_last_busy(cdd->ddev.dev);
pm_runtime_put_autosuspend(cdd->ddev.dev);
}
}
return IRQ_HANDLED;
Expand Down Expand Up @@ -447,6 +450,15 @@ static void push_desc_queue(struct cppi41_channel *c)
*/
__iowmb();

/*
* DMA transfers can take at least 200ms to complete with USB mass
* storage connected. To prevent autosuspend timeouts, we must use
* pm_runtime_get/put() when chan_busy[] is modified. This will get
* cleared in desc_to_chan() or cppi41_stop_chan() depending on the
* outcome of the transfer.
*/
pm_runtime_get(cdd->ddev.dev);

desc_phys = lower_32_bits(c->desc_phys);
desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc);
WARN_ON(cdd->chan_busy[desc_num]);
Expand All @@ -457,20 +469,26 @@ static void push_desc_queue(struct cppi41_channel *c)
cppi_writel(reg, cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num));
}

static void pending_desc(struct cppi41_channel *c)
/*
* Caller must hold cdd->lock to prevent push_desc_queue()
* getting called out of order. We have both cppi41_dma_issue_pending()
* and cppi41_runtime_resume() call this function.
*/
static void cppi41_run_queue(struct cppi41_dd *cdd)
{
struct cppi41_dd *cdd = c->cdd;
unsigned long flags;
struct cppi41_channel *c, *_c;

spin_lock_irqsave(&cdd->lock, flags);
list_add_tail(&c->node, &cdd->pending);
spin_unlock_irqrestore(&cdd->lock, flags);
list_for_each_entry_safe(c, _c, &cdd->pending, node) {
push_desc_queue(c);
list_del(&c->node);
}
}

static void cppi41_dma_issue_pending(struct dma_chan *chan)
{
struct cppi41_channel *c = to_cpp41_chan(chan);
struct cppi41_dd *cdd = c->cdd;
unsigned long flags;
int error;

error = pm_runtime_get(cdd->ddev.dev);
Expand All @@ -482,10 +500,11 @@ static void cppi41_dma_issue_pending(struct dma_chan *chan)
return;
}

if (likely(pm_runtime_active(cdd->ddev.dev)))
push_desc_queue(c);
else
pending_desc(c);
spin_lock_irqsave(&cdd->lock, flags);
list_add_tail(&c->node, &cdd->pending);
if (!cdd->is_suspended)
cppi41_run_queue(cdd);
spin_unlock_irqrestore(&cdd->lock, flags);

pm_runtime_mark_last_busy(cdd->ddev.dev);
pm_runtime_put_autosuspend(cdd->ddev.dev);
Expand Down Expand Up @@ -705,6 +724,9 @@ static int cppi41_stop_chan(struct dma_chan *chan)
WARN_ON(!cdd->chan_busy[desc_num]);
cdd->chan_busy[desc_num] = NULL;

/* Usecount for chan_busy[], paired with push_desc_queue() */
pm_runtime_put(cdd->ddev.dev);

return 0;
}

Expand Down Expand Up @@ -1150,23 +1172,24 @@ static int __maybe_unused cppi41_resume(struct device *dev)
static int __maybe_unused cppi41_runtime_suspend(struct device *dev)
{
struct cppi41_dd *cdd = dev_get_drvdata(dev);
unsigned long flags;

spin_lock_irqsave(&cdd->lock, flags);
cdd->is_suspended = true;
WARN_ON(!list_empty(&cdd->pending));
spin_unlock_irqrestore(&cdd->lock, flags);

return 0;
}

static int __maybe_unused cppi41_runtime_resume(struct device *dev)
{
struct cppi41_dd *cdd = dev_get_drvdata(dev);
struct cppi41_channel *c, *_c;
unsigned long flags;

spin_lock_irqsave(&cdd->lock, flags);
list_for_each_entry_safe(c, _c, &cdd->pending, node) {
push_desc_queue(c);
list_del(&c->node);
}
cdd->is_suspended = false;
cppi41_run_queue(cdd);
spin_unlock_irqrestore(&cdd->lock, flags);

return 0;
Expand Down
19 changes: 6 additions & 13 deletions drivers/dma/pl330.c
Original file line number Diff line number Diff line change
Expand Up @@ -1699,16 +1699,13 @@ static bool _chan_ns(const struct pl330_dmac *pl330, int i)
static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
{
struct pl330_thread *thrd = NULL;
unsigned long flags;
int chans, i;

if (pl330->state == DYING)
return NULL;

chans = pl330->pcfg.num_chan;

spin_lock_irqsave(&pl330->lock, flags);

for (i = 0; i < chans; i++) {
thrd = &pl330->channels[i];
if ((thrd->free) && (!_manager_ns(thrd) ||
Expand All @@ -1726,8 +1723,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
thrd = NULL;
}

spin_unlock_irqrestore(&pl330->lock, flags);

return thrd;
}

Expand All @@ -1745,7 +1740,6 @@ static inline void _free_event(struct pl330_thread *thrd, int ev)
static void pl330_release_channel(struct pl330_thread *thrd)
{
struct pl330_dmac *pl330;
unsigned long flags;

if (!thrd || thrd->free)
return;
Expand All @@ -1757,10 +1751,8 @@ static void pl330_release_channel(struct pl330_thread *thrd)

pl330 = thrd->dmac;

spin_lock_irqsave(&pl330->lock, flags);
_free_event(thrd, thrd->ev);
thrd->free = true;
spin_unlock_irqrestore(&pl330->lock, flags);
}

/* Initialize the structure for PL330 configuration, that can be used
Expand Down Expand Up @@ -2122,20 +2114,20 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
struct pl330_dmac *pl330 = pch->dmac;
unsigned long flags;

spin_lock_irqsave(&pch->lock, flags);
spin_lock_irqsave(&pl330->lock, flags);

dma_cookie_init(chan);
pch->cyclic = false;

pch->thread = pl330_request_channel(pl330);
if (!pch->thread) {
spin_unlock_irqrestore(&pch->lock, flags);
spin_unlock_irqrestore(&pl330->lock, flags);
return -ENOMEM;
}

tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch);

spin_unlock_irqrestore(&pch->lock, flags);
spin_unlock_irqrestore(&pl330->lock, flags);

return 1;
}
Expand Down Expand Up @@ -2238,20 +2230,21 @@ static int pl330_pause(struct dma_chan *chan)
static void pl330_free_chan_resources(struct dma_chan *chan)
{
struct dma_pl330_chan *pch = to_pchan(chan);
struct pl330_dmac *pl330 = pch->dmac;
unsigned long flags;

tasklet_kill(&pch->task);

pm_runtime_get_sync(pch->dmac->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
spin_lock_irqsave(&pl330->lock, flags);

pl330_release_channel(pch->thread);
pch->thread = NULL;

if (pch->cyclic)
list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);

spin_unlock_irqrestore(&pch->lock, flags);
spin_unlock_irqrestore(&pl330->lock, flags);
pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
}
Expand Down

0 comments on commit 3560950

Please sign in to comment.