]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.9.68/dmaengine-pl330-fix-double-lock.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 4.9.68 / dmaengine-pl330-fix-double-lock.patch
1 From foo@baz Wed Dec 6 17:39:55 CET 2017
2 From: Iago Abal <mail@iagoabal.eu>
3 Date: Wed, 11 Jan 2017 14:00:21 +0100
4 Subject: dmaengine: pl330: fix double lock
5
6 From: Iago Abal <mail@iagoabal.eu>
7
8
9 [ Upstream commit 91539eb1fda2d530d3b268eef542c5414e54bf1a ]
10
11 The static bug finder EBA (http://www.iagoabal.eu/eba/) reported the
12 following double-lock bug:
13
14 Double lock:
15 1. spin_lock_irqsave(pch->lock, flags) at pl330_free_chan_resources:2236;
16 2. call to function `pl330_release_channel' immediately after;
17 3. call to function `dma_pl330_rqcb' in line 1753;
18 4. spin_lock_irqsave(pch->lock, flags) at dma_pl330_rqcb:1505.
19
20 I have fixed it as suggested by Marek Szyprowski.
21
22 First, I have replaced `pch->lock' with `pl330->lock' in functions
23 `pl330_alloc_chan_resources' and `pl330_free_chan_resources'. This avoids
24 the double-lock by acquiring a different lock than `dma_pl330_rqcb'.
25
26 NOTE that, as a result, `pl330_free_chan_resources' executes
27 `list_splice_tail_init' on `pch->work_list' under lock `pl330->lock',
28 whereas in the rest of the code `pch->work_list' is protected by
29 `pch->lock'. I don't know if this may cause race conditions. Similarly
30 `pch->cyclic' is written by `pl330_alloc_chan_resources' under
31 `pl330->lock' but read by `pl330_tx_submit' under `pch->lock'.
32
33 Second, I have removed locking from `pl330_request_channel' and
34 `pl330_release_channel' functions. Function `pl330_request_channel' is
35 only called from `pl330_alloc_chan_resources', so the lock is already
36 held. Function `pl330_release_channel' is called from
37 `pl330_free_chan_resources', which already holds the lock, and from
38 `pl330_del'. Function `pl330_del' is called in an error path of
39 `pl330_probe' and at the end of `pl330_remove', but I assume that there
40 cannot be concurrent accesses to the protected data at those points.
41
42 Signed-off-by: Iago Abal <mail@iagoabal.eu>
43 Reviewed-by: Marek Szyprowski <m.szyprowski@samsung.com>
44 Signed-off-by: Vinod Koul <vinod.koul@intel.com>
45 Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
46 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
47 ---
48 drivers/dma/pl330.c | 19 ++++++-------------
49 1 file changed, 6 insertions(+), 13 deletions(-)
50
51 --- a/drivers/dma/pl330.c
52 +++ b/drivers/dma/pl330.c
53 @@ -1694,7 +1694,6 @@ static bool _chan_ns(const struct pl330_
54 static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
55 {
56 struct pl330_thread *thrd = NULL;
57 - unsigned long flags;
58 int chans, i;
59
60 if (pl330->state == DYING)
61 @@ -1702,8 +1701,6 @@ static struct pl330_thread *pl330_reques
62
63 chans = pl330->pcfg.num_chan;
64
65 - spin_lock_irqsave(&pl330->lock, flags);
66 -
67 for (i = 0; i < chans; i++) {
68 thrd = &pl330->channels[i];
69 if ((thrd->free) && (!_manager_ns(thrd) ||
70 @@ -1721,8 +1718,6 @@ static struct pl330_thread *pl330_reques
71 thrd = NULL;
72 }
73
74 - spin_unlock_irqrestore(&pl330->lock, flags);
75 -
76 return thrd;
77 }
78
79 @@ -1740,7 +1735,6 @@ static inline void _free_event(struct pl
80 static void pl330_release_channel(struct pl330_thread *thrd)
81 {
82 struct pl330_dmac *pl330;
83 - unsigned long flags;
84
85 if (!thrd || thrd->free)
86 return;
87 @@ -1752,10 +1746,8 @@ static void pl330_release_channel(struct
88
89 pl330 = thrd->dmac;
90
91 - spin_lock_irqsave(&pl330->lock, flags);
92 _free_event(thrd, thrd->ev);
93 thrd->free = true;
94 - spin_unlock_irqrestore(&pl330->lock, flags);
95 }
96
97 /* Initialize the structure for PL330 configuration, that can be used
98 @@ -2120,20 +2112,20 @@ static int pl330_alloc_chan_resources(st
99 struct pl330_dmac *pl330 = pch->dmac;
100 unsigned long flags;
101
102 - spin_lock_irqsave(&pch->lock, flags);
103 + spin_lock_irqsave(&pl330->lock, flags);
104
105 dma_cookie_init(chan);
106 pch->cyclic = false;
107
108 pch->thread = pl330_request_channel(pl330);
109 if (!pch->thread) {
110 - spin_unlock_irqrestore(&pch->lock, flags);
111 + spin_unlock_irqrestore(&pl330->lock, flags);
112 return -ENOMEM;
113 }
114
115 tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch);
116
117 - spin_unlock_irqrestore(&pch->lock, flags);
118 + spin_unlock_irqrestore(&pl330->lock, flags);
119
120 return 1;
121 }
122 @@ -2236,12 +2228,13 @@ static int pl330_pause(struct dma_chan *
123 static void pl330_free_chan_resources(struct dma_chan *chan)
124 {
125 struct dma_pl330_chan *pch = to_pchan(chan);
126 + struct pl330_dmac *pl330 = pch->dmac;
127 unsigned long flags;
128
129 tasklet_kill(&pch->task);
130
131 pm_runtime_get_sync(pch->dmac->ddma.dev);
132 - spin_lock_irqsave(&pch->lock, flags);
133 + spin_lock_irqsave(&pl330->lock, flags);
134
135 pl330_release_channel(pch->thread);
136 pch->thread = NULL;
137 @@ -2249,7 +2242,7 @@ static void pl330_free_chan_resources(st
138 if (pch->cyclic)
139 list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
140
141 - spin_unlock_irqrestore(&pch->lock, flags);
142 + spin_unlock_irqrestore(&pl330->lock, flags);
143 pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
144 pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
145 }