diff options
author | Jisheng Zhang <Jisheng.Zhang@synaptics.com> | 2019-01-25 08:29:20 +0100 |
---|---|---|
committer | Boris Brezillon <bbrezillon@kernel.org> | 2019-01-26 11:14:25 +0100 |
commit | f36c1f9a8dfd6a78e6c3fe7aff5e722b84307597 (patch) | |
tree | cf6b4f9bf5e9c4de75deb6d51a45088ee6b4faf0 /drivers/i3c | |
parent | i3c: fix missing detach if failed to retrieve i3c dev (diff) | |
download | linux-f36c1f9a8dfd6a78e6c3fe7aff5e722b84307597.tar.xz linux-f36c1f9a8dfd6a78e6c3fe7aff5e722b84307597.zip |
i3c: master: dw: fix deadlock
In dw_i3c_master_irq_handler(), we already have gotten
&master->xferqueue.lock, if we try to get the same lock again in
dw_i3c_master_dequeue_xfer(), deadlock happens.
We fix this issue by introduing dw_i3c_master_dequeue_xfer_locked()
which does all what dw_i3c_master_dequeue_xfer() does without trying
to lock &master->xferqueue.lock.
Signed-off-by: Jisheng Zhang <Jisheng.Zhang@synaptics.com>
Acked-by: Vitor Soares <vitor.soares@synopsys.com>
Signed-off-by: Boris Brezillon <bbrezillon@kernel.org>
Diffstat (limited to 'drivers/i3c')
-rw-r--r-- | drivers/i3c/master/dw-i3c-master.c | 18 |
1 files changed, 12 insertions, 6 deletions
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index f8c00b94817f..bb03079fbade 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -419,12 +419,9 @@ static void dw_i3c_master_enqueue_xfer(struct dw_i3c_master *master, spin_unlock_irqrestore(&master->xferqueue.lock, flags); } -static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master, - struct dw_i3c_xfer *xfer) +static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master, + struct dw_i3c_xfer *xfer) { - unsigned long flags; - - spin_lock_irqsave(&master->xferqueue.lock, flags); if (master->xferqueue.cur == xfer) { u32 status; @@ -439,6 +436,15 @@ static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master, } else { list_del_init(&xfer->node); } +} + +static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master, + struct dw_i3c_xfer *xfer) +{ + unsigned long flags; + + spin_lock_irqsave(&master->xferqueue.lock, flags); + dw_i3c_master_dequeue_xfer_locked(master, xfer); spin_unlock_irqrestore(&master->xferqueue.lock, flags); } @@ -494,7 +500,7 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr) complete(&xfer->comp); if (ret < 0) { - dw_i3c_master_dequeue_xfer(master, xfer); + dw_i3c_master_dequeue_xfer_locked(master, xfer); writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_RESUME, master->regs + DEVICE_CTRL); } |